From c1f37108a0a8867c644ee475453ec3bda51aee07 Mon Sep 17 00:00:00 2001 From: Shunsuke Shibayama Date: Sun, 13 Oct 2024 18:31:06 +0900 Subject: [PATCH] fix: misalignment of error display --- .gitignore | 1 + Cargo.lock | 22 ++--- Cargo.toml | 6 +- crates/py2erg/convert.rs | 138 +++++++++++++++++------------- crates/py2erg/gen_decl.rs | 2 +- crates/pylyzer_core/handle_err.rs | 19 ++-- 6 files changed, 108 insertions(+), 80 deletions(-) diff --git a/.gitignore b/.gitignore index 147e6e4..32f6358 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ __pycache__/ test*.py /site +.venv diff --git a/Cargo.lock b/Cargo.lock index 8ca4202..b075e43 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -145,23 +145,25 @@ checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "els" -version = "0.1.58-nightly.4" +version = "0.1.58-nightly.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82ca64c7e007a801f3c026026d4f7c65193ca2ccfab19018cf47b0946ed1de86" +checksum = "73c6e882638db2b88b5a529d52c94c493b48a802bf418974ff2aadc9dea2383d" dependencies = [ "erg_common", "erg_compiler", + "libc", "lsp-types", "molc", "serde", "serde_json", + "windows", ] [[package]] name = "erg_common" -version = "0.6.46-nightly.4" +version = "0.6.46-nightly.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c91d7308be743f27d0bcb6778d85d76bfad86fc54ae53ae5fab06b37bd54fd74" +checksum = "168c67e9de97fa12ade0f27a8e4f0037efd0576285cbb13c19e8724129abac53" dependencies = [ "backtrace-on-stack-overflow", "erg_proc_macros", @@ -172,9 +174,9 @@ dependencies = [ [[package]] name = "erg_compiler" -version = "0.6.46-nightly.4" +version = "0.6.46-nightly.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2ca9d5eb0b29b60d7ac8d7d639add33a4b331b35e4739775f0bd0f1e94be764" +checksum = "1c303d57ad8238b388bf90eb3660681668a8fb3c8b77fff2833b348b7a767617" dependencies = [ "erg_common", "erg_parser", @@ -182,9 +184,9 @@ dependencies = [ [[package]] name = "erg_parser" -version = "0.6.46-nightly.4" +version = "0.6.46-nightly.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0d0f70495239bd721afb1be7ba33c9146cbd7d4d578bd65fcb86e52561224e0" +checksum = "f0f1c0508bb6aa189c7ded5f4a260537aa4d16a40018be64f3bd4f65d140b46b" dependencies = [ "erg_common", "erg_proc_macros", @@ -193,9 +195,9 @@ dependencies = [ [[package]] name = "erg_proc_macros" -version = "0.6.46-nightly.4" +version = "0.6.46-nightly.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61073a06b84b2e9c36b3645494102780936b560ba80f8c466cf2cdc374740f3e" +checksum = "ef2f7b89cccd974d2ab3dc31b79417c2a6d53490c9c96dcc1ad9ed7513ef4bf7" dependencies = [ "quote", "syn 1.0.109", diff --git a/Cargo.toml b/Cargo.toml index ddf31c6..0f52a9f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,9 +24,9 @@ edition = "2021" repository = "https://github.com/mtshiba/pylyzer" [workspace.dependencies] -erg_common = { version = "0.6.46-nightly.4", features = ["py_compat", "els"] } -erg_compiler = { version = "0.6.46-nightly.4", features = ["py_compat", "els"] } -els = { version = "0.1.58-nightly.4", features = ["py_compat"] } +erg_common = { version = "0.6.46-nightly.5", features = ["py_compat", "els"] } +erg_compiler = { version = "0.6.46-nightly.5", features = ["py_compat", "els"] } +els = { version = "0.1.58-nightly.5", features = ["py_compat"] } # rustpython-parser = { version = "0.3.0", features = ["all-nodes-with-ranges", "location"] } # rustpython-ast = { version = "0.3.0", features = ["all-nodes-with-ranges", "location"] } rustpython-parser = { git = "https://github.com/RustPython/Parser", version = "0.4.0", features = ["all-nodes-with-ranges", "location"] } diff --git a/crates/py2erg/convert.rs b/crates/py2erg/convert.rs index a19e1bd..06470cb 100644 --- a/crates/py2erg/convert.rs +++ b/crates/py2erg/convert.rs @@ -3,6 +3,7 @@ use std::path::Path; use erg_common::config::ErgConfig; use erg_common::dict::Dict as HashMap; +use erg_common::error::Location as ErgLocation; use erg_common::fresh::FRESH_GEN; use erg_common::set::Set as HashSet; use erg_common::traits::{Locational, Stream}; @@ -140,6 +141,17 @@ fn escape_name(name: String) -> String { } } +fn quoted_symbol(sym: &str, lineno: u32, col_begin: u32) -> Token { + let col_end = col_begin + sym.chars().count() as u32; + Token { + kind: TokenKind::StrLit, + content: format!("\"{sym}\"").into(), + lineno, + col_begin, + col_end, + } +} + fn op_to_token(op: Operator) -> Token { let (kind, cont) = match op { Operator::Add => (TokenKind::Plus, "+"), @@ -164,7 +176,7 @@ pub fn pyloc_to_ergloc(range: PySourceRange) -> erg_common::error::Location { range.start.row.get(), range.start.column.to_zero_indexed(), range.end.unwrap().row.get(), - range.end.unwrap().column.get(), + range.end.unwrap().column.to_zero_indexed(), ) } @@ -726,7 +738,7 @@ impl ASTConverter { loc.row.get(), loc.column.to_zero_indexed(), ); - Identifier::new(VisModifierSpec::Public(dot), name) + Identifier::new(VisModifierSpec::Public(dot.loc()), name) } // TODO: module member mangling @@ -744,7 +756,7 @@ impl ASTConverter { loc.row.get(), loc.column.to_zero_indexed(), ); - Identifier::new(VisModifierSpec::Public(dot), name) + Identifier::new(VisModifierSpec::Public(dot.loc()), name) } // Duplicate param names will result in an error at the parser. So we don't need to check it here. @@ -849,9 +861,10 @@ impl ASTConverter { fn param_pattern_to_var(pat: ParamPattern) -> VarPattern { match pat { - ParamPattern::VarName(name) => { - VarPattern::Ident(Identifier::new(VisModifierSpec::Public(DOT), name)) - } + ParamPattern::VarName(name) => VarPattern::Ident(Identifier::new( + VisModifierSpec::Public(ErgLocation::Unknown), + name, + )), ParamPattern::Discard(token) => VarPattern::Discard(token), other => todo!("{other}"), } @@ -887,7 +900,7 @@ impl ASTConverter { let tmp = FRESH_GEN.fresh_varname(); let tmp_name = VarName::from_str_and_line(tmp, expr.location().row.get()); let tmp_expr = Expr::Accessor(Accessor::Ident(Identifier::new( - VisModifierSpec::Public(DOT), + VisModifierSpec::Public(ErgLocation::Unknown), tmp_name.clone(), ))); let mut block = vec![]; @@ -991,7 +1004,7 @@ impl ASTConverter { TokenKind::UBar, "_", loc.row.get(), - loc.column.get() - 1, + loc.column.to_zero_indexed(), )) } @@ -1365,8 +1378,8 @@ impl ASTConverter { .into_iter() .map(|elem| self.convert_type_spec(elem)) .collect(); - let parens = Self::gen_enclosure_tokens(TokenKind::LParen, tuple.range); - let tuple = TupleTypeSpec::new(Some(parens), tys); + let (l, r) = Self::gen_enclosure_tokens(TokenKind::LParen, tuple.range); + let tuple = TupleTypeSpec::new(Some((l.loc(), r.loc())), tys); TypeSpec::Tuple(tuple) } _ => Self::gen_dummy_type_spec(args.location()), @@ -1527,14 +1540,18 @@ impl ASTConverter { } py_ast::Constant::Complex { real: _, imag: _ } => Expr::Dummy(Dummy::new(None, vec![])), py_ast::Constant::Str(value) => { + let kind = if const_ + .range + .end + .is_some_and(|end| end.row != const_.range.start.row) + { + TokenKind::DocComment + } else { + TokenKind::StrLit + }; let value = format!("\"{value}\""); // column - 2 because of the quotes - let token = Token::new( - TokenKind::StrLit, - value, - loc.row.get(), - loc.column.to_zero_indexed(), - ); + let token = Token::new(kind, value, loc.row.get(), loc.column.to_zero_indexed()); Expr::Literal(Literal::new(token)) } py_ast::Constant::Bool(b) => { @@ -1646,7 +1663,7 @@ impl ASTConverter { .and_then(|last| last.col_end()) .unwrap_or(function.col_end().unwrap_or(0) + 1) }, - |loc| loc.row.get(), + |loc| loc.column.to_zero_indexed().saturating_sub(1), ); let paren = { let lp = Token::new( @@ -1656,7 +1673,7 @@ impl ASTConverter { function.col_end().unwrap_or(0), ); let rp = Token::new(TokenKind::RParen, ")", loc.row.get(), last_col); - (lp, rp) + (lp.loc(), rp.loc()) }; let args = Args::new(pos_args, var_args, kw_args, kw_var, Some(paren)); function.call_expr(args) @@ -1816,7 +1833,7 @@ impl ASTConverter { .into_iter() .map(|ex| PosArg::new(self.convert_expr(ex))) .collect::>(); - let elems = Args::pos_only(elements, Some((l, r))); + let elems = Args::pos_only(elements, Some((l.loc(), r.loc()))); Expr::Tuple(Tuple::Normal(NormalTuple::new(elems))) } py_ast::Expr::Subscript(subs) => { @@ -2021,7 +2038,7 @@ impl ASTConverter { *base_type = Some(Expr::Record(record)); } let call_ident = Identifier::new( - VisModifierSpec::Public(DOT), + VisModifierSpec::Public(ErgLocation::Unknown), VarName::from_static("__call__"), ); let class_ident = Identifier::public_with_line( @@ -2046,8 +2063,10 @@ impl ASTConverter { params, Some(class_spec), )); - let unreachable_acc = - Identifier::new(VisModifierSpec::Public(DOT), VarName::from_static("exit")); + let unreachable_acc = Identifier::new( + VisModifierSpec::Public(ErgLocation::Unknown), + VarName::from_static("exit"), + ); let body = Expr::Accessor(Accessor::Ident(unreachable_acc)).call_expr(Args::empty()); let body = DefBody::new(EQUAL, Block::new(vec![body]), DefId(0)); let def = Def::new(sig, body); @@ -2056,7 +2075,7 @@ impl ASTConverter { fn gen_default_init(&self, line: usize) -> Def { let call_ident = Identifier::new( - VisModifierSpec::Public(DOT), + VisModifierSpec::Public(ErgLocation::Unknown), VarName::from_static("__call__"), ); let params = Params::empty(); @@ -2071,8 +2090,10 @@ impl ASTConverter { params, Some(class_spec), )); - let unreachable_acc = - Identifier::new(VisModifierSpec::Public(DOT), VarName::from_static("exit")); + let unreachable_acc = Identifier::new( + VisModifierSpec::Public(ErgLocation::Unknown), + VarName::from_static("exit"), + ); let body = Expr::Accessor(Accessor::Ident(unreachable_acc)).call_expr(Args::empty()); let body = DefBody::new(EQUAL, Block::new(vec![body]), DefId(0)); Def::new(sig, body) @@ -2172,7 +2193,7 @@ impl ASTConverter { DefId(self.block_id_counter), class, class_as_expr, - VisModifierSpec::Public(DOT), + VisModifierSpec::Public(ErgLocation::Unknown), attrs, ); (base_type, vec![methods]) @@ -2556,7 +2577,10 @@ impl ASTConverter { let tmp = FRESH_GEN.fresh_varname(); let tmp_name = VarName::from_str_and_line(tmp, tuple.location().row.get()); - let tmp_ident = Identifier::new(VisModifierSpec::Public(DOT), tmp_name); + let tmp_ident = Identifier::new( + VisModifierSpec::Public(ErgLocation::Unknown), + tmp_name, + ); let tmp_expr = Expr::Accessor(Accessor::Ident(tmp_ident.clone())); let sig = Signature::Var(VarSignature::new( VarPattern::Ident(tmp_ident), @@ -2792,29 +2816,28 @@ impl ASTConverter { assert_acc.call_expr(args) } py_ast::Stmt::Import(import) => { - let loc = import.location(); + let import_loc = import.location(); let mut imports = vec![]; for name in import.names { let import_acc = Expr::Accessor(Accessor::Ident( - self.convert_ident("__import__".to_string(), loc), + self.convert_ident("__import__".to_string(), import_loc), )); - let cont = if name.asname.is_some() { - format!("\"{}\"", name.name.replace('.', "/")) + let sym = if name.asname.is_some() { + name.name.replace('.', "/") } else { - format!("\"{}\"", name.name.split('.').next().unwrap()) + name.name.split('.').next().unwrap().to_string() }; - let mod_name = Expr::Literal(Literal::new(Token::new( - TokenKind::StrLit, - cont, + let mod_name = Expr::Literal(Literal::new(quoted_symbol( + &sym, name.location().row.get(), - name.location().column.get() - 1, + name.location().column.to_zero_indexed(), ))); let call = import_acc.call1(mod_name); - let loc = name.location(); + let name_loc = name.location(); let def = if let Some(alias) = name.asname { self.register_name_info(&alias, NameKind::Variable); let var = VarSignature::new( - VarPattern::Ident(self.convert_ident(alias.to_string(), loc)), + VarPattern::Ident(self.convert_ident(alias.to_string(), name_loc)), None, ); Def::new( @@ -2841,7 +2864,8 @@ impl ASTConverter { } // from module import foo, bar py_ast::Stmt::ImportFrom(import_from) => { - let loc = import_from.location(); + let mut loc = import_from.location(); + loc.column = loc.column.saturating_add(5); self.convert_from_import(import_from.module, import_from.names, loc) } py_ast::Stmt::Try(try_) => { @@ -2881,14 +2905,9 @@ impl ASTConverter { let import_acc = Expr::Accessor(Accessor::Ident( self.convert_ident("__import__".to_string(), location), )); - let cont = if module == "." { - "\"__init__\"".to_string() - } else { - format!("\"{module}\"") - }; - let mod_name = Expr::Literal(Literal::new(Token::new( - TokenKind::StrLit, - cont, + let sym = if module == "." { "__init__" } else { &module }; + let mod_name = Expr::Literal(Literal::new(quoted_symbol( + sym, location.row.get(), location.column.to_zero_indexed(), ))); @@ -2935,14 +2954,9 @@ impl ASTConverter { .map(|s| s.replace('.', "/")) .unwrap_or_else(|| ".".to_string()); let module_path = Path::new(&module); - let cont = if module == "." { - "\"__init__\"".to_string() - } else { - format!("\"{module}\"") - }; - let mod_name = Expr::Literal(Literal::new(Token::new( - TokenKind::StrLit, - cont, + let sym = if module == "." { "__init__" } else { &module }; + let mod_name = Expr::Literal(Literal::new(quoted_symbol( + sym, location.row.get(), location.column.to_zero_indexed(), ))); @@ -2952,6 +2966,10 @@ impl ASTConverter { if names.len() == 1 && names[0].name.as_str() == "*" { return self.convert_glob_import(location, module); } + let names_range = PySourceRange { + start: names[0].location(), + end: names[names.len() - 1].end_location(), + }; for name in names { let name_path = self .cfg @@ -2975,10 +2993,9 @@ impl ASTConverter { } let mod_name = path.file_name().unwrap(); if name.name.as_str() == mod_name.to_string_lossy().trim_end_matches(".py") { - let cont = format!("\"{module}/{}\"", name.name); - let mod_name = Expr::Literal(Literal::new(Token::new( - TokenKind::StrLit, - cont, + let sym = format!("{module}/{}", name.name); + let mod_name = Expr::Literal(Literal::new(quoted_symbol( + &sym, location.row.get(), location.column.to_zero_indexed(), ))); @@ -2998,7 +3015,8 @@ impl ASTConverter { } let no_import = imports.is_empty(); let attrs = VarRecordAttrs::new(imports); - let pat = VarRecordPattern::new(Token::DUMMY, attrs, Token::DUMMY); + let braces = pyloc_to_ergloc(names_range); + let pat = VarRecordPattern::new(braces, attrs); let var = VarSignature::new(VarPattern::Record(pat), None); let def = Expr::Def(Def::new( Signature::Var(var), diff --git a/crates/py2erg/gen_decl.rs b/crates/py2erg/gen_decl.rs index 7706b58..4d4211e 100644 --- a/crates/py2erg/gen_decl.rs +++ b/crates/py2erg/gen_decl.rs @@ -127,7 +127,7 @@ impl DeclFileGenerator { let decl = format!(".{class_name}: ClassType"); self.code += &decl; self.code.push('\n'); - if let GenTypeObj::Subclass(class) = &def.obj { + if let GenTypeObj::Subclass(class) = def.obj.as_ref() { let sup = class .sup .as_ref() diff --git a/crates/pylyzer_core/handle_err.rs b/crates/pylyzer_core/handle_err.rs index 583a456..7883276 100644 --- a/crates/pylyzer_core/handle_err.rs +++ b/crates/pylyzer_core/handle_err.rs @@ -1,6 +1,6 @@ use erg_common::error::ErrorKind; use erg_common::log; -use erg_common::style::remove_style; +use erg_common::style::{remove_style, StyledStr}; // use erg_common::style::{remove_style, StyledString, Color}; use erg_compiler::context::ModuleContext; use erg_compiler::error::{CompileError, CompileErrors}; @@ -13,11 +13,18 @@ pub(crate) fn filter_errors(ctx: &ModuleContext, errors: CompileErrors) -> Compi } fn handle_name_error(error: CompileError) -> Option { - if error.core.main_message.contains("is already declared") - || error - .core - .main_message - .contains("cannot be assigned more than once") + let main = &error.core.main_message; + if main.contains("is already declared") + || main.contains("cannot be assigned more than once") + || { + main.contains(" is not defined") && { + let name = StyledStr::destyle(main.trim_end_matches(" is not defined")); + error + .core + .get_hint() + .is_some_and(|hint| hint.contains(name)) + } + } { None } else {