diff --git a/compiler/erg_common/error.rs b/compiler/erg_common/error.rs index 057e3c2b..714c8480 100644 --- a/compiler/erg_common/error.rs +++ b/compiler/erg_common/error.rs @@ -10,9 +10,9 @@ use crate::style::Attribute; use crate::style::Characters; use crate::style::Color; use crate::style::StyledStr; -use crate::style::StyledString; use crate::style::StyledStrings; use crate::style::Theme; +use crate::style::THEME; use crate::traits::{Locational, Stream}; use crate::{impl_display_from_debug, switch_lang}; @@ -228,37 +228,6 @@ impl From<&str> for ErrorKind { /// #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] pub enum Location { - /// - /// Error used when the error is caused by a discrepancy with a code on another line - /// - /// # Example - /// - /// Ownership error - /// - /// ```erg - /// a: Nat = 1 - /// a.consume_ownership() // move occurs - /// - /// function(a) // borrowed after moved - /// ``` - /// - /// `a` moves ownership in a method(or function) that are defined and consume it. - /// - /// ```erg - /// Location::RangePair { - /// ln_first: (2, 2), - /// col_first: (0, 1), - /// ln_second: (4, 4), - /// col_second: (9, 10), - /// } - /// ``` - /// - RangePair { - ln_first: (usize, usize), - col_first: (usize, usize), - ln_second: (usize, usize), - col_second: (usize, usize), - }, /// /// Location used for basic errors /// ```erg @@ -310,128 +279,39 @@ impl Location { } } - pub fn pair(lhs: Self, rhs: Self) -> Self { - Self::RangePair { - ln_first: (lhs.ln_begin().unwrap(), lhs.ln_end().unwrap()), - col_first: (lhs.col_begin().unwrap(), lhs.col_end().unwrap()), - ln_second: (rhs.ln_begin().unwrap(), rhs.ln_end().unwrap()), - col_second: (rhs.col_begin().unwrap(), rhs.col_end().unwrap()), - } - } - pub const fn ln_begin(&self) -> Option { match self { - Self::RangePair { - ln_first: (ln_begin, _), - .. + Self::Range { ln_begin, .. } | Self::LineRange(ln_begin, _) | Self::Line(ln_begin) => { + Some(*ln_begin) } - | Self::Range { ln_begin, .. } - | Self::LineRange(ln_begin, _) - | Self::Line(ln_begin) => Some(*ln_begin), Self::Unknown => None, } } pub const fn ln_end(&self) -> Option { match self { - Self::RangePair { - ln_second: (_, ln_end), - .. + Self::Range { ln_end, .. } | Self::LineRange(ln_end, _) | Self::Line(ln_end) => { + Some(*ln_end) } - | Self::Range { ln_end, .. } - | Self::LineRange(ln_end, _) - | Self::Line(ln_end) => Some(*ln_end), Self::Unknown => None, } } pub const fn col_begin(&self) -> Option { match self { - Self::RangePair { - col_first: (col_begin, _), - .. - } - | Self::Range { col_begin, .. } => Some(*col_begin), + Self::Range { col_begin, .. } => Some(*col_begin), _ => None, } } pub const fn col_end(&self) -> Option { match self { - Self::RangePair { - col_second: (_, col_end), - .. - } - | Self::Range { col_end, .. } => Some(*col_end), + Self::Range { col_end, .. } => Some(*col_end), _ => None, } } } -/// In Erg, common parts used by error. -/// Must be wrap when to use. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct ErrorCore { - pub errno: usize, - pub kind: ErrorKind, - pub loc: Location, - pub desc: String, - pub hint: Option, -} - -impl ErrorCore { - pub fn new>( - errno: usize, - kind: ErrorKind, - loc: Location, - desc: S, - hint: Option, - ) -> Self { - Self { - errno, - kind, - loc, - desc: desc.into(), - hint, - } - } - - pub fn dummy(errno: usize) -> Self { - Self::new( - errno, - DummyError, - Location::Line(errno as usize), - "", - None, - ) - } - - pub fn unreachable(fn_name: &str, line: u32) -> Self { - Self::bug(line as usize, Location::Line(line as usize), fn_name, line) - } - - pub fn bug(errno: usize, loc: Location, fn_name: &str, line: u32) -> Self { - const URL: StyledStr = StyledStr::new( - "https://github.com/erg-lang/erg", - Some(Color::White), - Some(Attribute::Underline), - ); - - Self::new( - errno, - CompilerSystemError, - loc, - switch_lang!( - "japanese" => format!("これはErgのバグです、開発者に報告して下さい({URL})\n{fn_name}:{line}より発生"), - "simplified_chinese" => format!("这是Erg的bug,请报告给{URL}\n原因来自: {fn_name}:{line}"), - "traditional_chinese" => format!("这是Erg的bug,请报告给{URL}\n原因来自: {fn_name}:{line}"), - "english" => format!("this is a bug of Erg, please report it to {URL}\ncaused from: {fn_name}:{line}"), - ), - None, - ) - } -} - #[allow(clippy::too_many_arguments)] fn format_context( e: &E, @@ -445,6 +325,8 @@ fn format_context( chars: &Characters, // kinds of error for specify the color mark: char, + sub_msg: &[String], + hint: Option<&String>, ) -> String { let mark = mark.to_string(); let codes = if e.input().is_repl() { @@ -484,20 +366,326 @@ fn format_context( } context.push_str("\n"); } - context.push_str_with_color(&offset, gutter_color); - context.push_str(&" ".repeat(col_end - 1)); - context.push_str_with_color(&chars.left_bottom_line(), err_color); - context.to_string() + + let msg_num = sub_msg.len().saturating_sub(1); + for (i, msg) in sub_msg.iter().enumerate() { + context.push_str_with_color(&offset, gutter_color); + context.push_str(&" ".repeat(col_end - 1)); + if i == msg_num && hint.is_none() { + context.push_str_with_color(&chars.left_bottom_line(), err_color); + } else { + context.push_str_with_color(&chars.left_cross(), err_color); + } + context.push_str(msg); + context.push_str("\n") + } + if let Some(hint) = hint { + context.push_str_with_color(&offset, gutter_color); + context.push_str(&" ".repeat(col_end - 1)); + context.push_str_with_color(&chars.left_bottom_line(), err_color); + context.push_str(hint); + context.push_str("\n") + } + context.to_string() + "\n" +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct SubMessage { + pub loc: Location, + msg: Vec, + hint: Option, +} + +impl SubMessage { + /// + /// Used when the msg or hint si empty. + /// `msg` is Vec\ instead of Option\ because it can be used when there are multiple `msg`s as well as multiple lines. + /// # Example + /// ``` + /// let msg = SubMessage::ambiguous_new(loc, vec![], None); // this code same as only_loc() + /// + /// let hint = Some("hint message here".to_string()) + /// let msg = SubMessage::ambiguous_new(loc, vec![], hint); + /// /* example + /// ------- + /// `- hint message here + /// */ + /// + /// let hint = Some("hint here".to_string()) + /// let first = StyledString::new("1th message", Color::Red, None); + /// let second = StyledString::new("2th message", Color::White, None); + /// : + /// let nth = StyledString::new("nth message", Color::Green, None); + /// let msg = SubMessage::ambiguous_new( + /// loc, + /// vec![ + /// first.to_string(), + /// second.to_string(), + /// ..., + /// nth.to_string(), + /// ], + /// hint); + /// /* example + /// ------- + /// :- 1th message + /// :- 2th message + /// : + /// :- nth message + /// `- hint here + /// */ + /// + /// ``` + /// + pub fn ambiguous_new(loc: Location, msg: Vec, hint: Option) -> Self { + Self { loc, msg, hint } + } + + /// + /// Used when only Location is fixed. + /// In this case, error position is just modified + /// # Example + /// ``` + /// let sub_msg = SubMessage::only_loc(loc); + /// ``` + pub fn only_loc(loc: Location) -> Self { + Self { + loc, + msg: Vec::new(), + hint: None, + } + } + + pub fn set_hint>(&mut self, hint: S) { + self.hint = Some(hint.into()); + } + + pub fn get_hint(self) -> Option { + self.hint + } + + // Line breaks are not included except for line breaks that signify the end of a sentence. + // In other words, do not include blank lines for formatting purposes. + fn format_code_and_pointer( + &self, + e: &E, + err_color: Color, + gutter_color: Color, + mark: char, + chars: &Characters, + ) -> String { + match self.loc { + Location::Range { + ln_begin, + col_begin, + ln_end, + col_end, + } => format_context( + e, + ln_begin, + ln_end, + col_begin, + col_end, + err_color, + gutter_color, + chars, + mark, + &self.msg, + self.hint.as_ref(), + ), + Location::LineRange(ln_begin, ln_end) => { + let input = e.input(); + let (vbreak, vbar) = chars.gutters(); + let mut cxt = StyledStrings::default(); + let codes = if input.is_repl() { + vec![input.reread()] + } else { + input.reread_lines(ln_begin, ln_end) + }; + let mark = mark.to_string(); + for (i, lineno) in (ln_begin..=ln_end).enumerate() { + cxt.push_str_with_color(&format!("{lineno} {vbar} "), gutter_color); + cxt.push_str(&codes[i]); + cxt.push_str("\n"); + cxt.push_str_with_color( + &format!("{} {}", &" ".repeat(lineno.to_string().len()), vbreak), + gutter_color, + ); + cxt.push_str(&" ".repeat(lineno.to_string().len())); + cxt.push_str_with_color(&mark.repeat(cmp::max(1, codes[i].len())), err_color); + cxt.push_str("\n"); + } + cxt.push_str("\n"); + for msg in self.msg.iter() { + cxt.push_str(msg); + cxt.push_str("\n"); + } + if let Some(hint) = self.hint.as_ref() { + cxt.push_str(hint); + cxt.push_str("\n"); + } + cxt.to_string() + } + Location::Line(lineno) => { + let input = e.input(); + let (_, vbar) = chars.gutters(); + let code = if input.is_repl() { + input.reread() + } else { + input.reread_lines(lineno, lineno).remove(0) + }; + let mut cxt = StyledStrings::default(); + cxt.push_str_with_color(&format!(" {lineno} {} ", vbar), gutter_color); + cxt.push_str(&code); + cxt.push_str("\n"); + for msg in self.msg.iter() { + cxt.push_str(msg); + cxt.push_str("\n"); + } + if let Some(hint) = self.hint.as_ref() { + cxt.push_str(hint); + cxt.push_str("\n"); + } + cxt.push_str("\n"); + cxt.to_string() + } + Location::Unknown => match e.input() { + Input::File(_) => "\n".to_string(), + other => { + let (_, vbar) = chars.gutters(); + let mut cxt = StyledStrings::default(); + cxt.push_str_with_color(&format!(" ? {} ", vbar), gutter_color); + cxt.push_str(&other.reread()); + cxt.push_str("\n"); + for msg in self.msg.iter() { + cxt.push_str(msg); + cxt.push_str("\n"); + } + if let Some(hint) = self.hint.as_ref() { + cxt.push_str(hint); + cxt.push_str("\n"); + } + cxt.push_str("\n"); + cxt.to_string() + } + }, + } + } +} + +/// In Erg, common parts used by error. +/// Must be wrap when to use. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ErrorCore { + pub sub_messages: Vec, + pub main_message: String, + pub errno: usize, + pub kind: ErrorKind, + pub loc: Location, + theme: Theme, +} + +impl ErrorCore { + pub fn new>( + sub_messages: Vec, + main_message: S, + errno: usize, + kind: ErrorKind, + loc: Location, + ) -> Self { + Self { + sub_messages, + main_message: main_message.into(), + errno, + kind, + loc, + theme: THEME, + } + } + + pub fn dummy(errno: usize) -> Self { + Self::new( + vec![SubMessage::only_loc(Location::Line(errno as usize))], + "", + errno, + DummyError, + Location::Line(errno as usize), + ) + } + + pub fn unreachable(fn_name: &str, line: u32) -> Self { + Self::bug(line as usize, Location::Line(line as usize), fn_name, line) + } + + pub fn bug(errno: usize, loc: Location, fn_name: &str, line: u32) -> Self { + const URL: StyledStr = StyledStr::new( + "https://github.com/erg-lang/erg", + Some(Color::White), + Some(Attribute::Underline), + ); + + let m_msg = switch_lang!( + "japanese" => format!("これはErgのバグです、開発者に報告して下さい({URL})\n{fn_name}:{line}より発生"), + "simplified_chinese" => format!("这是Erg的bug,请报告给{URL}\n原因来自: {fn_name}:{line}"), + "traditional_chinese" => format!("这是Erg的bug,请报告给{URL}\n原因来自: {fn_name}:{line}"), + "english" => format!("this is a bug of Erg, please report it to {URL}\ncaused from: {fn_name}:{line}"), + ); + Self::new( + vec![SubMessage::only_loc(loc)], + &m_msg, + errno, + CompilerSystemError, + loc, + ) + } + + pub fn fmt_header(&self, color: Color, caused_by: &str, input: &str) -> String { + let loc = match self.loc { + Location::Range { + ln_begin, ln_end, .. + } if ln_begin == ln_end => format!(", line {ln_begin}"), + Location::Range { + ln_begin, ln_end, .. + } + | Location::LineRange(ln_begin, ln_end) => format!(", line {ln_begin}..{ln_end}"), + Location::Line(lineno) => format!(", line {lineno}"), + Location::Unknown => "".to_string(), + }; + let kind = if self.kind.is_error() { + "Error" + } else if self.kind.is_warning() { + "Warning" + } else { + "Exception" + }; + let kind = self.theme.characters.error_kind_format(kind, self.errno); + format!( + "{kind}: File {input}{loc}, {caused_by}", + kind = StyledStr::new(&kind, Some(color), Some(Attribute::Bold)) + ) + } + + fn specified_theme(&self) -> (Color, char) { + let (color, mark) = if self.kind.is_error() { + self.theme.error() + } else if self.kind.is_warning() { + self.theme.warning() + } else { + self.theme.exception() + }; + (color, mark) + } } /// format: /// ```txt /// Error[#{.errno}]: File {file}, line {.loc (as line)}, in {.caused_by} -/// {.loc (as line)}| {src} -/// {pointer} -/// {.kind}: {.desc} /// -/// {.hint} +/// {.loc (as line)}| {src} +/// {offset} : {pointer} +/// {offset} : {sub_msgs} +/// {offset} : {.hint} +/// +/// {.kind}: {.desc} /// /// ``` /// @@ -505,21 +693,20 @@ fn format_context( /// ```txt /// Error[#2223]: File , line 1, in /// -/// 1 | 100 = i -/// --- -/// ╰─ SyntaxError: cannot assign to 100 +/// 1 │ 100 = i +/// · --- +/// · │─ sub_msg1: first sub message here +/// · │─ sub_msg2: second sub message here +/// · ╰─ hint: hint message here /// -/// hint: hint message here +/// SyntaxError: cannot assign to 100 /// /// ``` pub trait ErrorDisplay { fn core(&self) -> &ErrorCore; fn input(&self) -> &Input; - /// Colors and indication char for each type(error, warning, exception) - fn theme(&self) -> &Theme; /// The block name the error caused. /// This will be None if the error occurred before semantic analysis. - /// As for the internal error, do not put the fn name here. fn caused_by(&self) -> &str; /// the previous error that caused this error. fn ref_inner(&self) -> Option<&Self>; @@ -539,248 +726,44 @@ pub trait ErrorDisplay { } fn show(&self) -> String { - let theme = self.theme(); - let ((color, mark), kind) = if self.core().kind.is_error() { - (theme.error(), "Error") - } else if self.core().kind.is_warning() { - (theme.warning(), "Warning") - } else { - (theme.exception(), "Exception") - }; - - let (gutter_color, chars) = theme.characters(); - let kind = StyledString::new( - &chars.error_kind_format(kind, self.core().errno), - Some(color), - Some(Attribute::Bold), - ); - - // When hint is None, hint desc is "" and empty line is displayed, but hint is Some(...), hint desc is "..." and filled by text - if let Some(hint) = self.core().hint.as_ref() { - let (hint_color, _) = theme.hint(); - let mut hints = StyledStrings::default(); - hints.push_str_with_color_and_attribute("hint: ", hint_color, Attribute::Bold); - hints.push_str(hint); - format!( - "\ -{} -{}{}: {} - -{} - -", - self.format_header(kind), - self.format_code_and_pointer(color, gutter_color, mark, chars), - self.core().kind, - self.core().desc, - hints, - ) - } else { - format!( - "\ -{} -{}{}: {} - -", - self.format_header(kind), - self.format_code_and_pointer(color, gutter_color, mark, chars), - self.core().kind, - self.core().desc, - ) + let core = self.core(); + let (color, mark) = core.specified_theme(); + let (gutter_color, chars) = core.theme.characters(); + let mut msg = String::new(); + msg += &core.fmt_header(color, self.caused_by(), self.input().enclosed_name()); + msg += "\n\n"; + for sub_msg in &core.sub_messages { + msg += &sub_msg.format_code_and_pointer(self, color, gutter_color, mark, chars); } + msg += &core.main_message; + msg += "\n\n"; + msg } /// for fmt::Display fn format(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let theme = self.theme(); - let ((color, mark), kind) = if self.core().kind.is_error() { - (theme.error(), "Error") - } else if self.core().kind.is_warning() { - (theme.warning(), "Warning") - } else { - (theme.exception(), "Exception") - }; - let (gutter_color, chars) = theme.characters(); - let kind = StyledString::new( - &chars.error_kind_format(kind, self.core().errno), - Some(color), - Some(Attribute::Bold), - ); - - // When hint is None, hint desc is "" and empty line is displayed, but hint is Some(...), hint desc is "..." and filled by text - if let Some(hint) = self.core().hint.as_ref() { - let (hint_color, _) = theme.hint(); - let mut hints = StyledStrings::default(); - hints.push_str_with_color_and_attribute("hint: ", hint_color, Attribute::Bold); - hints.push_str(hint); - writeln!( + let core = self.core(); + let (color, mark) = core.specified_theme(); + let (gutter_color, chars) = core.theme.characters(); + write!( + f, + "{}\n\n", + core.fmt_header(color, self.caused_by(), self.input().enclosed_name()) + )?; + for sub_msg in &core.sub_messages { + write!( f, - "\ -{} -{}{}: {} - -{} - -", - self.format_header(kind), - self.format_code_and_pointer(color, gutter_color, mark, chars), - self.core().kind, - self.core().desc, - hints, - )?; - } else { - writeln!( - f, - "\ -{} -{}{}: {} - -", - self.format_header(kind), - self.format_code_and_pointer(color, gutter_color, mark, chars), - self.core().kind, - self.core().desc, + "{}", + &sub_msg.format_code_and_pointer(self, color, gutter_color, mark, chars) )?; } + write!(f, "{}\n\n", core.main_message)?; if let Some(inner) = self.ref_inner() { inner.format(f) } else { Ok(()) } } - - fn format_header(&self, kind: StyledString) -> String { - let loc = match self.core().loc { - Location::Range { - ln_begin, ln_end, .. - } if ln_begin == ln_end => format!(", line {ln_begin}"), - Location::Range { - ln_begin, ln_end, .. - } - | Location::LineRange(ln_begin, ln_end) => format!(", line {ln_begin}..{ln_end}"), - Location::RangePair { - ln_first: (l1, l2), - ln_second: (l3, l4), - .. - } => format!(", line {l1}..{l2}, {l3}..{l4}"), - Location::Line(lineno) => format!(", line {lineno}"), - Location::Unknown => "".to_string(), - }; - let caused_by = if self.caused_by() != "" { - format!(", in {}", self.caused_by()) - } else { - "".to_string() - }; - format!( - "{kind}: File {input}{loc}{caused_by}\n", - input = self.input().enclosed_name(), - ) - } - - fn format_code_and_pointer( - &self, - err_color: Color, - gutter_color: Color, - mark: char, - chars: &Characters, - ) -> String { - match self.core().loc { - // TODO: Current implementation does not allow for multiple descriptions of errors to be given at each location - // In the future, this will be implemented in a different structure that can handle multiple lines and files - Location::RangePair { - ln_first, - col_first, - ln_second, - col_second, - } => { - format_context( - self, - ln_first.0, - ln_first.1, - col_first.0, - col_first.1, - err_color, - gutter_color, - chars, - mark, - ) + - "\n" // TODO: dealing with error chains - + &format_context( - self, - ln_second.0, - ln_second.1, - col_second.0, - col_second.1, - err_color, - gutter_color, - chars, - mark, - ) - } - Location::Range { - ln_begin, - col_begin, - ln_end, - col_end, - } => format_context( - self, - ln_begin, - ln_end, - col_begin, - col_end, - err_color, - gutter_color, - chars, - mark, - ), - Location::LineRange(ln_begin, ln_end) => { - let (_, vbar) = chars.gutters(); - let mut cxt = StyledStrings::default(); - let codes = if self.input().is_repl() { - vec![self.input().reread()] - } else { - self.input().reread_lines(ln_begin, ln_end) - }; - let mark = mark.to_string(); - for (i, lineno) in (ln_begin..=ln_end).enumerate() { - cxt.push_str_with_color(&format!("{lineno} {}", vbar), err_color); - cxt.push_str(&codes[i]); - cxt.push_str("\n"); - cxt.push_str(&" ".repeat(lineno.to_string().len() + 3)); // +3 means ` | ` - cxt.push_str_with_color( - &mark.repeat(cmp::max(1, codes[i].len())), - gutter_color, - ); - cxt.push_str("\n"); - } - cxt.to_string() - } - Location::Line(lineno) => { - let (_, vbar) = chars.gutters(); - let code = if self.input().is_repl() { - self.input().reread() - } else { - self.input().reread_lines(lineno, lineno).remove(0) - }; - let mut cxt = StyledStrings::default(); - cxt.push_str_with_color(&format!(" {lineno} {} ", vbar), gutter_color); - cxt.push_str(&code); - cxt.push_str("\n"); - cxt.to_string() - } - Location::Unknown => match self.input() { - Input::File(_) => "\n".to_string(), - - other => { - let (_, vbar) = chars.gutters(); - let mut cxt = StyledStrings::default(); - cxt.push_str_with_color(&format!(" ? {}", vbar), gutter_color); - cxt.push_str(&other.reread()); - cxt.to_string() - } - }, - } - } } #[macro_export] diff --git a/compiler/erg_common/str.rs b/compiler/erg_common/str.rs index 425a0441..04a2bc94 100644 --- a/compiler/erg_common/str.rs +++ b/compiler/erg_common/str.rs @@ -1,4 +1,4 @@ -use std::borrow::Borrow; +use std::borrow::{Borrow, Cow}; use std::fmt; use std::hash::{Hash, Hasher}; use std::ops::{Add, Deref}; @@ -61,6 +61,15 @@ impl From for String { } } +impl<'a> From for Cow<'a, str> { + fn from(s: Str) -> Self { + match s { + Str::Static(s) => Cow::Owned(s.to_owned()), + _ => unreachable!(), + } + } +} + // &'static str -> &strになってしまわないように // あえて`impl> From for Str { ... }`はしない impl From<&'static str> for Str { diff --git a/compiler/erg_common/style.rs b/compiler/erg_common/style.rs index d4feb738..171a8830 100644 --- a/compiler/erg_common/style.rs +++ b/compiler/erg_common/style.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + pub const ATTR_RESET: &str = "\x1b[0m"; pub const BOLD: &str = "\x1b[1m"; pub const UNDERLINE: &str = "\x1b[4m"; @@ -15,15 +17,15 @@ pub const RED: &str = "\x1b[91m"; pub const WHITE: &str = "\x1b[97m"; pub const YELLOW: &str = "\x1b[93m"; // custom colors when use `pretty` -pub const CUSTOM_RED: &str = "\x1b[38;2;185;64;71m"; -pub const CUSTOM_BLUE: &str = "\x1b[38;2;230;234;227m"; -pub const CUSTOM_GRAY: &str = "\x1b[38;2;244;0;25m"; -pub const CUSTOM_CYAN: &str = "\x1b[38;2;160;216;239m"; -pub const CUSTOM_MAGENTA: &str = "\x1b[38;2;103;65;150m"; -pub const CUSTOM_GREEN: &str = "\x1b[38;2;170;209;71m"; -pub const CUSTOM_YELLOW: &str = "\x1b[38;2;230;180;34m"; +pub const CUSTOM_RED: &str = "\x1b[38;2;255;76;76m"; +pub const CUSTOM_BLUE: &str = "\x1b[38;2;76;76;255m"; +pub const CUSTOM_GRAY: &str = "\x1b[38;2;231;231;235m"; +pub const CUSTOM_CYAN: &str = "\x1b[38;2;76;255;255m"; +pub const CUSTOM_MAGENTA: &str = "\x1b[38;2;165;76;255m"; +pub const CUSTOM_GREEN: &str = "\x1b[38;2;76;255;76m"; +pub const CUSTOM_YELLOW: &str = "\x1b[38;2;255;255;76m"; -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Hash)] pub enum Color { Reset, Black, @@ -68,7 +70,7 @@ impl Color { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Hash)] pub enum Attribute { Reset, Underline, @@ -87,13 +89,14 @@ impl Attribute { } } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct ThemeColors { pub error: Color, pub warning: Color, pub exception: Color, pub gutter: Color, pub hint: Color, + pub accent: Color, } #[cfg(not(feature = "pretty"))] @@ -103,6 +106,7 @@ pub const COLORS: ThemeColors = ThemeColors { exception: Color::Magenta, gutter: Color::Cyan, hint: Color::Green, + accent: Color::White, }; #[cfg(feature = "pretty")] @@ -112,9 +116,10 @@ pub const COLORS: ThemeColors = ThemeColors { exception: Color::CustomMagenta, gutter: Color::CustomCyan, hint: Color::CustomGreen, + accent: Color::CustomGray, }; -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Characters { hat: char, // error wave: char, // exception @@ -141,10 +146,10 @@ impl Characters { (self.vbreak, self.vbar) } - // " `- " + // "`- " #[cfg(not(feature = "unicode"))] pub fn left_bottom_line(&self) -> String { - format!(" {}{} ", self.lbot, self.line) + format!("{}{} ", self.lbot, self.line) } // `╰─ ` @@ -153,6 +158,18 @@ impl Characters { format!("{}{} ", self.lbot, self.line) } + // "|- " + #[cfg(not(feature = "unicode"))] + pub fn left_cross(&self) -> String { + format!("{}{} ", self.vbar, self.line) + } + + // "│─ " + #[cfg(feature = "unicode")] + pub fn left_cross(&self) -> String { + format!("{}{} ", self.vbar, self.line) + } + // kind[padded error number] #[cfg(not(feature = "pretty"))] pub fn error_kind_format(&self, kind: &str, err_num: usize) -> String { @@ -177,7 +194,7 @@ impl Characters { } } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Theme { pub colors: ThemeColors, pub characters: Characters, @@ -254,11 +271,7 @@ pub struct StyledStr<'a> { } impl<'a> StyledStr<'a> { - pub const fn new<'b: 'a>( - text: &'b str, - color: Option, - attribute: Option, - ) -> Self { + pub const fn new(text: &'a str, color: Option, attribute: Option) -> Self { Self { text, color, @@ -300,9 +313,22 @@ pub struct StyledString { } impl StyledString { - pub fn new(s: &str, color: Option, attribute: Option) -> Self { + /// + /// # Example + /// ``` + /// let s = String::from("Hello, world"); + /// StyledString::new(s, None, None); + /// let s = "Hello, world"; + /// StyledString::new(s, None, None); + /// ``` + pub fn new<'a, S: Into>>( + s: S, + color: Option, + attribute: Option, + ) -> Self { + let text: Cow<'a, str> = s.into(); Self { - text: String::from(s), + text: text.into_owned(), color, attribute, } @@ -320,7 +346,11 @@ impl StyledString { /// println!("{text}"); // Two lines of text underlined are displayed /// ``` pub fn push_str(&mut self, s: &str) { - self.text.push_str(s); + self.text.push_str(s) + } + + pub fn is_empty(&self) -> bool { + self.text.is_empty() } } @@ -405,9 +435,10 @@ impl StyledStrings { /// texts.push_str_with_color("\n If you want to add break lines, you should add `\n`.", Color::Magenta); /// println!("{}", texts); /// ``` - pub fn push_str_with_color(&mut self, s: &str, color: Color) { + pub fn push_str_with_color<'a, S: Into>>(&mut self, s: S, color: Color) { if self.is_same_color(color) { - self.texts.last_mut().unwrap().text.push_str(s); + let text = s.into(); + self.texts.last_mut().unwrap().text.push_str(&text); } else { self.texts.push(StyledString::new(s, Some(color), None)); } @@ -426,23 +457,37 @@ impl StyledStrings { /// // texts.push_str_with_color_and_attribute("Must be specify the color and attribute", None, Attribute::Underline); /// println!("{}", texts); /// ``` - pub fn push_str_with_color_and_attribute(&mut self, s: &str, color: Color, attr: Attribute) { + pub fn push_str_with_color_and_attribute<'a, S: Into>>( + &mut self, + s: S, + color: Color, + attr: Attribute, + ) { if self.is_same_color(color) && self.is_same_attribute(attr) { - self.texts.last_mut().unwrap().text.push_str(s); + let text = s.into(); + self.texts.last_mut().unwrap().text.push_str(&text); } else { self.texts .push(StyledString::new(s, Some(color), Some(attr))); } } - pub fn is_same_color(&self, color: Color) -> bool { + /// + /// Determine if all strings in Vec are empty + /// Returns False if any string is present. + /// + pub fn is_empty(&self) -> bool { + self.texts.iter().all(|s| s.is_empty()) + } + + fn is_same_color(&self, color: Color) -> bool { if let Some(text) = self.texts.last() { return text.color == Some(color); } false } - pub fn is_same_attribute(&self, attr: Attribute) -> bool { + fn is_same_attribute(&self, attr: Attribute) -> bool { if let Some(text) = self.texts.last() { if let Some(text_attr) = text.attribute { return text_attr == attr; @@ -461,6 +506,12 @@ impl std::fmt::Display for StyledStrings { } } +impl From for String { + fn from(s: StyledStrings) -> Self { + s.to_string() + } +} + #[cfg(test)] mod tests { use super::*; @@ -513,10 +564,15 @@ mod tests { Attribute::Bold, ); texts.push_str_with_color_and_attribute( - "White and underlined text", - Color::White, + "Blue and underlined text\n", + Color::Blue, Attribute::Underline, ); + texts.push_str_with_color_and_attribute( + "Red and reversed text", + Color::Red, + Attribute::Reversed, + ); println!("{}", texts); } } diff --git a/compiler/erg_common/traits.rs b/compiler/erg_common/traits.rs index abbce927..10017ba6 100644 --- a/compiler/erg_common/traits.rs +++ b/compiler/erg_common/traits.rs @@ -477,12 +477,7 @@ pub trait Locational { fn ln_begin(&self) -> Option { match self.loc() { - Location::RangePair { - ln_first: (ln_begin, _), - .. - } - | Location::Range { ln_begin, .. } - | Location::LineRange(ln_begin, _) => Some(ln_begin), + Location::Range { ln_begin, .. } | Location::LineRange(ln_begin, _) => Some(ln_begin), Location::Line(lineno) => Some(lineno), Location::Unknown => None, } @@ -490,12 +485,7 @@ pub trait Locational { fn ln_end(&self) -> Option { match self.loc() { - Location::RangePair { - ln_second: (_, ln_end), - .. - } - | Location::Range { ln_end, .. } - | Location::LineRange(_, ln_end) => Some(ln_end), + Location::Range { ln_end, .. } | Location::LineRange(_, ln_end) => Some(ln_end), Location::Line(lineno) => Some(lineno), Location::Unknown => None, } diff --git a/compiler/erg_compiler/context/eval.rs b/compiler/erg_compiler/context/eval.rs index 2f8c945c..06f33279 100644 --- a/compiler/erg_compiler/context/eval.rs +++ b/compiler/erg_compiler/context/eval.rs @@ -241,7 +241,8 @@ impl Context { match subr { ConstSubr::User(_user) => todo!(), ConstSubr::Builtin(builtin) => builtin.call(args, self).map_err(|mut e| { - e.0.loc = loc; + // TODO: Is it possible to get 0? + e.0.sub_messages.get_mut(0).unwrap().loc = loc; EvalErrors::from(EvalError::new( *e.0, self.cfg.input.clone(), diff --git a/compiler/erg_compiler/context/initialize/const_func.rs b/compiler/erg_compiler/context/initialize/const_func.rs index fc9a983c..6b707d8b 100644 --- a/compiler/erg_compiler/context/initialize/const_func.rs +++ b/compiler/erg_compiler/context/initialize/const_func.rs @@ -6,29 +6,39 @@ use crate::context::Context; use crate::ty::constructors::{and, mono}; use crate::ty::value::{EvalValueResult, GenTypeObj, TypeObj, ValueObj}; use crate::ty::ValueArgs; -use erg_common::error::{ErrorCore, ErrorKind, Location}; -use erg_common::style::{RED, RESET, YELLOW}; +use erg_common::error::{ErrorCore, ErrorKind, Location, SubMessage}; +use erg_common::style::{Color, StyledStr, StyledString, THEME}; + +const ERR: Color = THEME.colors.error; +const WARN: Color = THEME.colors.warning; + +const SUP_ERR: StyledStr = StyledStr::new("Super", Some(ERR), None); +const SUP_WARN: StyledStr = StyledStr::new("Super", Some(WARN), None); +const CLASS_ERR: StyledStr = StyledStr::new("Class", Some(ERR), None); +const REQ_ERR: StyledStr = StyledStr::new("Requirement", Some(ERR), None); +const REQ_WARN: StyledStr = StyledStr::new("Requirement", Some(WARN), None); /// Requirement: Type, Impl := Type -> ClassType pub fn class_func(mut args: ValueArgs, ctx: &Context) -> EvalValueResult { let require = args.remove_left_or_key("Requirement").ok_or_else(|| { ErrorCore::new( + vec![SubMessage::only_loc(Location::Unknown)], + format!("{REQ_ERR} is not passed"), line!() as usize, ErrorKind::TypeError, Location::Unknown, - format!("{RED}Requirement{RESET} is not passed"), - None, ) })?; let Some(require) = require.as_type() else { + let require = StyledString::new(&format!("{}", require), Some(ERR), None); return Err(ErrorCore::new( + vec![SubMessage::only_loc(Location::Unknown)], + format!( + "non-type object {require} is passed to {REQ_WARN}", + ), line!() as usize, ErrorKind::TypeError, Location::Unknown, - format!( - "non-type object {RED}{require}{RESET} is passed to {YELLOW}Requirement{RESET}", - ), - None, ).into()); }; let impls = args.remove_left_or_key("Impl"); @@ -40,23 +50,25 @@ pub fn class_func(mut args: ValueArgs, ctx: &Context) -> EvalValueResult ClassType pub fn inherit_func(mut args: ValueArgs, ctx: &Context) -> EvalValueResult { let sup = args.remove_left_or_key("Super").ok_or_else(|| { + let sup = StyledStr::new("Super", Some(ERR), None); ErrorCore::new( + vec![SubMessage::only_loc(Location::Unknown)], + format!("{sup} is not passed"), line!() as usize, ErrorKind::KeyError, Location::Unknown, - format!("{RED}Super{RESET} is not passed"), - None, ) })?; let Some(sup) = sup.as_type() else { + let sup_ty = StyledString::new(&format!("{}", sup), Some(ERR), None); return Err(ErrorCore::new( + vec![SubMessage::only_loc(Location::Unknown)], + format!( + "non-class object {sup_ty} is passed to {SUP_WARN}", + ), line!() as usize, ErrorKind::TypeError, Location::Unknown, - format!( - "non-class object {RED}{sup}{RESET} is passed to {YELLOW}Super{RESET}", - ), - None, ).into()); }; let impls = args.remove_left_or_key("Impl"); @@ -74,11 +86,11 @@ pub fn inherit_func(mut args: ValueArgs, ctx: &Context) -> EvalValueResult EvalValueResult { let class = args.remove_left_or_key("Class").ok_or_else(|| { ErrorCore::new( + vec![SubMessage::only_loc(Location::Unknown)], + format!("{CLASS_ERR} is not passed"), line!() as usize, ErrorKind::KeyError, Location::Unknown, - format!("{RED}Class{RESET} is not passed"), - None, ) })?; match class { @@ -106,22 +118,23 @@ pub fn inheritable_func(mut args: ValueArgs, _ctx: &Context) -> EvalValueResult< pub fn trait_func(mut args: ValueArgs, ctx: &Context) -> EvalValueResult { let require = args.remove_left_or_key("Requirement").ok_or_else(|| { ErrorCore::new( + vec![SubMessage::only_loc(Location::Unknown)], + format!("{REQ_ERR} is not passed"), line!() as usize, ErrorKind::KeyError, Location::Unknown, - format!("{RED}Requirement{RESET} is not passed"), - None, ) })?; let Some(require) = require.as_type() else { + let require = StyledString::new(&format!("{}", require), Some(ERR), None); return Err(ErrorCore::new( + vec![SubMessage::only_loc(Location::Unknown)], + format!( + "non-type object {require} is passed to {REQ_WARN}", + ), line!() as usize, ErrorKind::TypeError, Location::Unknown, - format!( - "non-type object {RED}{require}{RESET} is passed to {YELLOW}Requirement{RESET}", - ), - None, ).into()); }; let impls = args.remove_left_or_key("Impl"); @@ -134,22 +147,23 @@ pub fn trait_func(mut args: ValueArgs, ctx: &Context) -> EvalValueResult EvalValueResult { let sup = args.remove_left_or_key("Super").ok_or_else(|| { ErrorCore::new( + vec![SubMessage::only_loc(Location::Unknown)], + format!("{SUP_ERR} is not passed"), line!() as usize, ErrorKind::KeyError, Location::Unknown, - format!("{RED}Super{RESET} is not passed"), - None, ) })?; let Some(sup) = sup.as_type() else { + let sup = StyledString::new(&format!("{}", sup), Some(ERR), None); return Err(ErrorCore::new( + vec![SubMessage::only_loc(Location::Unknown)], + format!( + "non-trait object {sup} is passed to {SUP_WARN}", + ), line!() as usize, ErrorKind::TypeError, Location::Unknown, - format!( - "non-trait object {RED}{sup}{RESET} is passed to {YELLOW}Super{RESET}", - ), - None, ).into()); }; let impls = args.remove_left_or_key("Impl"); @@ -171,16 +185,16 @@ pub fn __array_getitem__(mut args: ValueArgs, ctx: &Context) -> EvalValueResult< Ok(v.clone()) } else { Err(ErrorCore::new( - line!() as usize, - ErrorKind::IndexError, - Location::Unknown, + vec![SubMessage::only_loc(Location::Unknown)], format!( "[{}] has {} elements, but accessed {}th element", erg_common::fmt_vec(&slf), slf.len(), index ), - None, + line!() as usize, + ErrorKind::IndexError, + Location::Unknown, ) .into()) } @@ -210,11 +224,11 @@ pub fn __dict_getitem__(mut args: ValueArgs, ctx: &Context) -> EvalValueResult EvalValueResult Ok(ValueObj::Nat(start + index)) } else { Err(ErrorCore::new( + vec![SubMessage::only_loc(Location::Unknown)], + format!("Index out of range: {}", index), line!() as usize, ErrorKind::IndexError, Location::Unknown, - format!("Index out of range: {index}"), - None, ) .into()) } diff --git a/compiler/erg_compiler/context/inquire.rs b/compiler/erg_compiler/context/inquire.rs index 9678b208..c81aaa69 100644 --- a/compiler/erg_compiler/context/inquire.rs +++ b/compiler/erg_compiler/context/inquire.rs @@ -4,7 +4,7 @@ use std::path::{Path, PathBuf}; use erg_common::config::Input; use erg_common::env::erg_pystd_path; -use erg_common::error::{ErrorCore, ErrorKind, Location}; +use erg_common::error::{ErrorCore, ErrorKind, Location, SubMessage}; use erg_common::levenshtein::get_similar_name; use erg_common::set::Set; use erg_common::traits::{Locational, Stream}; @@ -756,13 +756,21 @@ impl Context { TyCheckErrors::new( errs.into_iter() .map(|e| { - // HACK: dname.loc()はダミーLocationしか返さないので、エラーならop.loc()で上書きする + let mut sub_msges = Vec::new(); + for sub_msg in e.core.sub_messages { + sub_msges.push(SubMessage::ambiguous_new( + // HACK: dname.loc()はダミーLocationしか返さないので、エラーならop.loc()で上書きする + bin.loc(), + vec![], + sub_msg.get_hint(), + )); + } let core = ErrorCore::new( + sub_msges, + e.core.main_message, e.core.errno, e.core.kind, - bin.loc(), - e.core.desc, - e.core.hint, + e.core.loc, ); TyCheckError::new(core, self.cfg.input.clone(), e.caused_by) }) @@ -797,12 +805,20 @@ impl Context { TyCheckErrors::new( errs.into_iter() .map(|e| { + let mut sub_msges = Vec::new(); + for sub_msg in e.core.sub_messages { + sub_msges.push(SubMessage::ambiguous_new( + unary.loc(), + vec![], + sub_msg.get_hint(), + )); + } let core = ErrorCore::new( + sub_msges, + e.core.main_message, e.core.errno, e.core.kind, - unary.loc(), - e.core.desc, - e.core.hint, + e.core.loc, ); TyCheckError::new(core, self.cfg.input.clone(), e.caused_by) }) @@ -1052,7 +1068,8 @@ impl Context { TyCheckError::type_mismatch_error( self.cfg.input.clone(), line!() as usize, - e.core.loc, + // TODO: Is it possible to get 0? + e.core.sub_messages.get(0).unwrap().loc, e.caused_by, &name[..], Some(nth), @@ -1107,7 +1124,8 @@ impl Context { TyCheckError::type_mismatch_error( self.cfg.input.clone(), line!() as usize, - e.core.loc, + // TODO: Is it possible to get 0? + e.core.sub_messages.get(0).unwrap().loc, e.caused_by, &name[..], Some(nth), @@ -1164,7 +1182,8 @@ impl Context { TyCheckError::type_mismatch_error( self.cfg.input.clone(), line!() as usize, - e.core.loc, + // TODO: Is it possible to get 0? + e.core.sub_messages.get(0).unwrap().loc, e.caused_by, &name[..], Some(nth), diff --git a/compiler/erg_compiler/context/register.rs b/compiler/erg_compiler/context/register.rs index 3560240e..6841f7b4 100644 --- a/compiler/erg_compiler/context/register.rs +++ b/compiler/erg_compiler/context/register.rs @@ -416,7 +416,8 @@ impl Context { TyCheckError::return_type_error( self.cfg.input.clone(), line!() as usize, - e.core.loc, + // TODO: is it possible to get 0? + e.core.sub_messages.get(0).unwrap().loc, e.caused_by, readable_name(name.inspect()), spec_ret_t, diff --git a/compiler/erg_compiler/error.rs b/compiler/erg_compiler/error.rs index cf838118..59548271 100644 --- a/compiler/erg_compiler/error.rs +++ b/compiler/erg_compiler/error.rs @@ -2,9 +2,11 @@ use std::fmt; use std::fmt::Display; use erg_common::config::Input; -use erg_common::error::{ErrorCore, ErrorDisplay, ErrorKind::*, Location, MultiErrorDisplay}; +use erg_common::error::{ + ErrorCore, ErrorDisplay, ErrorKind::*, Location, MultiErrorDisplay, SubMessage, +}; use erg_common::set::Set; -use erg_common::style::{Attribute, Color, StyledStr, StyledString, Theme, THEME}; +use erg_common::style::{Attribute, Color, StyledStr, StyledString, StyledStrings, Theme, THEME}; use erg_common::traits::{Locational, Stream}; use erg_common::vis::Visibility; use erg_common::{ @@ -122,7 +124,7 @@ impl From for CompileError { Self { core: Box::new(err.core), input: err.input, - caused_by: "".into(), + caused_by: "".to_owned(), theme: THEME, } } @@ -135,9 +137,6 @@ impl ErrorDisplay for CompileError { fn input(&self) -> &Input { &self.input } - fn theme(&self) -> &Theme { - &self.theme - } fn caused_by(&self) -> &str { &self.caused_by } @@ -146,10 +145,25 @@ impl ErrorDisplay for CompileError { } } +// found, error +const ERR: Color = THEME.colors.error; +// var name, lhs, rhs +const WARN: Color = THEME.colors.warning; +// expect, hint +const HINT: Color = THEME.colors.hint; +// url +const ACCENT: Color = THEME.colors.accent; +// url and feature = pretty +const UNDERLINE: Attribute = Attribute::Underline; +#[cfg(not(feature = "pretty"))] +const ATTR: Attribute = Attribute::Bold; +#[cfg(feature = "pretty")] +const ATTR: Attribute = Attribute::Underline; + const URL: StyledStr = StyledStr::new( "https://github.com/erg-lang/erg", - Some(Color::White), - Some(Attribute::Underline), + Some(ACCENT), + Some(UNDERLINE), ); impl CompileError { @@ -171,19 +185,19 @@ impl CompileError { ) -> Self { Self::new( ErrorCore::new( - errno, - CompilerSystemError, - loc, + vec![SubMessage::only_loc(loc)], switch_lang!( "japanese" => format!("これはErg compilerのバグです、開発者に報告して下さい ({URL})\n\n{fn_name}:{line}より発生"), "simplified_chinese" => format!("这是Erg编译器的错误,请报告给{URL}\n\n原因来自: {fn_name}:{line}"), "traditional_chinese" => format!("這是Erg編譯器的錯誤,請報告給{URL}\n\n原因來自: {fn_name}:{line}"), "english" => format!("this is a bug of the Erg compiler, please report it to {URL}\n\ncaused from: {fn_name}:{line}"), ), - None, + errno, + CompilerSystemError, + loc, ), input, - "".into(), + "".to_owned(), ) } @@ -196,43 +210,47 @@ impl CompileError { ) -> Self { Self::new( ErrorCore::new( + vec![SubMessage::only_loc(loc)], + switch_lang!( + "japanese" => format!("\ +スタックの要素数が異常です (要素数: {stack_len}, ブロックID: {block_id}) +これはコンパイラのバグです、開発者に報告して下さい ({URL}) +{fn_name}より発生"), + "simplified_chinese" => format!("\ +堆栈中的元素数无效(元素数: {stack_len},块id: {block_id}) +这是 Erg 编译器的一个错误,请报告它 ({URL}) +起因于: {fn_name}"), + "traditional_chinese" => format!("\ +堆棧中的元素數無效(元素數: {stack_len},塊id: {block_id})\n +這是 Erg 編譯器的一個錯誤,請報告它 ({URL}) +起因於: {fn_name}"), + "english" => format!("\ +the number of elements in the stack is invalid (num of elems: {stack_len}, block id: {block_id})\n +this is a bug of the Erg compiler, please report it to {URL} +caused from: {fn_name}"), + ), 0, CompilerSystemError, loc, - switch_lang!( - "japanese" => format!("スタックの要素数が異常です (要素数: {stack_len}, ブロックID: {block_id})\n\ - これはコンパイラのバグです、開発者に報告して下さい ({URL})\n\ - {fn_name}より発生"), - "simplified_chinese" => format!("堆栈中的元素数无效(元素数: {stack_len},块id: {block_id})\n\ - 这是 Erg 编译器的一个错误,请报告它 ({URL})\n\ - 起因于: {fn_name}"), - "traditional_chinese" => format!("堆棧中的元素數無效(元素數: {stack_len},塊id: {block_id})\n\ - 這是 Erg 編譯器的一個錯誤,請報告它 ({URL})\n\ - 起因於: {fn_name}"), - "english" => format!("the number of elements in the stack is invalid (num of elems: {stack_len}, block id: {block_id})\n\ - this is a bug of the Erg compiler, please report it ({URL})\n\ - caused from: {fn_name}"), - ), - None, ), input, - "".into(), + "".to_owned(), ) } pub fn feature_error(input: Input, loc: Location, name: &str, caused_by: String) -> Self { Self::new( ErrorCore::new( - 0, - FeatureError, - loc, + vec![SubMessage::only_loc(loc)], switch_lang!( "japanese" => format!("この機能({name})はまだ正式に提供されていません"), "simplified_chinese" => format!("此功能({name})尚未实现"), "traditional_chinese" => format!("此功能({name})尚未實現"), "english" => format!("this feature({name}) is not implemented yet"), ), - None, + 0, + FeatureError, + loc, ), input, caused_by, @@ -242,36 +260,32 @@ impl CompileError { pub fn system_exit() -> Self { Self::new( ErrorCore::new( - 0, - SystemExit, - Location::Unknown, + vec![SubMessage::only_loc(Location::Unknown)], switch_lang!( "japanese" => "システムを終了します", "simplified_chinese" => "系统正在退出", "traditional_chinese" => "系統正在退出", "english" => "system is exiting", ), - None, + 0, + SystemExit, + Location::Unknown, ), Input::Dummy, - "".into(), + "".to_owned(), ) } } pub type TyCheckError = CompileError; -const ERR: Color = THEME.colors.error; -const WARNING: Color = THEME.colors.warning; -const HINT: Color = THEME.colors.hint; - impl TyCheckError { pub fn dummy(input: Input, errno: usize) -> Self { - Self::new(ErrorCore::dummy(errno), input, "".into()) + Self::new(ErrorCore::dummy(errno), input, "".to_string()) } pub fn unreachable(input: Input, fn_name: &str, line: u32) -> Self { - Self::new(ErrorCore::unreachable(fn_name, line), input, "".into()) + Self::new(ErrorCore::unreachable(fn_name, line), input, "".to_string()) } pub fn checker_bug( @@ -283,19 +297,19 @@ impl TyCheckError { ) -> Self { Self::new( ErrorCore::new( - errno, - CompilerSystemError, - loc, + vec![SubMessage::only_loc(loc)], switch_lang!( "japanese" => format!("これはErg compilerのバグです、開発者に報告して下さい ({URL})\n\n{fn_name}:{line}より発生"), "simplified_chinese" => format!("这是Erg编译器的错误,请报告给{URL}\n\n原因来自: {fn_name}:{line}"), "traditional_chinese" => format!("這是Erg編譯器的錯誤,請報告給{URL}\n\n原因來自: {fn_name}:{line}"), "english" => format!("this is a bug of the Erg compiler, please report it to {URL}\n\ncaused from: {fn_name}:{line}"), ), - None, + errno, + CompilerSystemError, + loc, ), input, - "".into(), + "".to_string(), ) } @@ -309,16 +323,16 @@ impl TyCheckError { let name = readable_name(name); Self::new( ErrorCore::new( - errno, - TypeError, - loc, + vec![SubMessage::only_loc(loc)], switch_lang!( "japanese" => format!("{name}の型が指定されていません"), "simplified_chinese" => format!("{name}的类型未指定"), "traditional_chinese" => format!("{name}的類型未指定"), "english" => format!("the type of {name} is not specified"), ), - None, + errno, + TypeError, + loc, ), input, caused_by, @@ -335,9 +349,7 @@ impl TyCheckError { let param_ts = fmt_iter(param_ts); Self::new( ErrorCore::new( - errno, - NotImplementedError, - callee.loc(), + vec![SubMessage::only_loc(callee.loc())], switch_lang!( "japanese" => format!( "{callee}は{param_ts}を引数に取る呼び出し可能オブジェクトではありません" @@ -348,7 +360,9 @@ impl TyCheckError { "{callee} is not a Callable object that takes {param_ts} as an argument" ), ), - None, + errno, + NotImplementedError, + callee.loc(), ), input, caused_by, @@ -375,27 +389,42 @@ impl TyCheckError { "traditional_chinese" => format!("(第{pos}個參數)"), "english" => format!(" (the {} argument)", ordinal_num(pos)), ), - None => "".into(), + None => "".to_owned(), }; - let name = StyledString::new( - &format!("{}{}", name, ord), - Some(WARNING), - Some(Attribute::Bold), + let name = StyledString::new(format!("{}{}", name, ord), Some(WARN), Some(ATTR)); + let mut expct = StyledStrings::default(); + switch_lang!( + "japanese" => expct.push_str("予期した型: "), + "simplified_chinese" =>expct.push_str("预期: "), + "traditional_chinese" => expct.push_str("預期: "), + "english" => expct.push_str("expected: "), ); - let expect = StyledString::new(&format!("{}", expect), Some(HINT), Some(Attribute::Bold)); - let found = StyledString::new(&format!("{}", found), Some(ERR), Some(Attribute::Bold)); + expct.push_str_with_color_and_attribute(format!("{}", expect), HINT, ATTR); + + let mut fnd = StyledStrings::default(); + switch_lang!( + "japanese" => fnd.push_str("与えられた型: "), + "simplified_chinese" => fnd.push_str("但找到: "), + "traditional_chinese" => fnd.push_str("但找到: "), + "english" =>fnd.push_str("but found: "), + ); + fnd.push_str_with_color_and_attribute(format!("{}", found), ERR, ATTR); Self::new( ErrorCore::new( + vec![SubMessage::ambiguous_new( + loc, + vec![expct.to_string(), fnd.to_string()], + hint, + )], + switch_lang!( + "japanese" => format!("{name}の型が違います{}", fmt_option_map!(pre "\n与えられた型の単一化候補: ", candidates, |x: &Set| x.folded_display())), + "simplified_chinese" => format!("{name}的类型不匹配{}", fmt_option_map!(pre "\n某一类型的统一候选: ", candidates, |x: &Set| x.folded_display())), + "traditional_chinese" => format!("{name}的類型不匹配{}", fmt_option_map!(pre "\n某一類型的統一候選: ", candidates, |x: &Set| x.folded_display())), + "english" => format!("the type of {name} is mismatched{}", fmt_option_map!(pre "\nunification candidates of a given type: ", candidates, |x: &Set| x.folded_display())), + ), errno, TypeError, loc, - switch_lang!( - "japanese" => format!("{name}の型が違います\n\n予期した型: {expect}\n与えられた型: {found}{}", fmt_option_map!(pre "\n与えられた型の単一化候補: ", candidates, |x: &Set| x.folded_display())), - "simplified_chinese" => format!("{name}的类型不匹配\n\n预期: {expect}\n但找到: {found}{}", fmt_option_map!(pre "\n某一类型的统一候选: ", candidates, |x: &Set| x.folded_display())), - "traditional_chinese" => format!("{name}的類型不匹配\n\n預期: {expect}\n但找到: {found}{}", fmt_option_map!(pre "\n某一類型的統一候選: ", candidates, |x: &Set| x.folded_display())), - "english" => format!("the type of {name} is mismatched\n\nexpected: {expect}\nbut found: {found}{}", fmt_option_map!(pre "\nunification candidates of a given type: ", candidates, |x: &Set| x.folded_display())), - ), - hint, ), input, caused_by, @@ -411,20 +440,40 @@ impl TyCheckError { expect: &Type, found: &Type, ) -> Self { - let expect = StyledString::new(&format!("{}", expect), Some(HINT), Some(Attribute::Bold)); - let found = StyledString::new(&format!("{}", found), Some(ERR), Some(Attribute::Bold)); + let mut expct = StyledStrings::default(); + switch_lang!( + "japanese" => expct.push_str("予期した型: "), + "simplified_chinese" =>expct.push_str("预期: "), + "traditional_chinese" => expct.push_str("預期: "), + "english" => expct.push_str("expected: "), + ); + expct.push_str_with_color_and_attribute(format!("{}", expect), HINT, ATTR); + + let mut fnd = StyledStrings::default(); + switch_lang!( + "japanese" => fnd.push_str("与えられた型: "), + "simplified_chinese" => fnd.push_str("但找到: "), + "traditional_chinese" => fnd.push_str("但找到: "), + "english" =>fnd.push_str("but found: "), + ); + fnd.push_str_with_color_and_attribute(format!("{}", found), ERR, ATTR); + Self::new( ErrorCore::new( + vec![SubMessage::ambiguous_new( + loc, + vec![expct.to_string(), fnd.to_string()], + None, + )], + switch_lang!( + "japanese" => format!("{name}の戻り値の型が違います"), + "simplified_chinese" => format!("{name}的返回类型不匹配"), + "traditional_chinese" => format!("{name}的返回類型不匹配"), + "english" => format!("the return type of {name} is mismatched"), + ), errno, TypeError, loc, - switch_lang!( - "japanese" => format!("{name}の戻り値の型が違います\n\n予期した型: {expect}\n与えられた型: {found}"), - "simplified_chinese" => format!("{name}的返回类型不匹配\n\n预期: {expect}\n但找到: {found}"), - "traditional_chinese" => format!("{name}的返回類型不匹配\n\n預期: {expect}\n但找到: {found}"), - "english" => format!("the return type of {name} is mismatched\n\nexpected: {expect}\nbut found: {found}"), - ), - None, ), input, caused_by, @@ -441,16 +490,16 @@ impl TyCheckError { ) -> Self { Self::new( ErrorCore::new( - errno, - NameError, - loc, + vec![SubMessage::only_loc(loc)], switch_lang!( "japanese" => format!("{name}: {t}は宣言されましたが初期化されていません"), "simplified_chinese" => format!("{name}: {t}已声明但未初始化"), "traditional_chinese" => format!("{name}: {t}已宣告但未初始化"), "english" => format!("{name}: {t} is declared but not initialized"), ), - None, + errno, + NameError, + loc, ), input, caused_by, @@ -465,20 +514,40 @@ impl TyCheckError { expect: usize, found: usize, ) -> Self { - let expect = StyledString::new(&format!("{}", expect), Some(HINT), Some(Attribute::Bold)); - let found = StyledString::new(&format!("{}", found), Some(ERR), Some(Attribute::Bold)); + let mut expct = StyledStrings::default(); + switch_lang!( + "japanese" => expct.push_str("予期した個数: "), + "simplified_chinese" =>expct.push_str("预期: "), + "traditional_chinese" => expct.push_str("預期: "), + "english" => expct.push_str("expected: "), + ); + expct.push_str_with_color_and_attribute(format!("{}", expect), HINT, ATTR); + + let mut fnd = StyledStrings::default(); + switch_lang!( + "japanese" => fnd.push_str("与えられた個数: "), + "simplified_chinese" => fnd.push_str("但找到: "), + "traditional_chinese" => fnd.push_str("但找到: "), + "english" =>fnd.push_str("but found: "), + ); + fnd.push_str_with_color_and_attribute(format!("{}", found), ERR, ATTR); + Self::new( ErrorCore::new( + vec![SubMessage::ambiguous_new( + loc, + vec![expct.to_string(), fnd.to_string()], + None, + )], + switch_lang!( + "japanese" => format!("ポジショナル引数の数が違います"), + "simplified_chinese" => format!("正则参数的数量不匹配"), + "traditional_chinese" => format!("正則參數的數量不匹配"), + "english" => format!("the number of positional arguments is mismatched"), + ), errno, TypeError, loc, - switch_lang!( - "japanese" => format!("ポジショナル引数の数が違います\n\n予期した個数: {expect}\n与えられた個数: {found}"), - "simplified_chinese" => format!("正则参数的数量不匹配\n\n预期: {expect}\n但找到: {found}"), - "traditional_chinese" => format!("正則參數的數量不匹配\n\n預期: {expect}\n但找到: {found}"), - "english" => format!("the number of positional arguments is mismatched\n\nexpected: {expect}\nbut found: {found}"), - ), - None, ), input, caused_by, @@ -494,16 +563,16 @@ impl TyCheckError { ) -> Self { Self::new( ErrorCore::new( - errno, - TypeError, - loc, + vec![SubMessage::only_loc(loc)], switch_lang!( "japanese" => format!("{expr_t}型の全パターンを網羅していません"), "simplified_chinese" => format!("并非所有{expr_t}类型的模式都被涵盖"), "traditional_chinese" => format!("並非所有{expr_t}類型的模式都被涵蓋"), "english" => format!("not all patterns of type {expr_t} are covered"), ), - None, + errno, + TypeError, + loc, ), input, caused_by, @@ -519,16 +588,16 @@ impl TyCheckError { ) -> Self { Self::new( ErrorCore::new( - errno, - TypeError, - loc, + vec![SubMessage::only_loc(loc)], switch_lang!( "japanese" => format!("{expr}の型が推論できません"), "simplified_chinese" => format!("无法推断{expr}的类型"), "traditional_chinese" => format!("無法推斷{expr}的類型"), "english" => format!("failed to infer the type of {expr}"), ), - None, + errno, + TypeError, + loc, ), input, caused_by, @@ -536,11 +605,11 @@ impl TyCheckError { } pub fn dummy_infer_error(input: Input, fn_name: &str, line: u32) -> Self { - Self::new(ErrorCore::unreachable(fn_name, line), input, "".into()) + Self::new(ErrorCore::unreachable(fn_name, line), input, "".to_owned()) } pub fn not_relation(input: Input, fn_name: &str, line: u32) -> Self { - Self::new(ErrorCore::unreachable(fn_name, line), input, "".into()) + Self::new(ErrorCore::unreachable(fn_name, line), input, "".to_owned()) } #[allow(clippy::too_many_arguments)] @@ -555,26 +624,12 @@ impl TyCheckError { kw_args_len: usize, ) -> Self { let name = readable_name(callee_name); - let expect = StyledString::new( - &format!("{}", params_len), - Some(HINT), - Some(Attribute::Bold), - ); - let pos_args_len = StyledString::new( - &format!("{}", pos_args_len), - Some(ERR), - Some(Attribute::Bold), - ); - let kw_args_len = StyledString::new( - &format!("{}", kw_args_len), - Some(ERR), - Some(Attribute::Bold), - ); + let expect = StyledString::new(format!("{}", params_len), Some(HINT), Some(ATTR)); + let pos_args_len = StyledString::new(format!("{}", pos_args_len), Some(ERR), Some(ATTR)); + let kw_args_len = StyledString::new(format!("{}", kw_args_len), Some(ERR), Some(ATTR)); Self::new( ErrorCore::new( - errno, - TypeError, - loc, + vec![SubMessage::only_loc(loc)], switch_lang!( "japanese" => format!( "{name}に渡された引数の数が多すぎます @@ -585,9 +640,9 @@ impl TyCheckError { ), "simplified_chinese" => format!("传递给{name}的参数过多 -: {expect} -: {pos_args_len} -: {kw_args_len}" +总的预期参数: {expect} +通过的位置参数: {pos_args_len} +通过了关键字参数: {kw_args_len}" ), "traditional_chinese" => format!("傳遞給{name}的參數過多 @@ -603,7 +658,9 @@ passed positional args: {pos_args_len} passed keyword args: {kw_args_len}" ), ), - None, + errno, + TypeError, + loc, ), input, caused_by, @@ -619,24 +676,20 @@ passed keyword args: {kw_args_len}" missing_len: usize, missing_params: Vec, ) -> Self { - let name = readable_name(callee_name); - let vec_cxt = StyledString::new( - &fmt_vec(&missing_params), - Some(WARNING), - Some(Attribute::Bold), - ); + let name = StyledStr::new(readable_name(callee_name), Some(ACCENT), Some(ATTR)); + let vec_cxt = StyledString::new(&fmt_vec(&missing_params), Some(WARN), Some(ATTR)); Self::new( ErrorCore::new( - errno, - TypeError, - loc, + vec![SubMessage::only_loc(loc)], switch_lang!( "japanese" => format!("{name}に渡された引数が{missing_len}個足りません({vec_cxt})" ), "simplified_chinese" => format!("{name}的{missing_len}个位置参数不被传递({vec_cxt})"), "traditional_chinese" => format!("{name}的{missing_len}個位置參數不被傳遞({vec_cxt})"), "english" => format!("missing {missing_len} positional argument(s) for {name}: {vec_cxt}"), ), - None, + errno, + TypeError, + loc, ), input, caused_by, @@ -651,20 +704,20 @@ passed keyword args: {kw_args_len}" caused_by: String, arg_name: &str, ) -> Self { - let name = readable_name(callee_name); - let found = StyledString::new(arg_name, Some(ERR), Some(Attribute::Bold)); + let name = StyledStr::new(readable_name(callee_name), Some(ACCENT), Some(ATTR)); + let found = StyledString::new(arg_name, Some(ERR), Some(ATTR)); Self::new( ErrorCore::new( - errno, - TypeError, - loc, + vec![SubMessage::only_loc(loc)], switch_lang!( "japanese" => format!("{name}の引数{found}が複数回渡されています"), "simplified_chinese" => format!("{name}的参数{found}被多次传递"), "traditional_chinese" => format!("{name}的參數{found}被多次傳遞"), "english" => format!("{name}'s argument {found} is passed multiple times"), ), - None, + errno, + TypeError, + loc, ), input, caused_by, @@ -679,20 +732,20 @@ passed keyword args: {kw_args_len}" caused_by: String, param_name: &str, ) -> Self { - let name = readable_name(callee_name); - let found = StyledString::new(param_name, Some(ERR), Some(Attribute::Bold)); + let name = StyledStr::new(readable_name(callee_name), Some(ACCENT), Some(ATTR)); + let found = StyledString::new(param_name, Some(ERR), Some(ATTR)); Self::new( ErrorCore::new( - errno, - TypeError, - loc, + vec![SubMessage::only_loc(loc)], switch_lang!( "japanese" => format!("{name}には予期しないキーワード引数{found}が渡されています"), "simplified_chinese" => format!("{name}得到了意外的关键字参数{found}"), "traditional_chinese" => format!("{name}得到了意外的關鍵字參數{found}"), "english" => format!("{name} got unexpected keyword argument {found}"), ), - None, + errno, + TypeError, + loc, ), input, caused_by, @@ -707,20 +760,38 @@ passed keyword args: {kw_args_len}" loc: Location, caused_by: String, ) -> Self { - let lhs_t = StyledString::new(&format!("{}", lhs_t), Some(WARNING), Some(Attribute::Bold)); - let rhs_t = StyledString::new(&format!("{}", rhs_t), Some(WARNING), Some(Attribute::Bold)); + let mut lhs_typ = StyledStrings::default(); + switch_lang!( + "japanese" => lhs_typ.push_str("左辺: "), + "simplified_chinese" => lhs_typ.push_str("左边: "), + "traditional_chinese" => lhs_typ.push_str("左邊: "), + "english" => lhs_typ.push_str("lhs: "), + ); + lhs_typ.push_str_with_color_and_attribute(format!("{}", lhs_t), WARN, ATTR); + let mut rhs_typ = StyledStrings::default(); + switch_lang!( + "japanese" => rhs_typ.push_str("右辺: "), + "simplified_chinese" => rhs_typ.push_str("右边: "), + "traditional_chinese" => rhs_typ.push_str("右邊: "), + "english" => rhs_typ.push_str("rhs: "), + ); + rhs_typ.push_str_with_color_and_attribute(format!("{}", rhs_t), WARN, ATTR); Self::new( ErrorCore::new( + vec![SubMessage::ambiguous_new( + loc, + vec![lhs_typ.to_string(), rhs_typ.to_string()], + None, + )], + switch_lang!( + "japanese" => format!("型の単一化に失敗しました"), + "simplified_chinese" => format!("类型统一失败"), + "traditional_chinese" => format!("類型統一失敗"), + "english" => format!("unification failed"), + ), errno, TypeError, loc, - switch_lang!( - "japanese" => format!("型の単一化に失敗しました\n\n左辺: {lhs_t}\n右辺: {rhs_t}"), - "simplified_chinese" => format!("类型统一失败\n\n左边: {lhs_t}\n右边: {rhs_t}"), - "traditional_chinese" => format!("類型統一失敗\n\n左邊: {lhs_t}\n右邊: {rhs_t}"), - "english" => format!("unification failed\n\nlhs: {lhs_t}\nrhs: {rhs_t}"), - ), - None, ), input, caused_by, @@ -735,20 +806,38 @@ passed keyword args: {kw_args_len}" loc: Location, caused_by: String, ) -> Self { - let lhs_t = StyledString::new(&format!("{}", lhs_t), Some(WARNING), Some(Attribute::Bold)); - let rhs_t = StyledString::new(&format!("{}", rhs_t), Some(WARNING), Some(Attribute::Bold)); + let mut lhs_typ = StyledStrings::default(); + switch_lang!( + "japanese" => lhs_typ.push_str("左辺: "), + "simplified_chinese" => lhs_typ.push_str("左边: "), + "traditional_chinese" => lhs_typ.push_str("左邊: "), + "english" => lhs_typ.push_str("lhs: "), + ); + lhs_typ.push_str_with_color_and_attribute(format!("{}", lhs_t), WARN, ATTR); + let mut rhs_typ = StyledStrings::default(); + switch_lang!( + "japanese" => rhs_typ.push_str("右辺: "), + "simplified_chinese" => rhs_typ.push_str("右边: "), + "traditional_chinese" => rhs_typ.push_str("右邊: "), + "english" => rhs_typ.push_str("rhs: "), + ); + rhs_typ.push_str_with_color_and_attribute(format!("{}", rhs_t), WARN, ATTR); Self::new( ErrorCore::new( + vec![SubMessage::ambiguous_new( + loc, + vec![lhs_typ.to_string(), rhs_typ.to_string()], + None, + )], + switch_lang!( + "japanese" => format!("型の再単一化に失敗しました"), + "simplified_chinese" => format!("重新统一类型失败"), + "traditional_chinese" => format!("重新統一類型失敗"), + "english" => format!("re-unification failed"), + ), errno, TypeError, loc, - switch_lang!( - "japanese" => format!("型の再単一化に失敗しました\n\n左辺: {lhs_t}\n右辺: {rhs_t}"), - "simplified_chinese" => format!("重新统一类型失败\n\n左边: {lhs_t}\n右边: {rhs_t}"), - "traditional_chinese" => format!("重新統一類型失敗\n\n左邊: {lhs_t}\n右邊: {rhs_t}"), - "english" => format!("re-unification failed\n\nlhs: {lhs_t}\nrhs: {rhs_t}"), - ), - None, ), input, caused_by, @@ -763,20 +852,40 @@ passed keyword args: {kw_args_len}" loc: Location, caused_by: String, ) -> Self { - let sub_t = StyledString::new(&format!("{}", sub_t), Some(WARNING), Some(Attribute::Bold)); - let sup_t = StyledString::new(&format!("{}", sup_t), Some(WARNING), Some(Attribute::Bold)); + let mut sub_type = StyledStrings::default(); + switch_lang!( + "japanese" => sub_type.push_str("部分型: "), + "simplified_chinese" => sub_type.push_str("超类型: "), + "simplified_chinese" =>sub_type.push_str("超類型: "), + "english" => sub_type.push_str("subtype: "), + ); + sub_type.push_str_with_color_and_attribute(format!("{}", sub_t), HINT, ATTR); + + let mut sup_type = StyledStrings::default(); + switch_lang!( + "japanese" => sup_type.push_str("汎化型: "), + "simplified_chinese" => sup_type.push_str("超类型: "), + "simplified_chinese" => sup_type.push_str("超類型: "), + "english" =>sup_type.push_str("supertype: "), + ); + sup_type.push_str_with_color_and_attribute(format!("{}", sup_t), ERR, ATTR); + Self::new( ErrorCore::new( + vec![SubMessage::ambiguous_new( + loc, + vec![sub_type.to_string(), sup_type.to_string()], + None, + )], + switch_lang!( + "japanese" => format!("この式の部分型制約を満たせません"), + "simplified_chinese" => format!("无法满足此表达式中的子类型约束"), + "traditional_chinese" => format!("無法滿足此表達式中的子類型約束"), + "english" => format!("the subtype constraint in this expression cannot be satisfied"), + ), errno, TypeError, loc, - switch_lang!( - "japanese" => format!("この式の部分型制約を満たせません\n\nサブタイプ: {sub_t}\nスーパータイプ: {sup_t}"), - "simplified_chinese" => format!("无法满足此表达式中的子类型约束\n\n子类型: {sub_t}\n超类型: {sup_t}"), - "traditional_chinese" => format!("無法滿足此表達式中的子類型約束\n\n子類型: {sub_t}\n超類型: {sup_t}"), - "english" => format!("the subtype constraint in this expression cannot be satisfied:\nsubtype: {sub_t}\nsupertype: {sup_t}"), - ), - None, ), input, caused_by, @@ -790,20 +899,38 @@ passed keyword args: {kw_args_len}" rhs: &Predicate, caused_by: String, ) -> Self { - let lhs = StyledString::new(&format!("{}", lhs), Some(WARNING), Some(Attribute::Bold)); - let rhs = StyledString::new(&format!("{}", rhs), Some(WARNING), Some(Attribute::Bold)); + let mut lhs_uni = StyledStrings::default(); + switch_lang!( + "japanese" => lhs_uni.push_str("左辺: "), + "simplified_chinese" => lhs_uni.push_str("左边: "), + "traditional_chinese" => lhs_uni.push_str("左邊: "), + "english" => lhs_uni.push_str("lhs: "), + ); + lhs_uni.push_str_with_color_and_attribute(format!("{}", lhs), HINT, ATTR); + let mut rhs_uni = StyledStrings::default(); + switch_lang!( + "japanese" => rhs_uni.push_str("右辺: "), + "simplified_chinese" => rhs_uni.push_str("右边: "), + "traditional_chinese" => rhs_uni.push_str("右邊: "), + "english" => rhs_uni.push_str("rhs: "), + ); + rhs_uni.push_str_with_color_and_attribute(format!("{}", rhs), ERR, ATTR); Self::new( ErrorCore::new( + vec![SubMessage::ambiguous_new( + Location::Unknown, + vec![lhs_uni.to_string(), rhs_uni.to_string()], + None, + )], + switch_lang!( + "japanese" => format!("述語式の単一化に失敗しました"), + "simplified_chinese" => format!("无法统一谓词表达式"), + "traditional_chinese" => format!("無法統一謂詞表達式"), + "english" => format!("predicate unification failed"), + ), errno, TypeError, Location::Unknown, - switch_lang!( - "japanese" => format!("述語式の単一化に失敗しました\n\n左辺: {lhs}\n右辺: {rhs}"), - "simplified_chinese" => format!("无法统一谓词表达式\n\n左边: {lhs}\n左边: {rhs}"), - "traditional_chinese" => format!("無法統一謂詞表達式\n\n左邊: {lhs}\n左邊: {rhs}"), - "english" => format!("predicate unification failed\n\nlhs: {lhs}\nrhs: {rhs}"), - ), - None, ), input, caused_by, @@ -820,16 +947,16 @@ passed keyword args: {kw_args_len}" ) -> Self { Self::new( ErrorCore::new( - errno, - TypeError, - loc, + vec![SubMessage::ambiguous_new(loc, vec![], hint)], switch_lang!( "japanese" => format!("{proj}の候補がありません"), "simplified_chinese" => format!("{proj}没有候选项"), "traditional_chinese" => format!("{proj}沒有候選項"), "english" => format!("no candidate for {proj}"), ), - hint, + errno, + TypeError, + loc, ), input, caused_by, @@ -847,16 +974,16 @@ passed keyword args: {kw_args_len}" ) -> Self { Self::new( ErrorCore::new( - errno, - TypeError, - loc, + vec![SubMessage::ambiguous_new(loc, vec![], hint)], switch_lang!( "japanese" => format!("{class}は{trait_}を実装していません"), "simplified_chinese" => format!("{class}没有实现{trait_}"), "traditional_chinese" => format!("{class}沒有實現{trait_}"), "english" => format!("{class} does not implement {trait_}"), ), - hint, + errno, + TypeError, + loc, ), input, caused_by, @@ -871,12 +998,10 @@ passed keyword args: {kw_args_len}" name: &str, hint: Option, ) -> Self { - let found = StyledString::new(name, Some(ERR), Some(Attribute::Bold)); + let found = StyledString::new(name, Some(ERR), Some(ATTR)); Self::new( ErrorCore::new( - errno, - MethodError, - loc, + vec![SubMessage::ambiguous_new(loc, vec![], hint)], switch_lang!( "japanese" => format!( "{found}にメソッドを定義することはできません", @@ -891,7 +1016,9 @@ passed keyword args: {kw_args_len}" "cannot define methods for {found}", ), ), - hint, + errno, + MethodError, + loc, ), input, caused_by, @@ -910,21 +1037,51 @@ passed keyword args: {kw_args_len}" found: &Type, hint: Option, ) -> Self { - let member_name = StyledString::new(member_name, Some(WARNING), Some(Attribute::Bold)); - let expect = StyledString::new(&format!("{}", expect), Some(HINT), Some(Attribute::Bold)); - let found = StyledString::new(&format!("{}", found), Some(ERR), Some(Attribute::Bold)); + let mut expct = StyledStrings::default(); + switch_lang!( + "japanese" => { + expct.push_str_with_color_and_attribute(format!("{}", trait_type), ACCENT, ATTR); + expct.push_str("で宣言された型: "); + }, + "simplified_chinese" => { + expct.push_str_with_color_and_attribute(format!("{}", trait_type), ACCENT, ATTR); + expct.push_str("中声明的类型: "); + }, + "traditional_chinese" => { + expct.push_str_with_color_and_attribute(format!("{}", trait_type), ACCENT, ATTR); + expct.push_str("中聲明的類型: "); + }, + "english" => { + expct.push_str("declared in "); + expct.push_str_with_color(format!("{}: ", trait_type), ACCENT); + }, + ); + expct.push_str_with_color(format!("{}", expect), HINT); + let mut fnd = StyledStrings::default(); + switch_lang!( + "japanese" => fnd.push_str("与えられた型: "), + "simplified_chinese" => fnd.push_str("但找到: "), + "traditional_chinese" => fnd.push_str("但找到: "), + "english" => fnd.push_str("but found: "), + ); + fnd.push_str_with_color(format!("{}", found), ERR); + let member_name = StyledStr::new(member_name, Some(ACCENT), Some(ATTR)); Self::new( ErrorCore::new( + vec![SubMessage::ambiguous_new( + loc, + vec![expct.to_string(), fnd.to_string()], + hint, + )], + switch_lang!( + "japanese" => format!("{member_name}の型が違います"), + "simplified_chinese" => format!("{member_name}的类型不匹配"), + "traditional_chinese" => format!("{member_name}的類型不匹配"), + "english" => format!("the type of {member_name} is mismatched"), + ), errno, TypeError, loc, - switch_lang!( - "japanese" => format!("{member_name}の型が違います\n\n{trait_type}で宣言された型: {expect}\n与えられた型: {found}"), - "simplified_chinese" => format!("{member_name}的类型不匹配\n\n在{trait_type}中声明的类型: {expect}\n但找到: {found}"), - "traditional_chinese" => format!("{member_name}的類型不匹配\n\n在{trait_type}中聲明的類型: {expect}\n但找到: {found}"), - "english" => format!("the type of {member_name} is mismatched\n\ndeclared in {trait_type}: {expect}\nbut found: {found}"), - ), - hint, ), input, caused_by, @@ -941,19 +1098,19 @@ passed keyword args: {kw_args_len}" class_type: &Type, hint: Option, ) -> Self { - let member_name = StyledString::new(member_name, Some(WARNING), Some(Attribute::Bold)); + let member_name = StyledString::new(member_name, Some(WARN), Some(ATTR)); Self::new( ErrorCore::new( - errno, - TypeError, - Location::Unknown, + vec![SubMessage::ambiguous_new(Location::Unknown, vec![], hint)], switch_lang!( "japanese" => format!("{trait_type}の{member_name}が{class_type}で実装されていません"), "simplified_chinese" => format!("{trait_type}中的{member_name}没有在{class_type}中实现"), "traditional_chinese" => format!("{trait_type}中的{member_name}沒有在{class_type}中實現"), "english" => format!("{member_name} of {trait_type} is not implemented in {class_type}"), ), - hint, + errno, + TypeError, + Location::Unknown, ), input, caused_by, @@ -970,19 +1127,19 @@ passed keyword args: {kw_args_len}" class_type: &Type, hint: Option, ) -> Self { - let member_name = StyledString::new(member_name, Some(WARNING), Some(Attribute::Bold)); + let member_name = StyledString::new(member_name, Some(WARN), Some(ATTR)); Self::new( ErrorCore::new( - errno, - TypeError, - Location::Unknown, + vec![SubMessage::ambiguous_new(Location::Unknown, vec![], hint)], switch_lang!( "japanese" => format!("{class_type}の{member_name}は{trait_type}で宣言されていません"), "simplified_chinese" => format!("{class_type}中的{member_name}没有在{trait_type}中声明"), "traditional_chinese" => format!("{class_type}中的{member_name}沒有在{trait_type}中聲明"), "english" => format!("{member_name} of {class_type} is not declared in {trait_type}"), ), - hint, + errno, + TypeError, + Location::Unknown, ), input, caused_by, @@ -996,19 +1153,19 @@ passed keyword args: {kw_args_len}" loc: Location, caused_by: String, ) -> Self { - let found = StyledString::new(name, Some(ERR), Some(Attribute::Bold)); + let found = StyledString::new(name, Some(ERR), Some(ATTR)); Self::new( ErrorCore::new( - errno, - TypeError, - loc, + vec![SubMessage::only_loc(loc)], switch_lang!( "japanese" => format!("型変数{found}が定義されていません"), "simplified_chinese" => format!("类型变量{found}没有定义"), "traditional_chinese" => format!("類型變量{found}沒有定義"), "english" => format!("type variable {found} is not defined"), ), - None, + errno, + TypeError, + loc, ), input, caused_by, @@ -1022,25 +1179,73 @@ passed keyword args: {kw_args_len}" candidates: &[Type], caused_by: String, ) -> Self { + let hint = Some( + switch_lang!( + "japanese" => { + let mut s = StyledStrings::default(); + s.push_str("多相関数の場合は"); + s.push_str_with_color_and_attribute("f|T := Int|", ACCENT, ATTR); + s.push_str(", \n型属性の場合は"); + s.push_str_with_color_and_attribute("f|T := Trait|.X", ACCENT, ATTR); + s + }, + "simplified_chinese" => { + let mut s = StyledStrings::default(); + s.push_str("如果是多态函数,请使用"); + s.push_str_with_color_and_attribute("f|T := Int|", ACCENT, ATTR); + s.push_str(",\n如果是类型属性,请使用"); + s.push_str_with_color_and_attribute("f|T := Trait|.X", ACCENT, ATTR); + s + }, + "traditional_chinese" => { + let mut s = StyledStrings::default(); + s.push_str("如果是多型函數,請使用"); + s.push_str_with_color_and_attribute("f|T := Int|", ACCENT, ATTR); + s.push_str(",\n如果是類型屬性,請使用"); + s.push_str_with_color_and_attribute("f|T := Trait|.X", ACCENT, ATTR); + s + }, + "english" => { + let mut s = StyledStrings::default(); + s.push_str("if it is a polymorphic function, like "); + s.push_str_with_color_and_attribute("f|T := Int|", ACCENT, ATTR); + s.push_str("\nif it is a type attribute, like "); + s.push_str_with_color_and_attribute("f|T := Trait|.X ", ACCENT, ATTR); + s + }, + ) + .to_string(), + ); + let sub_msg = switch_lang!( + "japanese" => "型を指定してください", + "simplified_chinese" => "方式指定类型", + "traditional_chinese" => "specify the type", + "english" => "specify the type", + ); + let mut candidate = StyledStrings::default(); + switch_lang!( + "japanese" => candidate.push_str("候補: "), + "simplified_chinese" => candidate.push_str("候选: "), + "traditional_chinese" => candidate.push_str("候選: "), + "english" => candidate.push_str("candidates: "), + ); + candidate.push_str_with_color_and_attribute(&fmt_vec(candidates), WARN, ATTR); Self::new( ErrorCore::new( + vec![SubMessage::ambiguous_new( + expr.loc(), + vec![sub_msg.to_string(), candidate.to_string()], + hint, + )], + switch_lang!( + "japanese" => format!("{expr}の型を一意に決定できませんでした"), + "simplified_chinese" => format!("无法确定{expr}的类型"), + "traditional_chinese" => format!("無法確定{expr}的類型"), + "english" => format!("cannot determine the type of {expr}"), + ), errno, TypeError, expr.loc(), - switch_lang!( - "japanese" => format!("{expr}の型を一意に決定できませんでした\n\n候補: {}", fmt_vec(candidates)), - "simplified_chinese" => format!("无法确定{expr}的类型\n\n候选: {}", fmt_vec(candidates)), - "traditional_chinese" => format!("無法確定{expr}的類型\n\n候選: {}", fmt_vec(candidates)), - "english" => format!("cannot determine the type of {expr}\n\ncandidates: {}", fmt_vec(candidates)), - ), - Some( - switch_lang!( - "japanese" => "多相関数の場合は`f|T := Int|`, 型属性の場合は`T|T <: Trait|.X`などのようにして型を指定してください", - "simplified_chinese" => "如果是多态函数,请使用`f|T := Int|`,如果是类型属性,请使用`T|T <: Trait|.X`等方式指定类型", - "traditional_chinese" => "如果是多型函數,請使用`f|T := Int|`,如果是類型屬性,請使用`T|T <: Trait|.X`等方式指定類型", - "english" => "if it is a polymorphic function, use `f|T := Int|`, or if it is a type attribute, use `T|T <: Trait|.X` etc. to specify the type", - ).into(), - ), ), input, caused_by, @@ -1063,16 +1268,16 @@ impl EvalError { pub fn not_const_expr(input: Input, errno: usize, loc: Location, caused_by: String) -> Self { Self::new( ErrorCore::new( - errno, - NotConstExpr, - loc, + vec![SubMessage::only_loc(loc)], switch_lang!( "japanese" => "定数式ではありません", "simplified_chinese" => "不是常量表达式", "traditional_chinese" => "不是常量表達式", "english" => "not a constant expression", ), - None, + errno, + NotConstExpr, + loc, ), input, caused_by, @@ -1082,16 +1287,16 @@ impl EvalError { pub fn invalid_literal(input: Input, errno: usize, loc: Location, caused_by: String) -> Self { Self::new( ErrorCore::new( - errno, - SyntaxError, - loc, + vec![SubMessage::only_loc(loc)], switch_lang!( "japanese" => "リテラルが不正です", "simplified_chinese" => "字面量不合法", "traditional_chinese" => "字面量不合法", "english" => "invalid literal", ), - None, + errno, + SyntaxError, + loc, ), input, caused_by, @@ -1103,59 +1308,78 @@ pub type EffectError = TyCheckError; pub type EffectErrors = TyCheckErrors; impl EffectError { - pub fn has_effect>( - input: Input, - errno: usize, - expr: &Expr, - caused_by: S, - ) -> Self { + pub fn has_effect(input: Input, errno: usize, expr: &Expr, caused_by: String) -> Self { Self::new( ErrorCore::new( - errno, - HasEffect, - expr.loc(), + vec![SubMessage::only_loc(expr.loc())], switch_lang!( "japanese" => "この式には副作用があります", "simplified_chinese" => "此表达式会产生副作用", "traditional_chinese" => "此表達式會產生副作用", "english" => "this expression causes a side-effect", ), - None, + errno, + HasEffect, + expr.loc(), ), input, - caused_by.into(), + caused_by, ) } - pub fn proc_assign_error>( + pub fn proc_assign_error( input: Input, errno: usize, sig: &Signature, - caused_by: S, + caused_by: String, ) -> Self { + let hint = Some( + switch_lang!( + "japanese" => { + let mut s = StyledStrings::default(); + s.push_str("変数の末尾に"); + s.push_str_with_color_and_attribute("!", ACCENT, ATTR); + s.push_str("をつけてください"); + s + }, + "simplified_chinese" => { + let mut s = StyledStrings::default(); + s.push_str("请在变量名后加上"); + s.push_str_with_color_and_attribute("!", ACCENT, ATTR); + s + }, + "traditional_chinese" => { + let mut s = StyledStrings::default(); + s.push_str("請在變量名後加上"); + s.push_str_with_color_and_attribute("!", ACCENT, ATTR); + s + }, + "english" => { + + let mut s = StyledStrings::default(); + s.push_str("add "); + s.push_str_with_color_and_attribute("!", ACCENT, ATTR); + s.push_str(" to the end of the variable name"); + s + }, + ) + .to_string(), + ); Self::new( ErrorCore::new( - errno, - HasEffect, - sig.loc(), + vec![SubMessage::ambiguous_new(sig.loc(), vec![], hint)], switch_lang!( "japanese" => "プロシージャを通常の変数に代入することはできません", "simplified_chinese" => "不能将过程赋值给普通变量", "traditional_chinese" => "不能將過程賦值給普通變量", "english" => "cannot assign a procedure to a normal variable", ), - Some( - switch_lang!( - "japanese" => "変数の末尾に`!`をつけてください", - "simplified_chinese" => "请在变量名后加上`!`", - "traditional_chinese" => "請在變量名後加上`!`", - "english" => "add `!` to the end of the variable name", - ) - .into(), - ), + errno, + HasEffect, + sig.loc(), ), input, - caused_by.into(), + caused_by, ) } } @@ -1164,20 +1388,18 @@ pub type OwnershipError = TyCheckError; pub type OwnershipErrors = TyCheckErrors; impl OwnershipError { - pub fn move_error>( + pub fn move_error( input: Input, errno: usize, name: &str, name_loc: Location, moved_loc: Location, - caused_by: S, + caused_by: String, ) -> Self { - let found = StyledString::new(name, Some(ERR), Some(Attribute::Bold)); + let found = StyledString::new(name, Some(ERR), Some(ATTR)); Self::new( ErrorCore::new( - errno, - MoveError, - name_loc, + vec![SubMessage::only_loc(name_loc)], switch_lang!( "japanese" => format!( "{found}は{}行目ですでに移動されています", @@ -1196,10 +1418,12 @@ impl OwnershipError { moved_loc.ln_begin().unwrap_or(0) ), ), - None, + errno, + MoveError, + name_loc, ), input, - caused_by.into(), + caused_by, ) } } @@ -1212,16 +1436,22 @@ pub type LowerResult = TyCheckResult; pub type SingleLowerResult = SingleTyCheckResult; impl LowerError { - pub fn syntax_error>( + pub fn syntax_error( input: Input, errno: usize, loc: Location, caused_by: String, - desc: S, + desc: String, hint: Option, ) -> Self { Self::new( - ErrorCore::new(errno, SyntaxError, loc, desc, hint), + ErrorCore::new( + vec![SubMessage::ambiguous_new(loc, vec![], hint)], + desc, + errno, + SyntaxError, + loc, + ), input, caused_by, ) @@ -1237,16 +1467,16 @@ impl LowerError { let name = readable_name(name); Self::new( ErrorCore::new( - errno, - NameError, - loc, + vec![SubMessage::only_loc(loc)], switch_lang!( "japanese" => format!("{name}は既に宣言されています"), "simplified_chinese" => format!("{name}已声明"), "traditional_chinese" => format!("{name}已聲明"), "english" => format!("{name} is already declared"), ), - Option::::None, + errno, + NameError, + loc, ), input, caused_by, @@ -1263,16 +1493,16 @@ impl LowerError { let name = readable_name(name); Self::new( ErrorCore::new( - errno, - NameError, - loc, + vec![SubMessage::only_loc(loc)], switch_lang!( "japanese" => format!("{name}は既に定義されています"), "simplified_chinese" => format!("{name}已定义"), "traditional_chinese" => format!("{name}已定義"), "english" => format!("{name} is already defined"), ), - Option::::None, + errno, + NameError, + loc, ), input, caused_by, @@ -1288,21 +1518,21 @@ impl LowerError { spec_t: &Type, found_t: &Type, ) -> Self { - let name = readable_name(name); - let expect = StyledString::new(&format!("{}", spec_t), Some(HINT), Some(Attribute::Bold)); - let found = StyledString::new(&format!("{}", found_t), Some(ERR), Some(Attribute::Bold)); + let name = StyledString::new(readable_name(name), Some(ACCENT), None); + let expect = StyledString::new(format!("{}", spec_t), Some(HINT), Some(ATTR)); + let found = StyledString::new(format!("{}", found_t), Some(ERR), Some(ATTR)); Self::new( ErrorCore::new( - errno, - TypeError, - loc, + vec![SubMessage::only_loc(loc)], switch_lang!( "japanese" => format!("{name}は{expect}型として宣言されましたが、{found}型のオブジェクトが代入されています"), "simplified_chinese" => format!("{name}被声明为{expect},但分配了一个{found}对象"), "traditional_chinese" => format!("{name}被聲明為{expect},但分配了一個{found}對象"), "english" => format!("{name} was declared as {expect}, but an {found} object is assigned"), ), - Option::::None, + errno, + TypeError, + loc, ), input, caused_by, @@ -1319,6 +1549,7 @@ impl LowerError { ) -> Self { let name = readable_name(name); let hint = similar_name.map(|n| { + let n = StyledStr::new(n, Some(HINT), Some(ATTR)); switch_lang!( "japanese" => format!("似た名前の変数があります: {n}"), "simplified_chinese" => format!("存在相同名称变量: {n}"), @@ -1326,19 +1557,19 @@ impl LowerError { "english" => format!("exists a similar name variable: {n}"), ) }); - let found = StyledString::new(name, Some(ERR), Some(Attribute::Bold)); + let found = StyledString::new(name, Some(ERR), Some(ATTR)); Self::new( ErrorCore::new( - errno, - NameError, - loc, + vec![SubMessage::ambiguous_new(loc, vec![], hint)], switch_lang!( "japanese" => format!("{found}という変数は定義されていません"), "simplified_chinese" => format!("{found}未定义"), "traditional_chinese" => format!("{found}未定義"), "english" => format!("{found} is not defined"), ), - hint, + errno, + NameError, + loc, ), input, caused_by, @@ -1352,24 +1583,25 @@ impl LowerError { caused_by: String, typ: &Type, ) -> Self { - let typ = StyledString::new(&typ.to_string(), Some(ERR), Some(Attribute::Bold)); + let typ = StyledString::new(&typ.to_string(), Some(ERR), Some(ATTR)); + let hint = Some(switch_lang!( + "japanese" => format!("恐らくこれはErgコンパイラのバグです、{URL}へ報告してください"), + "simplified_chinese" => format!("这可能是Erg编译器的错误,请报告给{URL}"), + "traditional_chinese" => format!("這可能是Erg編譯器的錯誤,請報告給{URL}"), + "english" => format!("This may be a bug of Erg compiler, please report to {URL}"), + )); Self::new( ErrorCore::new( - errno, - NameError, - loc, + vec![SubMessage::ambiguous_new(loc, vec![], hint)], switch_lang!( "japanese" => format!("{typ}という型は定義されていません"), "simplified_chinese" => format!("{typ}未定义"), "traditional_chinese" => format!("{typ}未定義"), "english" => format!("Type {typ} is not defined"), ), - Some(switch_lang!( - "japanese" => format!("恐らくこれはErgコンパイラのバグです、{URL}へ報告してください"), - "simplified_chinese" => format!("这可能是Erg编译器的错误,请报告给{URL}"), - "traditional_chinese" => format!("這可能是Erg編譯器的錯誤,請報告給{URL}"), - "english" => format!("This may be a bug of Erg compiler, please report to {URL}"), - )), + errno, + NameError, + loc, ), input, caused_by, @@ -1393,19 +1625,19 @@ impl LowerError { "english" => format!("has a similar name attribute: {n}"), ) }); - let found = StyledString::new(name, Some(ERR), Some(Attribute::Bold)); + let found = StyledString::new(name, Some(ERR), Some(ATTR)); Self::new( ErrorCore::new( - errno, - AttributeError, - loc, + vec![SubMessage::ambiguous_new(loc, vec![], hint)], switch_lang!( "japanese" => format!("{obj_t}型オブジェクトに{found}という属性はありません"), "simplified_chinese" => format!("{obj_t}对象没有属性{found}"), "traditional_chinese" => format!("{obj_t}對像沒有屬性{found}"), "english" => format!("{obj_t} object has no attribute {found}"), ), - hint, + errno, + AttributeError, + loc, ), input, caused_by, @@ -1424,6 +1656,7 @@ impl LowerError { similar_name: Option<&str>, ) -> Self { let hint = similar_name.map(|n| { + let n = StyledStr::new(n, Some(HINT), Some(ATTR)); switch_lang!( "japanese" => format!("似た名前の属性があります: {n}"), "simplified_chinese" => format!("具有相同名称的属性: {n}"), @@ -1431,19 +1664,19 @@ impl LowerError { "english" => format!("has a similar name attribute: {n}"), ) }); - let found = StyledString::new(name, Some(ERR), Some(Attribute::Bold)); + let found = StyledString::new(name, Some(ERR), Some(ATTR)); Self::new( ErrorCore::new( - errno, - AttributeError, - loc, + vec![SubMessage::ambiguous_new(loc, vec![], hint)], switch_lang!( "japanese" => format!("{obj_name}(: {obj_t})に{found}という属性はありません"), "simplified_chinese" => format!("{obj_name}(: {obj_t})没有属性{found}"), "traditional_chinese" => format!("{obj_name}(: {obj_t})沒有屬性{found}"), "english" => format!("{obj_name}(: {obj_t}) has no attribute {found}"), ), - hint, + errno, + AttributeError, + loc, ), input, caused_by, @@ -1457,19 +1690,19 @@ impl LowerError { caused_by: String, name: &str, ) -> Self { - let name = StyledString::new(readable_name(name), Some(WARNING), Some(Attribute::Bold)); + let name = StyledStr::new(readable_name(name), Some(WARN), Some(ATTR)); Self::new( ErrorCore::new( - errno, - AssignError, - loc, + vec![SubMessage::only_loc(loc)], switch_lang!( "japanese" => format!("変数{name}に複数回代入することはできません"), "simplified_chinese" => format!("不能为变量{name}分配多次"), "traditional_chinese" => format!("不能為變量{name}分配多次"), "english" => format!("variable {name} cannot be assigned more than once"), ), - None, + errno, + AssignError, + loc, ), input, caused_by, @@ -1483,19 +1716,19 @@ impl LowerError { name: &str, caused_by: String, ) -> Self { - let name = StyledString::new(readable_name(name), Some(WARNING), Some(Attribute::Bold)); + let name = StyledString::new(readable_name(name), Some(WARN), Some(ATTR)); Self::new( ErrorCore::new( - errno, - UnusedWarning, - loc, + vec![SubMessage::only_loc(loc)], switch_lang!( "japanese" => format!("{name}は使用されていません"), "simplified_chinese" => format!("{name}未使用"), "traditional_chinese" => format!("{name}未使用"), "english" => format!("{name} is not used"), ), - None, + errno, + UnusedWarning, + loc, ), input, caused_by, @@ -1503,23 +1736,19 @@ impl LowerError { } pub fn del_error(input: Input, errno: usize, ident: &Identifier, caused_by: String) -> Self { - let name = StyledString::new( - readable_name(ident.inspect()), - Some(WARNING), - Some(Attribute::Bold), - ); + let name = StyledString::new(readable_name(ident.inspect()), Some(WARN), Some(ATTR)); Self::new( ErrorCore::new( - errno, - NameError, - ident.loc(), + vec![SubMessage::only_loc(ident.loc())], switch_lang!( "japanese" => format!("{name}は削除できません"), "simplified_chinese" => format!("{name}不能删除"), "traditional_chinese" => format!("{name}不能刪除"), "english" => format!("{name} cannot be deleted"), ), - None, + errno, + NameError, + ident.loc(), ), input, caused_by, @@ -1534,7 +1763,6 @@ impl LowerError { name: &str, vis: Visibility, ) -> Self { - let name = readable_name(name); let visibility = if vis.is_private() { switch_lang!( "japanese" => "非公開", @@ -1550,19 +1778,19 @@ impl LowerError { "english" => "public", ) }; - let found = StyledString::new(name, Some(ERR), Some(Attribute::Bold)); + let found = StyledString::new(readable_name(name), Some(ACCENT), Some(ATTR)); Self::new( ErrorCore::new( - errno, - VisibilityError, - loc, + vec![SubMessage::only_loc(loc)], switch_lang!( "japanese" => format!("{found}は{visibility}変数です"), "simplified_chinese" => format!("{found}是{visibility}变量",), "traditional_chinese" => format!("{found}是{visibility}變量",), "english" => format!("{found} is {visibility} variable",), ), - None, + errno, + VisibilityError, + loc, ), input, caused_by, @@ -1577,17 +1805,53 @@ impl LowerError { superclass: &Type, caused_by: S, ) -> Self { - let name = StyledString::new(name, Some(ERR), Some(Attribute::Bold)); - let superclass = StyledString::new( - &format!("{}", superclass), - Some(WARNING), - Some(Attribute::Bold), + let name = StyledString::new(name, Some(ERR), Some(ATTR)); + let superclass = StyledString::new(format!("{}", superclass), Some(WARN), Some(ATTR)); + let hint = Some( + switch_lang!( + "japanese" => { + let mut ovr = StyledStrings::default(); + ovr.push_str_with_color_and_attribute("@Override", HINT, ATTR); + ovr.push_str("デコレータを使用してください"); + ovr + }, + "simplified_chinese" => { + let mut ovr = StyledStrings::default(); + ovr.push_str("请使用"); + ovr.push_str_with_color_and_attribute("@Override", HINT, ATTR); + ovr.push_str("装饰器"); + ovr + }, + "traditional_chinese" => { + let mut ovr = StyledStrings::default(); + ovr.push_str("請使用"); + ovr.push_str_with_color_and_attribute("@Override", HINT, ATTR); + ovr.push_str("裝飾器"); + ovr + }, + "english" => { + let mut ovr = StyledStrings::default(); + ovr.push_str("use "); + ovr.push_str_with_color_and_attribute("@Override", HINT, ATTR); + ovr.push_str(" decorator"); + ovr + }, + ) + .to_string(), + ); + let sub_msg = switch_lang!( + "japanese" => "デフォルトでオーバーライドはできません", + "simplified_chinese" => "默认不可重写", + "simplified_chinese" => "默認不可重寫", + "english" => "cannot override by default", ); Self::new( ErrorCore::new( - errno, - NameError, - name_loc, + vec![SubMessage::ambiguous_new( + name_loc, + vec![sub_msg.to_string()], + hint, + )], switch_lang!( "japanese" => format!( "{name}は{superclass}で既に定義されています", @@ -1602,12 +1866,9 @@ impl LowerError { "{name} is already defined in {superclass}", ), ), - Some(switch_lang!( - "japanese" => "デフォルトでオーバーライドはできません(`Override`デコレータを使用してください)", - "simplified_chinese" => "默认不可重写(请使用`Override`装饰器)", - "traditional_chinese" => "默認不可重寫(請使用`Override`裝飾器)", - "english" => "cannot override by default (use `Override` decorator)", - ).into()), + errno, + NameError, + name_loc, ), input, caused_by.into(), @@ -1623,16 +1884,16 @@ impl LowerError { ) -> Self { Self::new( ErrorCore::new( - errno, - InheritanceError, - loc, + vec![SubMessage::only_loc(loc)], switch_lang!( "japanese" => format!("{class}は継承できません"), "simplified_chinese" => format!("{class}不可继承"), "traditional_chinese" => format!("{class}不可繼承"), "english" => format!("{class} is not inheritable"), ), - None, + errno, + InheritanceError, + loc, ), input, caused_by, @@ -1648,7 +1909,13 @@ impl LowerError { hint: Option, ) -> Self { Self::new( - ErrorCore::new(errno, IoError, loc, desc, hint), + ErrorCore::new( + vec![SubMessage::ambiguous_new(loc, vec![], hint)], + desc, + errno, + IoError, + loc, + ), input, caused_by, ) @@ -1679,26 +1946,142 @@ impl LowerError { similar_erg_mod: Option, similar_py_mod: Option, ) -> Self { - let hint = match (similar_erg_mod, similar_py_mod) { - (Some(erg), Some(py)) => { - let erg = StyledString::new(&erg, Some(WARNING), Some(Attribute::Bold)); - let py = StyledString::new(&py, Some(WARNING), Some(Attribute::Bold)); - Some(format!( - "similar name erg module {erg} and python module {py} exists (to import python modules, use `pyimport`)", - )) + let mut erg_str = StyledStrings::default(); + let mut py_str = StyledStrings::default(); + let hint = switch_lang!( + "japanese" => { + match (similar_erg_mod, similar_py_mod) { + (Some(erg), Some(py)) => { + erg_str.push_str("似た名前のergモジュールが存在します: "); + erg_str.push_str_with_color_and_attribute(erg, HINT, ATTR); + py_str.push_str("似た名前のpythonモジュールが存在します: "); + py_str.push_str_with_color_and_attribute(py, HINT, ATTR); + let mut hint = StyledStrings::default(); + hint.push_str("pythonのモジュールをインポートするためには"); + hint.push_str_with_color_and_attribute("pyimport", ACCENT, ATTR); + hint.push_str("を使用してください"); + Some(hint.to_string()) + } + (Some(erg), None) => { + erg_str.push_str("似た名前のergモジュールが存在します"); + erg_str.push_str_with_color_and_attribute(erg, ACCENT, ATTR); + None + } + (None, Some(py)) => { + py_str.push_str("似た名前のpythonモジュールが存在します"); + py_str.push_str_with_color_and_attribute(py, HINT, ATTR); + let mut hint = StyledStrings::default(); + hint.push_str("pythonのモジュールをインポートするためには"); + hint.push_str_with_color_and_attribute("pyimport", ACCENT, ATTR); + hint.push_str("を使用してください"); + Some(hint.to_string()) + } + (None, None) => None, } - (Some(erg), None) => { - let erg = StyledString::new(&erg, Some(WARNING), Some(Attribute::Bold)); - Some(format!("similar name erg module exists: {erg}")) + }, + "simplified_chinese" => { + match (similar_erg_mod, similar_py_mod) { + (Some(erg), Some(py)) => { + erg_str.push_str("存在相似名称的erg模块: "); + erg_str.push_str_with_color_and_attribute(erg, HINT, ATTR); + py_str.push_str("存在相似名称的python模块: "); + py_str.push_str_with_color_and_attribute(py, HINT, ATTR); + let mut hint = StyledStrings::default(); + hint.push_str("要导入python模块,请使用"); + hint.push_str_with_color_and_attribute("pyimport", ACCENT, ATTR); + Some(hint.to_string()) + } + (Some(erg), None) => { + erg_str.push_str("存在相似名称的erg模块: "); + erg_str.push_str_with_color_and_attribute(erg, HINT, ATTR); + None + } + (None, Some(py)) => { + py_str.push_str("存在相似名称的python模块: "); + py_str.push_str_with_color_and_attribute(py, HINT, ATTR); + let mut hint = StyledStrings::default(); + hint.push_str("要导入python模块,请使用"); + hint.push_str_with_color_and_attribute("pyimport", ACCENT, ATTR); + Some(hint.to_string()) + } + (None, None) => None, } - (None, Some(py)) => { - let py = StyledString::new(&py, Some(WARNING), Some(Attribute::Bold)); - Some(format!("similar name python module exists: {py} (to import python modules, use `pyimport`)")) + }, + "traditional_chinese" => { + match (similar_erg_mod, similar_py_mod) { + (Some(erg), Some(py)) => { + erg_str.push_str("存在類似名稱的erg模塊: "); + erg_str.push_str_with_color_and_attribute(erg, HINT, ATTR); + py_str.push_str("存在類似名稱的python模塊: "); + py_str.push_str_with_color_and_attribute(py, HINT, ATTR); + let mut hint = StyledStrings::default(); + hint.push_str("要導入python模塊, 請使用"); + hint.push_str_with_color_and_attribute("pyimport", ACCENT, ATTR); + Some(hint.to_string()) + } + (Some(erg), None) => { + erg_str.push_str("存在類似名稱的erg模塊: "); + erg_str.push_str_with_color_and_attribute(erg, HINT, ATTR); + None + } + (None, Some(py)) => { + py_str.push_str("存在類似名稱的python模塊: "); + py_str.push_str_with_color_and_attribute(py, HINT, ATTR); + let mut hint = StyledStrings::default(); + hint.push_str("要導入python模塊, 請使用"); + hint.push_str_with_color_and_attribute("pyimport", ACCENT, ATTR); + Some(hint.to_string()) + } + (None, None) => None, } - (None, None) => None, + }, + "english" => { + match (similar_erg_mod, similar_py_mod) { + (Some(erg), Some(py)) => { + erg_str.push_str("similar name erg module exists: "); + erg_str.push_str_with_color_and_attribute(erg, HINT, ATTR); + py_str.push_str("similar name python module exists: "); + py_str.push_str_with_color_and_attribute(py, HINT, ATTR); + let mut hint = StyledStrings::default(); + hint.push_str("to import python modules, use "); + hint.push_str_with_color_and_attribute("pyimport", ACCENT, ATTR); + Some(hint.to_string()) + } + (Some(erg), None) => { + erg_str.push_str("similar name erg module exists: "); + erg_str.push_str_with_color_and_attribute(erg, HINT, ATTR); + None + } + (None, Some(py)) => { + py_str.push_str("similar name python module exits: "); + py_str.push_str_with_color_and_attribute(py, HINT, ATTR); + let mut hint = StyledStrings::default(); + hint.push_str("to import python modules, use "); + hint.push_str_with_color_and_attribute("pyimport", ACCENT, ATTR); + Some(hint.to_string()) + } + (None, None) => None, + } + }, + ); + // .to_string().is_empty() is not necessarily empty because there are Color or Attribute that are not displayed + let msg = match (erg_str.is_empty(), py_str.is_empty()) { + (false, false) => vec![erg_str.to_string(), py_str.to_string()], + (false, true) => vec![erg_str.to_string()], + (true, false) => vec![py_str.to_string()], + (true, true) => vec![], }; - let hint = hint.map(String::from); - Self::file_error(input, errno, desc, loc, caused_by, hint) + Self::new( + ErrorCore::new( + vec![SubMessage::ambiguous_new(loc, msg, hint)], + desc, + errno, + ImportError, + loc, + ), + input, + caused_by, + ) } pub fn inner_typedef_error( @@ -1709,16 +2092,16 @@ impl LowerError { ) -> Self { Self::new( ErrorCore::new( - errno, - TypeError, - loc, + vec![SubMessage::only_loc(loc)], switch_lang!( "japanese" => format!("型はトップレベルで定義されなければなりません"), "simplified_chinese" => format!("类型必须在顶层定义"), "traditional_chinese" => format!("類型必須在頂層定義"), "english" => format!("types must be defined at the top level"), ), - None, + errno, + TypeError, + loc, ), input, caused_by, @@ -1728,16 +2111,16 @@ impl LowerError { pub fn declare_error(input: Input, errno: usize, loc: Location, caused_by: String) -> Self { Self::new( ErrorCore::new( - errno, - SyntaxError, - loc, + vec![SubMessage::only_loc(loc)], switch_lang!( "japanese" => format!("d.erファイル内では宣言、別名定義のみが許可されています"), "simplified_chinese" => format!("在d.er文件中只允许声明和别名定义"), "traditional_chinese" => format!("在d.er文件中只允許聲明和別名定義"), "english" => format!("declarations and alias definitions are only allowed in d.er files"), ), - None, + errno, + SyntaxError, + loc, ), input, caused_by, @@ -1754,24 +2137,20 @@ impl LowerError { cast_to: &Type, hint: Option, ) -> Self { - let name = StyledString::new(name, Some(WARNING), Some(Attribute::Bold)); - let found = StyledString::new( - &format!("{}", cast_to), - Some(WARNING), - Some(Attribute::Bold), - ); + let name = StyledString::new(name, Some(WARN), Some(ATTR)); + let found = StyledString::new(format!("{}", cast_to), Some(ERR), Some(ATTR)); Self::new( ErrorCore::new( - errno, - TypeError, - loc, + vec![SubMessage::ambiguous_new(loc, vec![], hint)], switch_lang!( "japanese" => format!("{name}の型を{found}にキャストすることはできません"), "simplified_chinese" => format!("{name}的类型无法转换为{found}"), "traditional_chinese" => format!("{name}的類型無法轉換為{found}"), "english" => format!("the type of {name} cannot be cast to {found}"), ), - hint, + errno, + TypeError, + loc, ), input, caused_by, @@ -1825,125 +2204,302 @@ pub type CompileWarnings = CompileErrors; #[cfg(test)] mod test { - - use erg_common::{config::Input, error::Location}; - - use crate::ty::Type; - use super::TyCheckError; + use crate::{ + error::{CompileError, EvalError, LowerError}, + hir::Identifier, + ty::{Predicate, Type}, + varinfo::VarInfo, + }; + use erg_common::{config::Input, error::Location}; + use erg_parser::ast::VarName; + // These Erg codes are not correct grammar. + // This test make sure sub_msg and hint are displayed correctly. #[test] - fn default_arg_error_test() { - let loc = Location::Range { - ln_begin: 1, - col_begin: 0, - ln_end: 1, - col_end: 5, - }; - let input = Input::Pipe("a = 1".to_string()); - let caused_by = "File name here basically"; - let desc = "Some kinds of error description here"; - let hint = Some("Some hint massage here\n".into()); + fn default_error_format_confirmation() { + let mut errors = Vec::new(); - let err = TyCheckError::syntax_error(input, 0, loc, caused_by.into(), desc, hint); - print!("{}", err); + let input = Input::Pipe("stack bug error".to_owned()); + let loc = Location::Line(1); + let err = CompileError::stack_bug(input, loc, 0, 0, "FileName"); + errors.push(err); - let loc = Location::Range { - ln_begin: 1, - col_begin: 24, - ln_end: 1, - col_end: 27, - }; - let input = Input::Pipe("Person = Class { name = Str }".to_string()); - let caused_by = "File name here basically"; + let input = Input::Pipe("checker bug error".to_owned()); + let errno = 0; + let err = TyCheckError::checker_bug(input, errno, Location::Unknown, "name", 1); + errors.push(err); + + let loc = Location::LineRange(1, 3); + let input = Input::Pipe("args\nmissing\nerror".to_string()); + let caused_by = ""; let err = TyCheckError::args_missing_error( input, - 0, + errno, loc, "\"Callee name here\"", caused_by.into(), 0, vec!["sample".into(), "args".into(), "here".into()], ); - print!("{}", err); - - let loc = Location::Range { - ln_begin: 1, - col_begin: 0, - ln_end: 3, - col_end: 5, - }; - let input = Input::Pipe( - "\ -if True: - sample - end -" - .to_string(), - ); - let caused_by = "File name here basically"; - let err = TyCheckError::args_missing_error( - input, - 0, - loc, - "\"Callee name here\"", - caused_by.into(), - 0, - vec!["sample".into(), "args".into(), "here".into()], - ); - print!("{}", err); - - let loc = Location::RangePair { - ln_first: (1, 2), - col_first: (0, 1), - ln_second: (4, 4), - col_second: (9, 10), - }; - let input = Input::Pipe( - "\ -a: Nat = 1 -a.ownership_is_moved() - -function(a) -" - .to_string(), - ); - let err = TyCheckError::checker_bug(input, 0, loc, "file_name", 0); - print!("{}", err); + errors.push(err); let loc = Location::Range { ln_begin: 1, col_begin: 0, ln_end: 1, - col_end: 3, - }; - let input = Input::Pipe("add(x, y):Nat = x - y".to_string()); - let err = TyCheckError::checker_bug(input, 0, loc, "file_name", 0); - print!("{}", err); - - let loc = Location::Range { - ln_begin: 1, - col_begin: 11, - ln_end: 1, - col_end: 14, + col_end: 17, }; let expect = Type::Nat; - let found = Type::Obj; - let input = Input::Pipe("add(x, y): Nat = x - y".to_string()); - let caused_by = "File name here basically"; + let found = Type::Int; + let input = Input::Pipe("return type error".to_string()); + let name = "name"; let err = TyCheckError::return_type_error( input, - 0, + errno, loc, - caused_by.into(), - "name", + caused_by.to_string(), + name, &expect, &found, ); - print!("{}", err); + errors.push(err); - let input = Input::Pipe("Dummy code here".to_string()); - let err = TyCheckError::unreachable(input, "file name here", 1); - print!("{}", err); + let loc = Location::Range { + ln_begin: 1, + col_begin: 0, + ln_end: 1, + col_end: 4, + }; + let expect = Type::Nat; + let found = Type::Int; + let input = Input::Pipe("type mismatch error".to_string()); + let err = TyCheckError::type_mismatch_error( + input, + errno, + loc, + caused_by.into(), + name, + Some(1), + &expect, + &found, + None, + Some("hint message here".to_owned()), + ); + errors.push(err); + + let input = Input::Pipe( + "too_many_args_error(some_long_name_variable_1, + some_long_name_variable_2, + some_long_name_variable_3, + some_long_name_variable_4) =" + .to_string(), + ); + let loc = Location::LineRange(1, 4); + let callee_name = "callee name"; + let params_len = 3; + let pos_args_len = 4; + let kw_args_len = 4; + let err = TyCheckError::too_many_args_error( + input, + errno, + loc, + callee_name, + caused_by.to_string(), + params_len, + pos_args_len, + kw_args_len, + ); + errors.push(err); + + let input = Input::Pipe("argument error".to_string()); + let loc = Location::range(1, 0, 1, 8); + let err = TyCheckError::argument_error(input, errno, loc, caused_by.to_string(), 1, 2); + errors.push(err); + + let input = Input::Pipe("Nat <: Int <: Ratio".to_string()); + let loc = Location::range(1, 0, 1, 10); + let sub_t = &Type::Nat; + let sup_t = &Type::Int; + let err = + TyCheckError::subtyping_error(input, errno, sub_t, sup_t, loc, caused_by.to_string()); + errors.push(err); + + let input = Input::Pipe("pred unification error".to_string()); + let lhs = &Predicate::Const("Str".into()); + let rhs = &Predicate::Const("Nat".into()); + let err = + TyCheckError::pred_unification_error(input, errno, lhs, rhs, caused_by.to_string()); + errors.push(err); + + let input = Input::Pipe("Trait member type error".to_string()); + let errno = 0; + let loc = Location::Range { + ln_begin: 1, + col_begin: 0, + ln_end: 1, + col_end: 5, + }; + let t_ty = &Type::Float; + let exp = &Type::Nat; + let fnd = &Type::Obj; + let err = TyCheckError::trait_member_type_error( + input, + errno, + loc, + caused_by.to_string(), + "member name", + t_ty, + exp, + fnd, + Some("hint message here".to_string()), + ); + errors.push(err); + + let input = Input::Pipe("trait member not defined error".to_string()); + let member_name = "member name"; + let trait_type = &Type::ClassType; + let class_type = &Type::Ellipsis; + let hint = Some("hint message here".to_string()); + let err = TyCheckError::trait_member_not_defined_error( + input, + errno, + caused_by.to_string(), + member_name, + trait_type, + class_type, + hint, + ); + errors.push(err); + + let input = Input::Pipe("singular no attribute error".to_string()); + let loc = Location::Range { + ln_begin: 1, + col_begin: 0, + ln_end: 1, + col_end: 8, + }; + let obj_name = "ojb name"; + let obj_t = Type::Bool; + let name = "name"; + let similar_name = Some("similar name"); + let err = LowerError::singular_no_attr_error( + input, + errno, + loc, + caused_by.to_string(), + obj_name, + &obj_t, + name, + similar_name, + ); + errors.push(err); + + let input = Input::Pipe("ambiguous type error".to_string()); + let expr = Identifier::new( + Some(erg_parser::token::Token { + kind: erg_parser::token::TokenKind::EOF, + content: "expr_content".into(), + lineno: 1, + col_begin: 1, + }), + VarName::from_str("variable_name".into()), + None, + VarInfo::new( + Type::Nat, + crate::varinfo::Mutability::Const, + erg_common::vis::Visibility::Private, + crate::varinfo::VarKind::Builtin, + None, + None, + None, + ), + ); + let candidates = &[Type::Nat, Type::Inf, Type::Bool]; + let err = + EvalError::ambiguous_type_error(input, errno, &expr, candidates, caused_by.to_string()); + errors.push(err); + + let input = Input::Pipe("invalid type cast error".to_string()); + let loc = Location::range(1, 8, 1, 17); + let cast_to = Type::Error; + let hint = Some("hint message here".to_string()); + let err = EvalError::invalid_type_cast_error( + input, + errno, + loc, + caused_by.to_string(), + name, + &cast_to, + hint, + ); + errors.push(err); + + let input = Input::Pipe("override error".to_string()); + let name_loc = Location::range(1, 0, 1, 8); + let superclass = &Type::Failure; + let err = TyCheckError::override_error( + input, + errno, + name, + name_loc, + superclass, + caused_by.to_string(), + ); + errors.push(err); + + let input = Input::Pipe("visibility error".to_string()); + let loc = Location::Line(1); + let vis = erg_common::vis::Visibility::Private; + let err = + TyCheckError::visibility_error(input, errno, loc, caused_by.to_string(), name, vis); + errors.push(err); + + let input = Input::Pipe("import nunpy as np".to_string()); + let errno = 0; + let desc = "nunpy is not defined".to_string(); + let loc = Location::range(1, 7, 1, 12); + let similar_erg_mod = Some("numpyer".into()); + let similar_py_mod = Some("numpy".into()); + let err = TyCheckError::import_error( + input.clone(), + errno, + desc.clone(), + loc, + caused_by.to_string(), + similar_erg_mod.clone(), + similar_py_mod.clone(), + ); + errors.push(err); + + let err = TyCheckError::import_error( + input.clone(), + errno, + desc.clone(), + loc, + caused_by.to_string(), + None, + similar_py_mod, + ); + errors.push(err); + + let err = TyCheckError::import_error( + input.clone(), + errno, + desc.clone(), + loc, + caused_by.to_string(), + similar_erg_mod, + None, + ); + errors.push(err); + + let err = + TyCheckError::import_error(input, errno, desc, loc, caused_by.to_string(), None, None); + errors.push(err); + + for err in errors.into_iter() { + print!("{err}"); + } } } diff --git a/compiler/erg_compiler/lower.rs b/compiler/erg_compiler/lower.rs index c95c50d8..3827169b 100644 --- a/compiler/erg_compiler/lower.rs +++ b/compiler/erg_compiler/lower.rs @@ -191,7 +191,7 @@ impl ASTLowerer { "traditional_chinese" => "如果您不想使用該值,請使用discard函數", "english" => "if you don't use the value, use discard function", ) - .into(), + .to_owned(), ), )) } else { @@ -266,7 +266,8 @@ impl ASTLowerer { "simplified_chinese" => "数组元素必须全部是相同类型", "traditional_chinese" => "數組元素必須全部是相同類型", "english" => "all elements of an array must be of the same type", - ), + ) + .to_owned(), Some( switch_lang!( "japanese" => "Int or Strなど明示的に型を指定してください", @@ -274,7 +275,7 @@ impl ASTLowerer { "traditional_chinese" => "請明確指定類型,例如: Int or Str", "english" => "please specify the type explicitly, e.g. Int or Str", ) - .into(), + .to_owned(), ), ))); } @@ -412,7 +413,8 @@ impl ASTLowerer { "simplified_chinese" => "集合元素必须全部是相同类型", "traditional_chinese" => "集合元素必須全部是相同類型", "english" => "all elements of a set must be of the same type", - ), + ) + .to_owned(), Some( switch_lang!( "japanese" => "Int or Strなど明示的に型を指定してください", @@ -420,7 +422,7 @@ impl ASTLowerer { "traditional_chinese" => "明確指定類型,例如: Int or Str", "english" => "please specify the type explicitly, e.g. Int or Str", ) - .into(), + .to_owned(), ), ))); } @@ -538,7 +540,8 @@ impl ASTLowerer { "simplified_chinese" => "Dict的值必须是同一类型", "traditional_chinese" => "Dict的值必須是同一類型", "english" => "Values of Dict must be the same type", - ), + ) + .to_owned(), Some( switch_lang!( "japanese" => "Int or Strなど明示的に型を指定してください", @@ -546,7 +549,7 @@ impl ASTLowerer { "traditional_chinese" => "明確指定類型,例如: Int or Str", "english" => "please specify the type explicitly, e.g. Int or Str", ) - .into(), + .to_owned(), ), ))); } @@ -692,7 +695,7 @@ impl ASTLowerer { line!() as usize, call.args.loc(), self.ctx.caused_by(), - "invalid assert casting type", + "invalid assert casting type".to_owned(), None, ))); } @@ -751,7 +754,7 @@ impl ASTLowerer { line!() as usize, other.loc(), self.ctx.caused_by(), - "", + "".to_owned(), None, ))) } diff --git a/compiler/erg_compiler/ty/deserialize.rs b/compiler/erg_compiler/ty/deserialize.rs index 3550d0fa..d2353ca7 100644 --- a/compiler/erg_compiler/ty/deserialize.rs +++ b/compiler/erg_compiler/ty/deserialize.rs @@ -5,7 +5,7 @@ use std::string::FromUtf8Error; use erg_common::cache::CacheSet; use erg_common::config::{ErgConfig, Input}; use erg_common::dict::Dict; -use erg_common::error::{ErrorCore, ErrorKind, Location}; +use erg_common::error::{ErrorCore, ErrorKind, Location, SubMessage}; use erg_common::python_util::PythonVersion; use erg_common::serialize::DataTypePrefix; use erg_common::{fn_name, switch_lang}; @@ -39,11 +39,11 @@ impl From for DeserializeError { impl From for ErrorCore { fn from(err: DeserializeError) -> Self { ErrorCore::new( + vec![SubMessage::only_loc(Location::Unknown)], + err.desc, err.errno, ErrorKind::ImportError, Location::Unknown, - err.desc, - Option::::None, ) } } diff --git a/compiler/erg_parser/error.rs b/compiler/erg_parser/error.rs index 72f2fc56..7b578e9c 100644 --- a/compiler/erg_parser/error.rs +++ b/compiler/erg_parser/error.rs @@ -4,8 +4,10 @@ use std::fmt; use erg_common::config::Input; -use erg_common::error::{ErrorCore, ErrorDisplay, ErrorKind::*, Location, MultiErrorDisplay}; -use erg_common::style::{Attribute, Color, StyledStr, StyledString, Theme}; +use erg_common::error::{ + ErrorCore, ErrorDisplay, ErrorKind::*, Location, MultiErrorDisplay, SubMessage, +}; +use erg_common::style::{Attribute, Color, StyledStr, StyledString, THEME}; use erg_common::traits::Stream; use erg_common::{impl_display_and_error, impl_stream_for_wrapper, switch_lang}; @@ -29,62 +31,68 @@ pub struct LexErrors(Vec); impl_stream_for_wrapper!(LexErrors, LexError); +const ERR: Color = THEME.colors.error; +const HINT: Color = THEME.colors.hint; +const ACCENT: Color = THEME.colors.accent; + impl LexError { pub fn new(core: ErrorCore) -> Self { Self(Box::new(core)) } pub fn set_hint>(&mut self, hint: S) { - self.0.hint = Some(hint.into()); + if let Some(sub_msg) = self.0.sub_messages.get_mut(0) { + sub_msg.set_hint(hint) + } } pub fn compiler_bug(errno: usize, loc: Location, fn_name: &str, line: u32) -> Self { const URL: StyledStr = StyledStr::new( "https://github.com/erg-lang/erg", - Some(Color::White), + Some(ACCENT), Some(Attribute::Underline), ); Self::new(ErrorCore::new( - errno, - CompilerSystemError, - loc, + vec![SubMessage::only_loc(loc)], switch_lang!( "japanese" => format!("これはErg compilerのバグです、開発者に報告して下さい ({URL})\n{fn_name}:{line}より発生"), "simplified_chinese" => format!("这是Erg编译器的一个错误,请报告给{URL}\n原因来自: {fn_name}:{line}"), "traditional_chinese" => format!("這是Erg編譯器的一個錯誤,請報告給{URL}\n原因來自: {fn_name}:{line}"), "english" => format!("this is a bug of the Erg compiler, please report it to {URL}\ncaused from: {fn_name}:{line}"), ), - None, + errno, + CompilerSystemError, + loc, )) } pub fn feature_error(errno: usize, loc: Location, name: &str) -> Self { Self::new(ErrorCore::new( - errno, - FeatureError, - loc, + vec![SubMessage::only_loc(loc)], switch_lang!( "japanese" => format!("この機能({name})はまだ正式に提供されていません"), "simplified_chinese" => format!("此功能({name})尚未实现"), "traditional_chinese" => format!("此功能({name})尚未實現"), "english" => format!("this feature({name}) is not implemented yet"), ), - None, + errno, + FeatureError, + loc, )) } pub fn simple_syntax_error(errno: usize, loc: Location) -> Self { Self::new(ErrorCore::new( - errno, - SyntaxError, - loc, + vec![SubMessage::only_loc(loc)], switch_lang!( "japanese" => "不正な構文です", "simplified_chinese" => "无效的语法", "traditional_chinese" => "無效的語法", "english" => "invalid syntax", ), - None, + errno, + SyntaxError, + loc, )) } @@ -94,7 +102,13 @@ impl LexError { desc: S, hint: Option, ) -> Self { - Self::new(ErrorCore::new(errno, SyntaxError, loc, desc, hint)) + Self::new(ErrorCore::new( + vec![SubMessage::ambiguous_new(loc, vec![], hint)], + desc, + errno, + SyntaxError, + loc, + )) } pub fn syntax_warning>( @@ -103,7 +117,13 @@ impl LexError { desc: S, hint: Option, ) -> Self { - Self::new(ErrorCore::new(errno, SyntaxWarning, loc, desc, hint)) + Self::new(ErrorCore::new( + vec![SubMessage::ambiguous_new(loc, vec![], hint)], + desc, + errno, + SyntaxWarning, + loc, + )) } pub fn no_var_error( @@ -113,6 +133,7 @@ impl LexError { similar_name: Option, ) -> Self { let hint = similar_name.map(|n| { + let n = StyledString::new(&n, Some(HINT), Some(Attribute::Bold)); switch_lang!( "japanese" => format!("似た名前の変数があります: {n}"), "simplified_chinese" => format!("存在相同名称变量: {n}"), @@ -120,18 +141,18 @@ impl LexError { "english" => format!("exists a similar name variable: {n}"), ) }); - let name = StyledString::new(name, Some(Color::Red), Some(Attribute::Underline)); + let name = StyledString::new(name, Some(ERR), Some(Attribute::Underline)); Self::new(ErrorCore::new( - errno, - NameError, - loc, + vec![SubMessage::ambiguous_new(loc, vec![], hint)], switch_lang!( "japanese" => format!("{name}という変数は定義されていません"), "simplified_chinese" => format!("{name}未定义"), "traditional_chinese" => format!("{name}未定義"), "english" => format!("{name} is not defined"), ), - hint, + errno, + NameError, + loc, )) } } @@ -164,7 +185,6 @@ pub type DesugaringResult = Result; pub struct ParserRunnerError { pub core: ErrorCore, pub input: Input, - pub theme: Theme, } impl_display_and_error!(ParserRunnerError); @@ -176,9 +196,6 @@ impl ErrorDisplay for ParserRunnerError { fn input(&self) -> &Input { &self.input } - fn theme(&self) -> &Theme { - &self.theme - } fn caused_by(&self) -> &str { "" } @@ -188,8 +205,8 @@ impl ErrorDisplay for ParserRunnerError { } impl ParserRunnerError { - pub const fn new(core: ErrorCore, input: Input, theme: Theme) -> Self { - Self { core, input, theme } + pub const fn new(core: ErrorCore, input: Input) -> Self { + Self { core, input } } } @@ -209,10 +226,10 @@ impl fmt::Display for ParserRunnerErrors { } impl ParserRunnerErrors { - pub fn convert(input: &Input, errs: ParseErrors, theme: Theme) -> Self { + pub fn convert(input: &Input, errs: ParseErrors) -> Self { Self( errs.into_iter() - .map(|err| ParserRunnerError::new(*err.0, input.clone(), theme)) + .map(|err| ParserRunnerError::new(*err.0, input.clone())) .collect(), ) } diff --git a/compiler/erg_parser/lex.rs b/compiler/erg_parser/lex.rs index f13c2600..c38abd99 100644 --- a/compiler/erg_parser/lex.rs +++ b/compiler/erg_parser/lex.rs @@ -4,7 +4,6 @@ use std::cmp::Ordering; use erg_common::cache::CacheSet; use erg_common::config::ErgConfig; use erg_common::config::Input; -use erg_common::style::THEME; use erg_common::traits::{Locational, Runnable, Stream}; use erg_common::{debug_power_assert, fn_name_full, normalize_newline, switch_lang}; @@ -43,7 +42,7 @@ impl Runnable for LexerRunner { let lexer = Lexer::from_str(self.input().read()); let ts = lexer .lex() - .map_err(|errs| LexerRunnerErrors::convert(self.input(), errs, THEME))?; + .map_err(|errs| LexerRunnerErrors::convert(self.input(), errs))?; println!("{ts}"); Ok(0) } @@ -53,13 +52,13 @@ impl Runnable for LexerRunner { if cfg!(feature = "debug") { let ts = lexer .lex() - .map_err(|errs| LexerRunnerErrors::convert(self.input(), errs, THEME))?; + .map_err(|errs| LexerRunnerErrors::convert(self.input(), errs))?; println!("{ts}"); Ok(ts.to_string()) } else { Ok(lexer .lex() - .map_err(|errs| LexerRunnerErrors::convert(self.input(), errs, THEME))? + .map_err(|errs| LexerRunnerErrors::convert(self.input(), errs))? .to_string()) } } diff --git a/compiler/erg_parser/parse.rs b/compiler/erg_parser/parse.rs index eecfb3d3..bf8bd106 100644 --- a/compiler/erg_parser/parse.rs +++ b/compiler/erg_parser/parse.rs @@ -11,7 +11,6 @@ use erg_common::error::Location; use erg_common::option_enum_unwrap; use erg_common::set::Set as HashSet; use erg_common::str::Str; -use erg_common::style::THEME; use erg_common::traits::Runnable; use erg_common::traits::{Locational, Stream}; use erg_common::{ @@ -207,16 +206,16 @@ impl ParserRunner { pub fn parse_token_stream(&mut self, ts: TokenStream) -> Result { Parser::new(ts) .parse() - .map_err(|errs| ParserRunnerErrors::convert(self.input(), errs, THEME)) + .map_err(|errs| ParserRunnerErrors::convert(self.input(), errs)) } pub fn parse(&mut self, src: String) -> Result { let ts = Lexer::new(Input::Str(src)) .lex() - .map_err(|errs| ParserRunnerErrors::convert(self.input(), errs, THEME))?; + .map_err(|errs| ParserRunnerErrors::convert(self.input(), errs))?; Parser::new(ts) .parse() - .map_err(|errs| ParserRunnerErrors::convert(self.input(), errs, THEME)) + .map_err(|errs| ParserRunnerErrors::convert(self.input(), errs)) } } diff --git a/compiler/erg_parser/tests/parse_test.rs b/compiler/erg_parser/tests/parse_test.rs index 62590f91..15734391 100644 --- a/compiler/erg_parser/tests/parse_test.rs +++ b/compiler/erg_parser/tests/parse_test.rs @@ -1,6 +1,5 @@ use erg_common::config::{ErgConfig, Input}; use erg_common::error::MultiErrorDisplay; -use erg_common::style::THEME; use erg_common::traits::Runnable; use erg_parser::error::ParserRunnerErrors; @@ -64,7 +63,7 @@ fn parse_test_from_code(file_path: &'static str) -> Result<(), ParserRunnerError match parser.parse_token_stream( lexer .lex() - .map_err(|errs| ParserRunnerErrors::convert(&input, errs, THEME))?, + .map_err(|errs| ParserRunnerErrors::convert(&input, errs))?, ) { Ok(module) => { println!("{module}");