Update: use Span and const Theme in style.rs

This commit is contained in:
GreasySlug 2022-11-12 18:03:10 +09:00
parent 2e8ac2848d
commit a16d46423f
6 changed files with 449 additions and 233 deletions

View file

@ -3,16 +3,17 @@
//! エラー処理に関する汎用的なコンポーネントを提供する //! エラー処理に関する汎用的なコンポーネントを提供する
use std::cmp; use std::cmp;
use std::fmt; use std::fmt;
use std::fmt::Write as _;
use std::io::{stderr, BufWriter, Write as _}; use std::io::{stderr, BufWriter, Write as _};
use crate::astr::AtomicStr; use crate::astr::AtomicStr;
use crate::config::Input; use crate::config::Input;
use crate::style::Attribute;
use crate::style::Characters;
use crate::style::Color; use crate::style::Color;
use crate::style::Color::{Cyan, Green, Magenta, Red, Yellow}; use crate::style::StrSpan;
use crate::style::Spans; use crate::style::StringSpan;
use crate::style::RESET; use crate::style::StringSpans;
use crate::style::{ATT_RESET, UNDERLINE}; use crate::style::Theme;
use crate::traits::{Locational, Stream}; use crate::traits::{Locational, Stream};
use crate::{fmt_option, impl_display_from_debug, switch_lang}; use crate::{fmt_option, impl_display_from_debug, switch_lang};
@ -362,59 +363,83 @@ impl ErrorCore {
} }
pub fn bug(errno: usize, loc: Location, fn_name: &str, line: u32) -> Self { pub fn bug(errno: usize, loc: Location, fn_name: &str, line: u32) -> Self {
const URL: StrSpan = StrSpan::new(
"https://github.com/erg-lang/erg",
Some(Color::White),
Some(Attribute::Underline),
);
Self::new( Self::new(
errno, errno,
CompilerSystemError, CompilerSystemError,
loc, loc,
switch_lang!( switch_lang!(
"japanese" => format!("これはErgのバグです、開発者に報告して下さい (https://github.com/erg-lang/erg)\n{fn_name}:{line}より発生"), "japanese" => format!("これはErgのバグです、開発者に報告して下さい({URL})\n{fn_name}:{line}より発生"),
"simplified_chinese" => format!("这是Erg的bug请报告给https://github.com/erg-lang/erg\n原因来自: {fn_name}:{line}"), "simplified_chinese" => format!("这是Erg的bug请报告给{URL}\n原因来自: {fn_name}:{line}"),
"traditional_chinese" => format!("这是Erg的bug请报告给https://github.com/erg-lang/erg\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 https://github.com/erg-lang/erg\ncaused from: {fn_name}:{line}"), "english" => format!("this is a bug of Erg, please report it to {URL}\ncaused from: {fn_name}:{line}"),
), ),
None, None,
) )
} }
} }
pub const VBAR_UNICODE: &str = ""; #[allow(clippy::too_many_arguments)]
pub const VBAR_BREAK_UNICODE: &str = "·"; fn format_context<E: ErrorDisplay + ?Sized>(
fn format_code_and_pointer<E: ErrorDisplay + ?Sized>(
e: &E, e: &E,
ln_begin: usize, ln_begin: usize,
ln_end: usize, ln_end: usize,
col_begin: usize, col_begin: usize,
col_end: usize, col_end: usize,
// kinds of error color and gutter(line number and vertical bar)
colors: (Color, Color),
// for formatting points
chars: &Characters,
// kinds of error for specify the color
mark: char,
) -> String { ) -> String {
let (err_color, gutter_color) = colors;
let mark = mark.to_string();
let codes = if e.input().is_repl() { let codes = if e.input().is_repl() {
vec![e.input().reread()] vec![e.input().reread()]
} else { } else {
e.input().reread_lines(ln_begin, ln_end) e.input().reread_lines(ln_begin, ln_end)
}; };
let mut res = CYAN.to_string(); let mut context = StringSpans::default();
let final_step = ln_end - ln_begin; let final_step = ln_end - ln_begin;
let max_digit = ln_end.to_string().len();
let offset = format!("{} {} ", &" ".repeat(max_digit), chars.vbreak);
for (i, lineno) in (ln_begin..=ln_end).enumerate() { for (i, lineno) in (ln_begin..=ln_end).enumerate() {
let mut pointer = " ".repeat(lineno.to_string().len() + 2); // +2 means `| ` context.push_str_with_color(
&format!("{:<max_digit$} {vbar} ", lineno, vbar = chars.vbar),
gutter_color,
);
context.push_str(&codes[i]);
context.push_str("\n");
context.push_str_with_color(&offset, gutter_color);
if i == 0 && i == final_step { if i == 0 && i == final_step {
pointer += &" ".repeat(col_begin); context.push_str(&" ".repeat(col_begin));
pointer += &"^".repeat(cmp::max(1, col_end.saturating_sub(col_begin))); context.push_str_with_color(
&mark.repeat(cmp::max(1, col_end.saturating_sub(col_begin))),
err_color,
);
} else if i == 0 { } else if i == 0 {
pointer += &" ".repeat(col_begin); context.push_str(&" ".repeat(col_begin));
pointer += &"^".repeat(cmp::max(1, codes[i].len().saturating_sub(col_begin))); context.push_str_with_color(
&mark.repeat(cmp::max(1, codes[i].len().saturating_sub(col_begin))),
err_color,
);
} else if i == final_step { } else if i == final_step {
pointer += &"^".repeat(col_end); context.push_str_with_color(&mark.repeat(col_end), err_color);
} else { } else {
pointer += &"^".repeat(cmp::max(1, codes[i].len())); context.push_str_with_color(&mark.repeat(cmp::max(1, codes[i].len())), err_color);
} }
writeln!( context.push_str("\n");
res,
"{lineno}{VBAR_UNICODE} {code}\n{pointer}",
code = codes.get(i).unwrap_or(&String::new()),
)
.unwrap();
} }
res + RESET 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()
} }
/// format: /// format:
@ -435,6 +460,8 @@ fn format_code_and_pointer<E: ErrorDisplay + ?Sized>(
pub trait ErrorDisplay { pub trait ErrorDisplay {
fn core(&self) -> &ErrorCore; fn core(&self) -> &ErrorCore;
fn input(&self) -> &Input; fn input(&self) -> &Input;
/// Colors and indication char for each type(error, warning, exception)
fn theme(&self) -> &Theme;
/// The block name the error caused. /// The block name the error caused.
/// This will be None if the error occurred before semantic analysis. /// This will be None if the error occurred before semantic analysis.
/// As for the internal error, do not put the fn name here. /// As for the internal error, do not put the fn name here.
@ -457,26 +484,85 @@ pub trait ErrorDisplay {
} }
fn show(&self) -> String { 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 = StringSpan::new(
&theme.error_kind_format(kind, self.core().errno),
Some(color),
Some(Attribute::Bold),
);
if let Some(hint) = self.core().hint.as_ref() {
let (hint_color, _) = theme.hint();
let mut hints = StringSpans::default();
hints.push_str_with_color_and_attribute("hint: ", hint_color, Attribute::Bold);
hints.push_str(hint);
format!( format!(
"{}{}{}: {}{}\n", "\
self.format_header(), {}
self.format_code_and_pointer(), {}{}: {}
{}
",
self.format_header(kind),
self.format_code_and_pointer((color, gutter_color), mark, chars),
self.core().kind, self.core().kind,
self.core().desc, self.core().desc,
fmt_option!(pre format!("\n{GREEN}hint{RESET}: "), self.core().hint) hints,
) )
} else {
format!(
"\
{}
{}{}: {}
",
self.format_header(kind),
self.format_code_and_pointer((color, gutter_color), mark, chars),
self.core().kind,
self.core().desc,
)
}
} }
/// for fmt::Display /// for fmt::Display
fn format(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 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 (hint, _) = theme.hint();
let (gutter_color, chars) = theme.characters();
let context = self.format_code_and_pointer((color, gutter_color), mark, chars);
let kind = StringSpan::new(
&theme.error_kind_format(kind, self.core().errno),
Some(color),
Some(Attribute::Bold),
);
writeln!( writeln!(
f, f,
"{}{}{}: {}{}", "\
self.format_header(), {}
self.format_code_and_pointer(), {}{}: {}
{}",
self.format_header(kind),
context,
self.core().kind, self.core().kind,
self.core().desc, self.core().desc,
fmt_option!(pre format!("\n{GREEN}hint{RESET}: "), self.core().hint), fmt_option!(pre StrSpan::new("hint: ", Some(hint), Some(Attribute::Bold)), self.core().hint)
)?; )?;
if let Some(inner) = self.ref_inner() { if let Some(inner) = self.ref_inner() {
inner.format(f) inner.format(f)
@ -485,14 +571,7 @@ pub trait ErrorDisplay {
} }
} }
fn format_header(&self) -> String { fn format_header(&self, kind: StringSpan) -> String {
let (color, err_or_warn) = if self.core().kind.is_warning() {
(YELLOW, "Warning")
} else if self.core().kind.is_exception() {
("", "Exception")
} else {
(RED, "Error")
};
let loc = match self.core().loc { let loc = match self.core().loc {
Location::Range { Location::Range {
ln_begin, ln_end, .. ln_begin, ln_end, ..
@ -515,13 +594,17 @@ pub trait ErrorDisplay {
"".to_string() "".to_string()
}; };
format!( format!(
"{color}{err_or_warn}[#{errno:>04}]{RESET}: File {input}{loc}{caused_by}\n", "{kind}: File {input}{loc}{caused_by}\n",
errno = self.core().errno,
input = self.input().enclosed_name(), input = self.input().enclosed_name(),
) )
} }
fn format_code_and_pointer(&self) -> String { fn format_code_and_pointer(
&self,
colors: (Color, Color),
mark: char,
chars: &Characters,
) -> String {
match self.core().loc { match self.core().loc {
Location::RangePair { Location::RangePair {
ln_first, ln_first,
@ -529,13 +612,25 @@ pub trait ErrorDisplay {
ln_second, ln_second,
col_second, col_second,
} => { } => {
format_code_and_pointer(self, ln_first.0, ln_first.1, col_first.0, col_first.1) format_context(
+ &format_code_and_pointer( self,
ln_first.0,
ln_first.1,
col_first.0,
col_first.1,
colors,
chars,
mark,
) +
+ &format_context(
self, self,
ln_second.0, ln_second.0,
ln_second.1, ln_second.1,
col_second.0, col_second.0,
col_second.1, col_second.1,
colors,
chars,
mark,
) )
} }
Location::Range { Location::Range {
@ -543,40 +638,49 @@ pub trait ErrorDisplay {
col_begin, col_begin,
ln_end, ln_end,
col_end, col_end,
} => format_code_and_pointer(self, ln_begin, ln_end, col_begin, col_end), } => format_context(
self, ln_begin, ln_end, col_begin, col_end, colors, chars, mark,
),
Location::LineRange(ln_begin, ln_end) => { Location::LineRange(ln_begin, ln_end) => {
let (err_color, gutter_color) = colors;
let mut cxt = StringSpans::default();
let codes = if self.input().is_repl() { let codes = if self.input().is_repl() {
vec![self.input().reread()] vec![self.input().reread()]
} else { } else {
self.input().reread_lines(ln_begin, ln_end) self.input().reread_lines(ln_begin, ln_end)
}; };
let mut res = CYAN.to_string(); let mark = mark.to_string();
for (i, lineno) in (ln_begin..=ln_end).enumerate() { for (i, lineno) in (ln_begin..=ln_end).enumerate() {
let mut pointer = " ".repeat(lineno.to_string().len() + 2); // +2 means `| ` cxt.push_str_with_color(&format!("{lineno} {}", chars.vbar), err_color);
pointer += &"^".repeat(cmp::max(1, codes[i].len())); cxt.push_str(&codes[i]);
writeln!( cxt.push_str(&" ".repeat(lineno.to_string().len() + 3)); // +3 means ` | `
res, cxt.push_str_with_color(&mark.repeat(cmp::max(1, codes[i].len())), gutter_color)
"{lineno}{VBAR_UNICODE} {code}\n{pointer}",
code = codes[i]
)
.unwrap();
} }
res + RESET cxt.to_string()
} }
Location::Line(lineno) => { Location::Line(lineno) => {
let (_, gutter_color) = colors;
let code = if self.input().is_repl() { let code = if self.input().is_repl() {
self.input().reread() self.input().reread()
} else { } else {
self.input().reread_lines(lineno, lineno).remove(0) self.input().reread_lines(lineno, lineno).remove(0)
}; };
format!("{CYAN}{lineno}{VBAR_UNICODE} {code}\n{RESET}") let mut cxt = StringSpans::default();
cxt.push_str_with_color(&format!(" {lineno} {} ", chars.vbar), gutter_color);
cxt.push_str(&code);
cxt.push_str("\n");
cxt.to_string()
} }
Location::Unknown => match self.input() { Location::Unknown => match self.input() {
Input::File(_) => "\n".to_string(), Input::File(_) => "\n".to_string(),
other => format!(
"{CYAN}?{VBAR_UNICODE} {code}\n{RESET}", other => {
code = other.reread() let (_, gutter_color) = colors;
), let mut cxt = StringSpans::default();
cxt.push_str_with_color(&format!(" ? {}", chars.vbar), gutter_color);
cxt.push_str(&other.reread());
cxt.to_string()
}
}, },
} }
} }

View file

@ -4,7 +4,7 @@ use erg_common::astr::AtomicStr;
use erg_common::config::Input; use erg_common::config::Input;
use erg_common::error::{ErrorCore, ErrorDisplay, ErrorKind::*, Location, MultiErrorDisplay}; use erg_common::error::{ErrorCore, ErrorDisplay, ErrorKind::*, Location, MultiErrorDisplay};
use erg_common::set::Set; use erg_common::set::Set;
use erg_common::style::{GREEN, RED, RESET, YELLOW}; use erg_common::style::{Attribute, Color, StrSpan, StringSpan, Theme, THEME};
use erg_common::traits::{Locational, Stream}; use erg_common::traits::{Locational, Stream};
use erg_common::vis::Visibility; use erg_common::vis::Visibility;
use erg_common::{ use erg_common::{
@ -112,6 +112,7 @@ pub struct CompileError {
pub core: Box<ErrorCore>, // ErrorCore is large, so box it pub core: Box<ErrorCore>, // ErrorCore is large, so box it
pub input: Input, pub input: Input,
pub caused_by: AtomicStr, pub caused_by: AtomicStr,
pub theme: Theme,
} }
impl_display_and_error!(CompileError); impl_display_and_error!(CompileError);
@ -122,6 +123,7 @@ impl From<ParserRunnerError> for CompileError {
core: Box::new(err.core), core: Box::new(err.core),
input: err.input, input: err.input,
caused_by: "".into(), caused_by: "".into(),
theme: THEME,
} }
} }
} }
@ -133,6 +135,9 @@ impl ErrorDisplay for CompileError {
fn input(&self) -> &Input { fn input(&self) -> &Input {
&self.input &self.input
} }
fn theme(&self) -> &Theme {
&self.theme
}
fn caused_by(&self) -> &str { fn caused_by(&self) -> &str {
&self.caused_by &self.caused_by
} }
@ -141,12 +146,19 @@ impl ErrorDisplay for CompileError {
} }
} }
const URL: StrSpan = StrSpan::new(
"https://github.com/erg-lang/erg",
Some(Color::White),
Some(Attribute::Underline),
);
impl CompileError { impl CompileError {
pub fn new(core: ErrorCore, input: Input, caused_by: AtomicStr) -> Self { pub fn new(core: ErrorCore, input: Input, caused_by: AtomicStr) -> Self {
Self { Self {
core: Box::new(core), core: Box::new(core),
input, input,
caused_by, caused_by,
theme: THEME,
} }
} }
@ -163,10 +175,10 @@ impl CompileError {
CompilerSystemError, CompilerSystemError,
loc, loc,
switch_lang!( switch_lang!(
"japanese" => format!("これはErg compilerのバグです、開発者に報告して下さい (https://github.com/erg-lang/erg)\n{fn_name}:{line}より発生"), "japanese" => format!("これはErg compilerのバグです、開発者に報告して下さい ({URL})\n\n{fn_name}:{line}より発生"),
"simplified_chinese" => format!("这是Erg编译器的错误请报告给https://github.com/erg-lang/erg\n原因来自: {fn_name}:{line}"), "simplified_chinese" => format!("这是Erg编译器的错误请报告给{URL}\n\n原因来自: {fn_name}:{line}"),
"traditional_chinese" => format!("這是Erg編譯器的錯誤請報告給https://github.com/erg-lang/erg\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 https://github.com/erg-lang/erg\ncaused from: {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, None,
), ),
@ -189,16 +201,16 @@ impl CompileError {
loc, loc,
switch_lang!( switch_lang!(
"japanese" => format!("スタックの要素数が異常です (要素数: {stack_len}, ブロックID: {block_id})\n\ "japanese" => format!("スタックの要素数が異常です (要素数: {stack_len}, ブロックID: {block_id})\n\
(https://github.com/erg-lang/erg)\n\ ({URL})\n\
{fn_name}"), {fn_name}"),
"simplified_chinese" => format!("堆栈中的元素数无效(元素数: {stack_len}块id: {block_id}\n\ "simplified_chinese" => format!("堆栈中的元素数无效(元素数: {stack_len}块id: {block_id}\n\
Erg (https://github.com/erg-lang/erg)\n\ Erg ({URL})\n\
: {fn_name}"), : {fn_name}"),
"traditional_chinese" => format!("堆棧中的元素數無效(元素數: {stack_len}塊id: {block_id}\n\ "traditional_chinese" => format!("堆棧中的元素數無效(元素數: {stack_len}塊id: {block_id}\n\
Erg (https://github.com/erg-lang/erg)\n\ Erg ({URL})\n\
: {fn_name}"), : {fn_name}"),
"english" => format!("the number of elements in the stack is invalid (num of elems: {stack_len}, block id: {block_id})\n\ "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 (https://github.com/erg-lang/erg)\n\ this is a bug of the Erg compiler, please report it ({URL})\n\
caused from: {fn_name}"), caused from: {fn_name}"),
), ),
None, None,
@ -249,6 +261,10 @@ impl CompileError {
pub type TyCheckError = CompileError; pub type TyCheckError = CompileError;
const ERR: Color = THEME.colors.error;
const WARNING: Color = THEME.colors.warning;
const HINT: Color = THEME.colors.hint;
impl TyCheckError { impl TyCheckError {
pub fn dummy(input: Input, errno: usize) -> Self { pub fn dummy(input: Input, errno: usize) -> Self {
Self::new(ErrorCore::dummy(errno), input, "".into()) Self::new(ErrorCore::dummy(errno), input, "".into())
@ -271,10 +287,10 @@ impl TyCheckError {
CompilerSystemError, CompilerSystemError,
loc, loc,
switch_lang!( switch_lang!(
"japanese" => format!("これはErg compilerのバグです、開発者に報告して下さい (https://github.com/erg-lang/erg)\n{fn_name}:{line}より発生"), "japanese" => format!("これはErg compilerのバグです、開発者に報告して下さい ({URL})\n\n{fn_name}:{line}より発生"),
"simplified_chinese" => format!("这是Erg编译器的错误请报告给https://github.com/erg-lang/erg\n原因来自: {fn_name}:{line}"), "simplified_chinese" => format!("这是Erg编译器的错误请报告给{URL}\n\n原因来自: {fn_name}:{line}"),
"traditional_chinese" => format!("這是Erg編譯器的錯誤請報告給https://github.com/erg-lang/erg\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 https://github.com/erg-lang/erg\ncaused from: {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, None,
), ),
@ -361,16 +377,23 @@ impl TyCheckError {
), ),
None => "".into(), None => "".into(),
}; };
let name = StringSpan::new(
&format!("{}{}", name, ord),
Some(WARNING),
Some(Attribute::Bold),
);
let expect = StringSpan::new(&format!("{}", expect), Some(HINT), Some(Attribute::Bold));
let found = StringSpan::new(&format!("{}", found), Some(ERR), Some(Attribute::Bold));
Self::new( Self::new(
ErrorCore::new( ErrorCore::new(
errno, errno,
TypeError, TypeError,
loc, loc,
switch_lang!( switch_lang!(
"japanese" => format!("{YELLOW}{name}{ord}{RESET}の型が違います。\n予期した型: {GREEN}{expect}{RESET}\n与えられた型: {RED}{found}{RESET}\n{}", fmt_option_map!(pre "与えられた型の単一化候補:\n", candidates, |x: &Set<Type>| x.folded_display())), "japanese" => format!("{name}の型が違います\n\n予期した型: {expect}\n与えられた型: {found}{}", fmt_option_map!(pre "\n与えられた型の単一化候補: ", candidates, |x: &Set<Type>| x.folded_display())),
"simplified_chinese" => format!("{YELLOW}{name}{ord}{RESET}的类型不匹配: \n预期: {GREEN}{expect}{RESET}\n但找到: {RED}{found}{RESET}\n{}", fmt_option_map!(pre "某一类型的统一候选: \n", candidates, |x: &Set<Type>| x.folded_display())), "simplified_chinese" => format!("{name}的类型不匹配\n\n预期: {expect}\n但找到: {found}{}", fmt_option_map!(pre "\n某一类型的统一候选: ", candidates, |x: &Set<Type>| x.folded_display())),
"traditional_chinese" => format!("{YELLOW}{name}{ord}{RESET}的類型不匹配: \n預期: {GREEN}{expect}{RESET}\n但找到: {RED}{found}{RESET}\n{}", fmt_option_map!(pre "某一類型的統一候選\n", candidates, |x: &Set<Type>| x.folded_display())), "traditional_chinese" => format!("{name}的類型不匹配\n\n預期: {expect}\n但找到: {found}{}", fmt_option_map!(pre "\n某一類型的統一候選: ", candidates, |x: &Set<Type>| x.folded_display())),
"english" => format!("the type of {YELLOW}{name}{ord}{RESET} is mismatched:\nexpected: {GREEN}{expect}{RESET}\nbut found: {RED}{found}{RESET}\n{}", fmt_option_map!(pre "unification candidates of a given type:\n", candidates, |x: &Set<Type>| 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<Type>| x.folded_display())),
), ),
hint, hint,
), ),
@ -388,16 +411,18 @@ impl TyCheckError {
expect: &Type, expect: &Type,
found: &Type, found: &Type,
) -> Self { ) -> Self {
let expect = StringSpan::new(&format!("{}", expect), Some(HINT), Some(Attribute::Bold));
let found = StringSpan::new(&format!("{}", found), Some(ERR), Some(Attribute::Bold));
Self::new( Self::new(
ErrorCore::new( ErrorCore::new(
errno, errno,
TypeError, TypeError,
loc, loc,
switch_lang!( switch_lang!(
"japanese" => format!("{name}の戻り値の型が違います\n予期した型: {GREEN}{expect}{RESET}\n与えられた型: {RED}{found}{RESET}"), "japanese" => format!("{name}の戻り値の型が違います\n\n予期した型: {expect}\n与えられた型: {found}"),
"simplified_chinese" => format!("{name}的返回类型不匹配: \n预期: {GREEN}{expect}{RESET}\n但找到: {RED}{found}{RESET}"), "simplified_chinese" => format!("{name}的返回类型不匹配\n\n预期: {expect}\n但找到: {found}"),
"traditional_chinese" => format!("{name}的返回類型不匹配: \n預期: {GREEN}{expect}{RESET}\n但找到: {RED}{found}{RESET}"), "traditional_chinese" => format!("{name}的返回類型不匹配\n\n預期: {expect}\n但找到: {found}"),
"english" => format!("the return type of {name} is mismatched:\nexpected: {GREEN}{expect}{RESET}\nbut found: {RED}{found}{RESET}"), "english" => format!("the return type of {name} is mismatched\n\nexpected: {expect}\nbut found: {found}"),
), ),
None, None,
), ),
@ -440,16 +465,18 @@ impl TyCheckError {
expect: usize, expect: usize,
found: usize, found: usize,
) -> Self { ) -> Self {
let expect = StringSpan::new(&format!("{}", expect), Some(HINT), Some(Attribute::Bold));
let found = StringSpan::new(&format!("{}", found), Some(ERR), Some(Attribute::Bold));
Self::new( Self::new(
ErrorCore::new( ErrorCore::new(
errno, errno,
TypeError, TypeError,
loc, loc,
switch_lang!( switch_lang!(
"japanese" => format!("ポジショナル引数の数が違います\n予期した個数: {GREEN}{expect}{RESET}\n与えられた個数: {RED}{found}{RESET}"), "japanese" => format!("ポジショナル引数の数が違います\n\n予期した個数: {expect}\n与えられた個数: {found}"),
"simplified_chinese" => format!("正则参数的数量不匹配: \n预期: {GREEN}{expect}{RESET}\n但找到: {RED}{found}{RESET}"), "simplified_chinese" => format!("正则参数的数量不匹配\n\n预期: {expect}\n但找到: {found}"),
"traditional_chinese" => format!("正則參數的數量不匹配: \n預期: {GREEN}{expect}{RESET}\n但找到: {RED}{found}{RESET}"), "traditional_chinese" => format!("正則參數的數量不匹配\n\n預期: {expect}\n但找到: {found}"),
"english" => format!("the number of positional arguments is mismatched:\nexpected: {GREEN}{expect}{RESET}\nbut found: {RED}{found}{RESET}"), "english" => format!("the number of positional arguments is mismatched\n\nexpected: {expect}\nbut found: {found}"),
), ),
None, None,
), ),
@ -528,6 +555,21 @@ impl TyCheckError {
kw_args_len: usize, kw_args_len: usize,
) -> Self { ) -> Self {
let name = readable_name(callee_name); let name = readable_name(callee_name);
let expect = StringSpan::new(
&format!("{}", params_len),
Some(HINT),
Some(Attribute::Bold),
);
let pos_args_len = StringSpan::new(
&format!("{}", pos_args_len),
Some(ERR),
Some(Attribute::Bold),
);
let kw_args_len = StringSpan::new(
&format!("{}", kw_args_len),
Some(ERR),
Some(Attribute::Bold),
);
Self::new( Self::new(
ErrorCore::new( ErrorCore::new(
errno, errno,
@ -536,27 +578,29 @@ impl TyCheckError {
switch_lang!( switch_lang!(
"japanese" => format!( "japanese" => format!(
"{name}に渡された引数の数が多すぎます "{name}に渡された引数の数が多すぎます
: {GREEN}{params_len}{RESET}
: {RED}{pos_args_len}{RESET} : {expect}
: {RED}{kw_args_len}{RESET}" : {pos_args_len}
: {kw_args_len}"
), ),
"simplified_chinese" => format!("传递给{name}的参数过多 "simplified_chinese" => format!("传递给{name}的参数过多
: {GREEN}{params_len}{RESET}
: {RED}{pos_args_len}{RESET} : {expect}
: {RED}{kw_args_len}{RESET} : {pos_args_len}
" : {kw_args_len}"
), ),
"traditional_chinese" => format!("傳遞給{name}的參數過多 "traditional_chinese" => format!("傳遞給{name}的參數過多
: {GREEN}{params_len}{RESET}
: {RED}{pos_args_len}{RESET} : {expect}
: {RED}{kw_args_len}{RESET} : {pos_args_len}
" : {kw_args_len}"
), ),
"english" => format!( "english" => format!(
"too many arguments for {name}: "too many arguments for {name}
total expected params: {GREEN}{params_len}{RESET}
passed positional args: {RED}{pos_args_len}{RESET} total expected params: {expect}
passed keyword args: {RED}{kw_args_len}{RESET}" passed positional args: {pos_args_len}
passed keyword args: {kw_args_len}"
), ),
), ),
None, None,
@ -576,16 +620,21 @@ passed keyword args: {RED}{kw_args_len}{RESET}"
missing_params: Vec<Str>, missing_params: Vec<Str>,
) -> Self { ) -> Self {
let name = readable_name(callee_name); let name = readable_name(callee_name);
let vec_cxt = StringSpan::new(
&fmt_vec(&missing_params),
Some(WARNING),
Some(Attribute::Bold),
);
Self::new( Self::new(
ErrorCore::new( ErrorCore::new(
errno, errno,
TypeError, TypeError,
loc, loc,
switch_lang!( switch_lang!(
"japanese" => format!("{name}に渡された引数が{missing_len}個足りません({YELLOW}{}{RESET})。", fmt_vec(&missing_params)), "japanese" => format!("{name}に渡された引数が{missing_len}個足りません({vec_cxt})" ),
"simplified_chinese" => format!("{name}{missing_len}个位置参数不被传递。({YELLOW}{}{RESET})。", fmt_vec(&missing_params)), "simplified_chinese" => format!("{name}{missing_len}个位置参数不被传递({vec_cxt})"),
"traditional_chinese" => format!("{name}{missing_len}個位置參數不被傳遞。({YELLOW}{}{RESET})。", fmt_vec(&missing_params)), "traditional_chinese" => format!("{name}{missing_len}個位置參數不被傳遞({vec_cxt})"),
"english" => format!("missing {missing_len} positional argument(s) for {name}: {YELLOW}{}{RESET}", fmt_vec(&missing_params)), "english" => format!("missing {missing_len} positional argument(s) for {name}: {vec_cxt}"),
), ),
None, None,
), ),
@ -603,16 +652,17 @@ passed keyword args: {RED}{kw_args_len}{RESET}"
arg_name: &str, arg_name: &str,
) -> Self { ) -> Self {
let name = readable_name(callee_name); let name = readable_name(callee_name);
let found = StringSpan::new(arg_name, Some(ERR), Some(Attribute::Bold));
Self::new( Self::new(
ErrorCore::new( ErrorCore::new(
errno, errno,
TypeError, TypeError,
loc, loc,
switch_lang!( switch_lang!(
"japanese" => format!("{name}の引数{RED}{arg_name}{RESET}が複数回渡されています"), "japanese" => format!("{name}の引数{found}が複数回渡されています"),
"simplified_chinese" => format!("{name}的参数{RED}{arg_name}{RESET}被多次传递"), "simplified_chinese" => format!("{name}的参数{found}被多次传递"),
"traditional_chinese" => format!("{name}的參數{RED}{arg_name}{RESET}被多次傳遞"), "traditional_chinese" => format!("{name}的參數{found}被多次傳遞"),
"english" => format!("{name}'s argument {RED}{arg_name}{RESET} is passed multiple times"), "english" => format!("{name}'s argument {found} is passed multiple times"),
), ),
None, None,
), ),
@ -630,16 +680,17 @@ passed keyword args: {RED}{kw_args_len}{RESET}"
param_name: &str, param_name: &str,
) -> Self { ) -> Self {
let name = readable_name(callee_name); let name = readable_name(callee_name);
let found = StringSpan::new(param_name, Some(ERR), Some(Attribute::Bold));
Self::new( Self::new(
ErrorCore::new( ErrorCore::new(
errno, errno,
TypeError, TypeError,
loc, loc,
switch_lang!( switch_lang!(
"japanese" => format!("{name}には予期しないキーワード引数{RED}{param_name}{RESET}が渡されています"), "japanese" => format!("{name}には予期しないキーワード引数{found}が渡されています"),
"simplified_chinese" => format!("{name}得到了意外的关键字参数{RED}{param_name}{RESET}"), "simplified_chinese" => format!("{name}得到了意外的关键字参数{found}"),
"traditional_chinese" => format!("{name}得到了意外的關鍵字參數{RED}{param_name}{RESET}"), "traditional_chinese" => format!("{name}得到了意外的關鍵字參數{found}"),
"english" => format!("{name} got unexpected keyword argument {RED}{param_name}{RESET}"), "english" => format!("{name} got unexpected keyword argument {found}"),
), ),
None, None,
), ),
@ -656,16 +707,18 @@ passed keyword args: {RED}{kw_args_len}{RESET}"
loc: Location, loc: Location,
caused_by: AtomicStr, caused_by: AtomicStr,
) -> Self { ) -> Self {
let lhs_t = StringSpan::new(&format!("{}", lhs_t), Some(WARNING), Some(Attribute::Bold));
let rhs_t = StringSpan::new(&format!("{}", rhs_t), Some(WARNING), Some(Attribute::Bold));
Self::new( Self::new(
ErrorCore::new( ErrorCore::new(
errno, errno,
TypeError, TypeError,
loc, loc,
switch_lang!( switch_lang!(
"japanese" => format!("型の単一化に失敗しました:\n左辺: {YELLOW}{lhs_t}{RESET}\n右辺: {YELLOW}{rhs_t}{RESET}"), "japanese" => format!("型の単一化に失敗しました\n\n左辺: {lhs_t}\n右辺: {rhs_t}"),
"simplified_chinese" => format!("类型统一失败: \n左边: {YELLOW}{lhs_t}{RESET}\n右边: {YELLOW}{rhs_t}{RESET}"), "simplified_chinese" => format!("类型统一失败\n\n左边: {lhs_t}\n右边: {rhs_t}"),
"traditional_chinese" => format!("類型統一失敗: \n左邊: {YELLOW}{lhs_t}{RESET}\n右邊: {YELLOW}{rhs_t}{RESET}"), "traditional_chinese" => format!("類型統一失敗\n\n左邊: {lhs_t}\n右邊: {rhs_t}"),
"english" => format!("unification failed:\nlhs: {YELLOW}{lhs_t}{RESET}\nrhs: {YELLOW}{rhs_t}{RESET}"), "english" => format!("unification failed\n\nlhs: {lhs_t}\nrhs: {rhs_t}"),
), ),
None, None,
), ),
@ -682,16 +735,18 @@ passed keyword args: {RED}{kw_args_len}{RESET}"
loc: Location, loc: Location,
caused_by: AtomicStr, caused_by: AtomicStr,
) -> Self { ) -> Self {
let lhs_t = StringSpan::new(&format!("{}", lhs_t), Some(WARNING), Some(Attribute::Bold));
let rhs_t = StringSpan::new(&format!("{}", rhs_t), Some(WARNING), Some(Attribute::Bold));
Self::new( Self::new(
ErrorCore::new( ErrorCore::new(
errno, errno,
TypeError, TypeError,
loc, loc,
switch_lang!( switch_lang!(
"japanese" => format!("型の再単一化に失敗しました:\n左辺: {YELLOW}{lhs_t}{RESET}\n右辺: {YELLOW}{rhs_t}{RESET}"), "japanese" => format!("型の再単一化に失敗しました\n\n左辺: {lhs_t}\n右辺: {rhs_t}"),
"simplified_chinese" => format!("重新统一类型失败: \n左边: {YELLOW}{lhs_t}{RESET}\n右边: {YELLOW}{rhs_t}{RESET}"), "simplified_chinese" => format!("重新统一类型失败\n\n左边: {lhs_t}\n右边: {rhs_t}"),
"traditional_chinese" => format!("重新統一類型失敗: \n左邊: {YELLOW}{lhs_t}{RESET}\n右邊: {YELLOW}{rhs_t}{RESET}"), "traditional_chinese" => format!("重新統一類型失敗\n\n左邊: {lhs_t}\n右邊: {rhs_t}"),
"english" => format!("re-unification failed:\nlhs: {YELLOW}{lhs_t}{RESET}\nrhs: {YELLOW}{rhs_t}{RESET}"), "english" => format!("re-unification failed\n\nlhs: {lhs_t}\nrhs: {rhs_t}"),
), ),
None, None,
), ),
@ -708,16 +763,18 @@ passed keyword args: {RED}{kw_args_len}{RESET}"
loc: Location, loc: Location,
caused_by: AtomicStr, caused_by: AtomicStr,
) -> Self { ) -> Self {
let sub_t = StringSpan::new(&format!("{}", sub_t), Some(WARNING), Some(Attribute::Bold));
let sup_t = StringSpan::new(&format!("{}", sup_t), Some(WARNING), Some(Attribute::Bold));
Self::new( Self::new(
ErrorCore::new( ErrorCore::new(
errno, errno,
TypeError, TypeError,
loc, loc,
switch_lang!( switch_lang!(
"japanese" => format!("この式の部分型制約を満たせません:\nサブタイプ: {YELLOW}{sub_t}{RESET}\nスーパータイプ: {YELLOW}{sup_t}{RESET}"), "japanese" => format!("この式の部分型制約を満たせません\n\nサブタイプ: {sub_t}\nスーパータイプ: {sup_t}"),
"simplified_chinese" => format!("无法满足此表达式中的子类型约束: \n子类型: {YELLOW}{sub_t}{RESET}\n超类型: {YELLOW}{sup_t}{RESET}"), "simplified_chinese" => format!("无法满足此表达式中的子类型约束\n\n子类型: {sub_t}\n超类型: {sup_t}"),
"traditional_chinese" => format!("無法滿足此表達式中的子類型約束: \n子類型: {YELLOW}{sub_t}{RESET}\n超類型: {YELLOW}{sup_t}{RESET}"), "traditional_chinese" => format!("無法滿足此表達式中的子類型約束\n\n子類型: {sub_t}\n超類型: {sup_t}"),
"english" => format!("the subtype constraint in this expression cannot be satisfied:\nsubtype: {YELLOW}{sub_t}{RESET}\nsupertype: {YELLOW}{sup_t}{RESET}"), "english" => format!("the subtype constraint in this expression cannot be satisfied:\nsubtype: {sub_t}\nsupertype: {sup_t}"),
), ),
None, None,
), ),
@ -733,16 +790,18 @@ passed keyword args: {RED}{kw_args_len}{RESET}"
rhs: &Predicate, rhs: &Predicate,
caused_by: AtomicStr, caused_by: AtomicStr,
) -> Self { ) -> Self {
let lhs = StringSpan::new(&format!("{}", lhs), Some(WARNING), Some(Attribute::Bold));
let rhs = StringSpan::new(&format!("{}", rhs), Some(WARNING), Some(Attribute::Bold));
Self::new( Self::new(
ErrorCore::new( ErrorCore::new(
errno, errno,
TypeError, TypeError,
Location::Unknown, Location::Unknown,
switch_lang!( switch_lang!(
"japanese" => format!("述語式の単一化に失敗しました:\n左辺: {YELLOW}{lhs}{RESET}\n右辺: {YELLOW}{rhs}{RESET}"), "japanese" => format!("述語式の単一化に失敗しました\n\n左辺: {lhs}\n右辺: {rhs}"),
"simplified_chinese" => format!("无法统一谓词表达式: \n左边: {YELLOW}{lhs}{RESET}\n左边: {YELLOW}{rhs}{RESET}"), "simplified_chinese" => format!("无法统一谓词表达式\n\n左边: {lhs}\n左边: {rhs}"),
"traditional_chinese" => format!("無法統一謂詞表達式: \n左邊: {YELLOW}{lhs}{RESET}\n左邊: {YELLOW}{rhs}{RESET}"), "traditional_chinese" => format!("無法統一謂詞表達式\n\n左邊: {lhs}\n左邊: {rhs}"),
"english" => format!("predicate unification failed:\nlhs: {YELLOW}{lhs}{RESET}\nrhs: {YELLOW}{rhs}{RESET}"), "english" => format!("predicate unification failed\n\nlhs: {lhs}\nrhs: {rhs}"),
), ),
None, None,
), ),
@ -812,6 +871,7 @@ passed keyword args: {RED}{kw_args_len}{RESET}"
name: &str, name: &str,
hint: Option<AtomicStr>, hint: Option<AtomicStr>,
) -> Self { ) -> Self {
let found = StringSpan::new(name, Some(ERR), Some(Attribute::Bold));
Self::new( Self::new(
ErrorCore::new( ErrorCore::new(
errno, errno,
@ -819,16 +879,16 @@ passed keyword args: {RED}{kw_args_len}{RESET}"
loc, loc,
switch_lang!( switch_lang!(
"japanese" => format!( "japanese" => format!(
"{RED}{name}{RESET}にメソッドを定義することはできません", "{found}にメソッドを定義することはできません",
), ),
"simplified_chinese" => format!( "simplified_chinese" => format!(
"{RED}{name}{RESET}不可定义方法", "{found}不可定义方法",
), ),
"traditional_chinese" => format!( "traditional_chinese" => format!(
"{RED}{name}{RESET}不可定義方法", "{found}不可定義方法",
), ),
"english" => format!( "english" => format!(
"cannot define methods for {RED}{name}{RESET}", "cannot define methods for {found}",
), ),
), ),
hint, hint,
@ -850,16 +910,19 @@ passed keyword args: {RED}{kw_args_len}{RESET}"
found: &Type, found: &Type,
hint: Option<AtomicStr>, hint: Option<AtomicStr>,
) -> Self { ) -> Self {
let member_name = StringSpan::new(member_name, Some(WARNING), Some(Attribute::Bold));
let expect = StringSpan::new(&format!("{}", expect), Some(HINT), Some(Attribute::Bold));
let found = StringSpan::new(&format!("{}", found), Some(ERR), Some(Attribute::Bold));
Self::new( Self::new(
ErrorCore::new( ErrorCore::new(
errno, errno,
TypeError, TypeError,
loc, loc,
switch_lang!( switch_lang!(
"japanese" => format!("{YELLOW}{member_name}{RESET}の型が違います。\n{trait_type}で宣言された型: {GREEN}{expect}{RESET}\n与えられた型: {RED}{found}{RESET}"), "japanese" => format!("{member_name}の型が違います\n\n{trait_type}で宣言された型: {expect}\n与えられた型: {found}"),
"simplified_chinese" => format!("{YELLOW}{member_name}{RESET}的类型不匹配: \n{trait_type}中声明的类型: {GREEN}{expect}{RESET}\n但找到: {RED}{found}{RESET}"), "simplified_chinese" => format!("{member_name}的类型不匹配\n\n{trait_type}中声明的类型: {expect}\n但找到: {found}"),
"traditional_chinese" => format!("{YELLOW}{member_name}{RESET}的類型不匹配: \n{trait_type}中聲明的類型: {GREEN}{expect}{RESET}\n但找到: {RED}{found}{RESET}"), "traditional_chinese" => format!("{member_name}的類型不匹配\n\n{trait_type}中聲明的類型: {expect}\n但找到: {found}"),
"english" => format!("the type of {YELLOW}{member_name}{RESET} is mismatched:\ndeclared in {trait_type}: {GREEN}{expect}{RESET}\nbut found: {RED}{found}{RESET}"), "english" => format!("the type of {member_name} is mismatched\n\ndeclared in {trait_type}: {expect}\nbut found: {found}"),
), ),
hint, hint,
), ),
@ -878,16 +941,17 @@ passed keyword args: {RED}{kw_args_len}{RESET}"
class_type: &Type, class_type: &Type,
hint: Option<AtomicStr>, hint: Option<AtomicStr>,
) -> Self { ) -> Self {
let member_name = StringSpan::new(member_name, Some(WARNING), Some(Attribute::Bold));
Self::new( Self::new(
ErrorCore::new( ErrorCore::new(
errno, errno,
TypeError, TypeError,
Location::Unknown, Location::Unknown,
switch_lang!( switch_lang!(
"japanese" => format!("{trait_type}{YELLOW}{member_name}{RESET}{class_type}で実装されていません"), "japanese" => format!("{trait_type}{member_name}{class_type}で実装されていません"),
"simplified_chinese" => format!("{trait_type}中的{YELLOW}{member_name}{RESET}没有在{class_type}中实现"), "simplified_chinese" => format!("{trait_type}中的{member_name}没有在{class_type}中实现"),
"traditional_chinese" => format!("{trait_type}中的{YELLOW}{member_name}{RESET}沒有在{class_type}中實現"), "traditional_chinese" => format!("{trait_type}中的{member_name}沒有在{class_type}中實現"),
"english" => format!("{YELLOW}{member_name}{RESET} of {trait_type} is not implemented in {class_type}"), "english" => format!("{member_name} of {trait_type} is not implemented in {class_type}"),
), ),
hint, hint,
), ),
@ -906,16 +970,17 @@ passed keyword args: {RED}{kw_args_len}{RESET}"
class_type: &Type, class_type: &Type,
hint: Option<AtomicStr>, hint: Option<AtomicStr>,
) -> Self { ) -> Self {
let member_name = StringSpan::new(member_name, Some(WARNING), Some(Attribute::Bold));
Self::new( Self::new(
ErrorCore::new( ErrorCore::new(
errno, errno,
TypeError, TypeError,
Location::Unknown, Location::Unknown,
switch_lang!( switch_lang!(
"japanese" => format!("{class_type}{YELLOW}{member_name}{RESET}{trait_type}で宣言されていません"), "japanese" => format!("{class_type}{member_name}{trait_type}で宣言されていません"),
"simplified_chinese" => format!("{class_type}中的{YELLOW}{member_name}{RESET}没有在{trait_type}中声明"), "simplified_chinese" => format!("{class_type}中的{member_name}没有在{trait_type}中声明"),
"traditional_chinese" => format!("{class_type}中的{YELLOW}{member_name}{RESET}沒有在{trait_type}中聲明"), "traditional_chinese" => format!("{class_type}中的{member_name}沒有在{trait_type}中聲明"),
"english" => format!("{YELLOW}{member_name}{RESET} of {class_type} is not declared in {trait_type}"), "english" => format!("{member_name} of {class_type} is not declared in {trait_type}"),
), ),
hint, hint,
), ),
@ -931,16 +996,17 @@ passed keyword args: {RED}{kw_args_len}{RESET}"
loc: Location, loc: Location,
caused_by: AtomicStr, caused_by: AtomicStr,
) -> Self { ) -> Self {
let found = StringSpan::new(name, Some(ERR), Some(Attribute::Bold));
Self::new( Self::new(
ErrorCore::new( ErrorCore::new(
errno, errno,
TypeError, TypeError,
loc, loc,
switch_lang!( switch_lang!(
"japanese" => format!("型変数{RED}{name}{RESET}が定義されていません"), "japanese" => format!("型変数{found}が定義されていません"),
"simplified_chinese" => format!("类型变量{RED}{name}{RESET}没有定义"), "simplified_chinese" => format!("类型变量{found}没有定义"),
"traditional_chinese" => format!("類型變量{RED}{name}{RESET}沒有定義"), "traditional_chinese" => format!("類型變量{found}沒有定義"),
"english" => format!("type variable {RED}{name}{RESET} is not defined"), "english" => format!("type variable {found} is not defined"),
), ),
None, None,
), ),
@ -962,10 +1028,10 @@ passed keyword args: {RED}{kw_args_len}{RESET}"
TypeError, TypeError,
expr.loc(), expr.loc(),
switch_lang!( switch_lang!(
"japanese" => format!("{expr}の型を一意に決定できませんでした\n候補: {}", fmt_vec(candidates)), "japanese" => format!("{expr}の型を一意に決定できませんでした\n\n候補: {}", fmt_vec(candidates)),
"simplified_chinese" => format!("无法确定{expr}的类型\n候选: {}", fmt_vec(candidates)), "simplified_chinese" => format!("无法确定{expr}的类型\n\n候选: {}", fmt_vec(candidates)),
"traditional_chinese" => format!("無法確定{expr}的類型\n候選: {}", fmt_vec(candidates)), "traditional_chinese" => format!("無法確定{expr}的類型\n\n候選: {}", fmt_vec(candidates)),
"english" => format!("cannot determine the type of {expr}\ncandidates: {}", fmt_vec(candidates)), "english" => format!("cannot determine the type of {expr}\n\ncandidates: {}", fmt_vec(candidates)),
), ),
Some( Some(
switch_lang!( switch_lang!(
@ -1111,6 +1177,7 @@ impl OwnershipError {
moved_loc: Location, moved_loc: Location,
caused_by: S, caused_by: S,
) -> Self { ) -> Self {
let found = StringSpan::new(name, Some(ERR), Some(Attribute::Bold));
Self::new( Self::new(
ErrorCore::new( ErrorCore::new(
errno, errno,
@ -1118,19 +1185,19 @@ impl OwnershipError {
name_loc, name_loc,
switch_lang!( switch_lang!(
"japanese" => format!( "japanese" => format!(
"{RED}{name}{RESET}は{}行目ですでに移動されています", "{found}は{}行目ですでに移動されています",
moved_loc.ln_begin().unwrap_or(0) moved_loc.ln_begin().unwrap_or(0)
), ),
"simplified_chinese" => format!( "simplified_chinese" => format!(
"{RED}{name}{RESET}已移至第{}行", "{found}已移至第{}行",
moved_loc.ln_begin().unwrap_or(0) moved_loc.ln_begin().unwrap_or(0)
), ),
"traditional_chinese" => format!( "traditional_chinese" => format!(
"{RED}{name}{RESET}已移至第{}行", "{found}已移至第{}行",
moved_loc.ln_begin().unwrap_or(0) moved_loc.ln_begin().unwrap_or(0)
), ),
"english" => format!( "english" => format!(
"{RED}{name}{RESET} was moved in line {}", "{found} was moved in line {}",
moved_loc.ln_begin().unwrap_or(0) moved_loc.ln_begin().unwrap_or(0)
), ),
), ),
@ -1227,16 +1294,18 @@ impl LowerError {
found_t: &Type, found_t: &Type,
) -> Self { ) -> Self {
let name = readable_name(name); let name = readable_name(name);
let expect = StringSpan::new(&format!("{}", spec_t), Some(HINT), Some(Attribute::Bold));
let found = StringSpan::new(&format!("{}", found_t), Some(ERR), Some(Attribute::Bold));
Self::new( Self::new(
ErrorCore::new( ErrorCore::new(
errno, errno,
TypeError, TypeError,
loc, loc,
switch_lang!( switch_lang!(
"japanese" => format!("{name}{GREEN}{spec_t}{RESET}型として宣言されましたが、{RED}{found_t}{RESET}型のオブジェクトが代入されています"), "japanese" => format!("{name}{expect}型として宣言されましたが、{found}型のオブジェクトが代入されています"),
"simplified_chinese" => format!("{name}被声明为{GREEN}{spec_t}{RESET},但分配了一个{RED}{found_t}{RESET}对象"), "simplified_chinese" => format!("{name}被声明为{expect},但分配了一个{found}对象"),
"traditional_chinese" => format!("{name}被聲明為{GREEN}{spec_t}{RESET},但分配了一個{RED}{found_t}{RESET}對象"), "traditional_chinese" => format!("{name}被聲明為{expect},但分配了一個{found}對象"),
"english" => format!("{name} was declared as {GREEN}{spec_t}{RESET}, but an {RED}{found_t}{RESET} object is assigned"), "english" => format!("{name} was declared as {expect}, but an {found} object is assigned"),
), ),
Option::<AtomicStr>::None, Option::<AtomicStr>::None,
), ),
@ -1263,16 +1332,17 @@ impl LowerError {
) )
.into() .into()
}); });
let found = StringSpan::new(name, Some(ERR), Some(Attribute::Bold));
Self::new( Self::new(
ErrorCore::new( ErrorCore::new(
errno, errno,
NameError, NameError,
loc, loc,
switch_lang!( switch_lang!(
"japanese" => format!("{RED}{name}{RESET}という変数は定義されていません"), "japanese" => format!("{found}という変数は定義されていません"),
"simplified_chinese" => format!("{RED}{name}{RESET}未定义"), "simplified_chinese" => format!("{found}未定义"),
"traditional_chinese" => format!("{RED}{name}{RESET}未定義"), "traditional_chinese" => format!("{found}未定義"),
"english" => format!("{RED}{name}{RESET} is not defined"), "english" => format!("{found} is not defined"),
), ),
hint, hint,
), ),
@ -1299,16 +1369,17 @@ impl LowerError {
) )
.into() .into()
}); });
let found = StringSpan::new(name, Some(ERR), Some(Attribute::Bold));
Self::new( Self::new(
ErrorCore::new( ErrorCore::new(
errno, errno,
AttributeError, AttributeError,
loc, loc,
switch_lang!( switch_lang!(
"japanese" => format!("{obj_t}型オブジェクトに{RED}{name}{RESET}という属性はありません"), "japanese" => format!("{obj_t}型オブジェクトに{found}という属性はありません"),
"simplified_chinese" => format!("{obj_t}对象没有属性{RED}{name}{RESET}"), "simplified_chinese" => format!("{obj_t}对象没有属性{found}"),
"traditional_chinese" => format!("{obj_t}對像沒有屬性{RED}{name}{RESET}"), "traditional_chinese" => format!("{obj_t}對像沒有屬性{found}"),
"english" => format!("{obj_t} object has no attribute {RED}{name}{RESET}"), "english" => format!("{obj_t} object has no attribute {found}"),
), ),
hint, hint,
), ),
@ -1337,16 +1408,17 @@ impl LowerError {
) )
.into() .into()
}); });
let found = StringSpan::new(name, Some(ERR), Some(Attribute::Bold));
Self::new( Self::new(
ErrorCore::new( ErrorCore::new(
errno, errno,
AttributeError, AttributeError,
loc, loc,
switch_lang!( switch_lang!(
"japanese" => format!("{obj_name}(: {obj_t})に{RED}{name}{RESET}という属性はありません"), "japanese" => format!("{obj_name}(: {obj_t})に{found}という属性はありません"),
"simplified_chinese" => format!("{obj_name}(: {obj_t})没有属性{RED}{name}{RESET}"), "simplified_chinese" => format!("{obj_name}(: {obj_t})没有属性{found}"),
"traditional_chinese" => format!("{obj_name}(: {obj_t})沒有屬性{RED}{name}{RESET}"), "traditional_chinese" => format!("{obj_name}(: {obj_t})沒有屬性{found}"),
"english" => format!("{obj_name}(: {obj_t}) has no attribute {RED}{name}{RESET}"), "english" => format!("{obj_name}(: {obj_t}) has no attribute {found}"),
), ),
hint, hint,
), ),
@ -1362,17 +1434,17 @@ impl LowerError {
caused_by: AtomicStr, caused_by: AtomicStr,
name: &str, name: &str,
) -> Self { ) -> Self {
let name = readable_name(name); let name = StringSpan::new(readable_name(name), Some(WARNING), Some(Attribute::Bold));
Self::new( Self::new(
ErrorCore::new( ErrorCore::new(
errno, errno,
AssignError, AssignError,
loc, loc,
switch_lang!( switch_lang!(
"japanese" => format!("変数{YELLOW}{name}{RESET}に複数回代入することはできません"), "japanese" => format!("変数{name}に複数回代入することはできません"),
"simplified_chinese" => format!("不能为变量{YELLOW}{name}{RESET}分配多次"), "simplified_chinese" => format!("不能为变量{name}分配多次"),
"traditional_chinese" => format!("不能為變量{YELLOW}{name}{RESET}分配多次"), "traditional_chinese" => format!("不能為變量{name}分配多次"),
"english" => format!("variable {YELLOW}{name}{RESET} cannot be assigned more than once"), "english" => format!("variable {name} cannot be assigned more than once"),
), ),
None, None,
), ),
@ -1388,17 +1460,17 @@ impl LowerError {
name: &str, name: &str,
caused_by: AtomicStr, caused_by: AtomicStr,
) -> Self { ) -> Self {
let name = readable_name(name); let name = StringSpan::new(readable_name(name), Some(WARNING), Some(Attribute::Bold));
Self::new( Self::new(
ErrorCore::new( ErrorCore::new(
errno, errno,
UnusedWarning, UnusedWarning,
loc, loc,
switch_lang!( switch_lang!(
"japanese" => format!("{YELLOW}{name}{RESET}は使用されていません"), "japanese" => format!("{name}は使用されていません"),
"simplified_chinese" => format!("{YELLOW}{name}{RESET}未使用"), "simplified_chinese" => format!("{name}未使用"),
"traditional_chinese" => format!("{YELLOW}{name}{RESET}未使用"), "traditional_chinese" => format!("{name}未使用"),
"english" => format!("{YELLOW}{name}{RESET} is not used"), "english" => format!("{name} is not used"),
), ),
None, None,
), ),
@ -1408,17 +1480,21 @@ impl LowerError {
} }
pub fn del_error(input: Input, errno: usize, ident: &Identifier, caused_by: AtomicStr) -> Self { pub fn del_error(input: Input, errno: usize, ident: &Identifier, caused_by: AtomicStr) -> Self {
let name = readable_name(ident.inspect()); let name = StringSpan::new(
readable_name(ident.inspect()),
Some(WARNING),
Some(Attribute::Bold),
);
Self::new( Self::new(
ErrorCore::new( ErrorCore::new(
errno, errno,
NameError, NameError,
ident.loc(), ident.loc(),
switch_lang!( switch_lang!(
"japanese" => format!("{YELLOW}{name}{RESET}は削除できません"), "japanese" => format!("{name}は削除できません"),
"simplified_chinese" => format!("{YELLOW}{name}{RESET}不能删除"), "simplified_chinese" => format!("{name}不能删除"),
"traditional_chinese" => format!("{YELLOW}{name}{RESET}不能刪除"), "traditional_chinese" => format!("{name}不能刪除"),
"english" => format!("{YELLOW}{name}{RESET} cannot be deleted"), "english" => format!("{name} cannot be deleted"),
), ),
None, None,
), ),
@ -1451,16 +1527,17 @@ impl LowerError {
"english" => "public", "english" => "public",
) )
}; };
let found = StringSpan::new(name, Some(ERR), Some(Attribute::Bold));
Self::new( Self::new(
ErrorCore::new( ErrorCore::new(
errno, errno,
VisibilityError, VisibilityError,
loc, loc,
switch_lang!( switch_lang!(
"japanese" => format!("{RED}{name}{RESET}{visibility}変数です"), "japanese" => format!("{found}{visibility}変数です"),
"simplified_chinese" => format!("{RED}{name}{RESET}{visibility}变量",), "simplified_chinese" => format!("{found}{visibility}变量",),
"traditional_chinese" => format!("{RED}{name}{RESET}{visibility}變量",), "traditional_chinese" => format!("{found}{visibility}變量",),
"english" => format!("{RED}{name}{RESET} is {visibility} variable",), "english" => format!("{found} is {visibility} variable",),
), ),
None, None,
), ),
@ -1477,6 +1554,12 @@ impl LowerError {
superclass: &Type, superclass: &Type,
caused_by: S, caused_by: S,
) -> Self { ) -> Self {
let name = StringSpan::new(name, Some(ERR), Some(Attribute::Bold));
let superclass = StringSpan::new(
&format!("{}", superclass),
Some(WARNING),
Some(Attribute::Bold),
);
Self::new( Self::new(
ErrorCore::new( ErrorCore::new(
errno, errno,
@ -1484,16 +1567,16 @@ impl LowerError {
name_loc, name_loc,
switch_lang!( switch_lang!(
"japanese" => format!( "japanese" => format!(
"{RED}{name}{RESET}は{YELLOW}{superclass}{RESET}で既に定義されています", "{name}は{superclass}で既に定義されています",
), ),
"simplified_chinese" => format!( "simplified_chinese" => format!(
"{RED}{name}{RESET}已在{YELLOW}{superclass}{RESET}中定义", "{name}已在{superclass}中定义",
), ),
"traditional_chinese" => format!( "traditional_chinese" => format!(
"{RED}{name}{RESET}已在{YELLOW}{superclass}{RESET}中定義", "{name}已在{superclass}中定義",
), ),
"english" => format!( "english" => format!(
"{RED}{name}{RESET} is already defined in {YELLOW}{superclass}{RESET}", "{name} is already defined in {superclass}",
), ),
), ),
Some(switch_lang!( Some(switch_lang!(
@ -1574,11 +1657,21 @@ impl LowerError {
similar_py_mod: Option<Str>, similar_py_mod: Option<Str>,
) -> Self { ) -> Self {
let hint = match (similar_erg_mod, similar_py_mod) { let hint = match (similar_erg_mod, similar_py_mod) {
(Some(erg), Some(py)) => Some(format!( (Some(erg), Some(py)) => {
"similar name erg module {YELLOW}{erg}{RESET} and python module {YELLOW}{py}{RESET} exists (to import python modules, use `pyimport`)", let erg = StringSpan::new(&erg, Some(WARNING), Some(Attribute::Bold));
)), let py = StringSpan::new(&py, Some(WARNING), Some(Attribute::Bold));
(Some(erg), None) => Some(format!("similar name erg module exists: {YELLOW}{erg}{RESET}")), Some(format!(
(None, Some(py)) => Some(format!("similar name python module exists: {YELLOW}{py}{RESET} (to import python modules, use `pyimport`)")), "similar name erg module {erg} and python module {py} exists (to import python modules, use `pyimport`)",
))
}
(Some(erg), None) => {
let erg = StringSpan::new(&erg, Some(WARNING), Some(Attribute::Bold));
Some(format!("similar name erg module exists: {erg}"))
}
(None, Some(py)) => {
let py = StringSpan::new(&py, Some(WARNING), Some(Attribute::Bold));
Some(format!("similar name python module exists: {py} (to import python modules, use `pyimport`)"))
}
(None, None) => None, (None, None) => None,
}; };
let hint = hint.map(AtomicStr::from); let hint = hint.map(AtomicStr::from);
@ -1638,16 +1731,22 @@ impl LowerError {
cast_to: &Type, cast_to: &Type,
hint: Option<AtomicStr>, hint: Option<AtomicStr>,
) -> Self { ) -> Self {
let name = StringSpan::new(name, Some(WARNING), Some(Attribute::Bold));
let found = StringSpan::new(
&format!("{}", cast_to),
Some(WARNING),
Some(Attribute::Bold),
);
Self::new( Self::new(
ErrorCore::new( ErrorCore::new(
errno, errno,
TypeError, TypeError,
loc, loc,
switch_lang!( switch_lang!(
"japanese" => format!("{YELLOW}{name}{RESET}の型を{RED}{cast_to}{RESET}にキャストすることはできません"), "japanese" => format!("{name}の型を{found}にキャストすることはできません"),
"simplified_chinese" => format!("{YELLOW}{name}{RESET}的类型无法转换为{RED}{cast_to}{RESET}"), "simplified_chinese" => format!("{name}的类型无法转换为{found}"),
"traditional_chinese" => format!("{YELLOW}{name}{RESET}的類型無法轉換為{RED}{cast_to}{RESET}"), "traditional_chinese" => format!("{name}的類型無法轉換為{found}"),
"english" => format!("the type of {YELLOW}{name}{RESET} cannot be cast to {RED}{cast_to}{RESET}"), "english" => format!("the type of {name} cannot be cast to {found}"),
), ),
hint, hint,
), ),

View file

@ -4,7 +4,7 @@
use erg_common::astr::AtomicStr; use erg_common::astr::AtomicStr;
use erg_common::config::Input; use erg_common::config::Input;
use erg_common::error::{ErrorCore, ErrorDisplay, ErrorKind::*, Location, MultiErrorDisplay}; use erg_common::error::{ErrorCore, ErrorDisplay, ErrorKind::*, Location, MultiErrorDisplay};
use erg_common::style::{RED, RESET}; use erg_common::style::{Attribute, Color, StrSpan, StringSpan, Theme};
use erg_common::traits::Stream; use erg_common::traits::Stream;
use erg_common::{impl_display_and_error, impl_stream_for_wrapper, switch_lang}; use erg_common::{impl_display_and_error, impl_stream_for_wrapper, switch_lang};
@ -38,15 +38,20 @@ impl LexError {
} }
pub fn compiler_bug(errno: usize, loc: Location, fn_name: &str, line: u32) -> Self { pub fn compiler_bug(errno: usize, loc: Location, fn_name: &str, line: u32) -> Self {
const URL: StrSpan = StrSpan::new(
"https://github.com/erg-lang/erg",
Some(Color::White),
Some(Attribute::Underline),
);
Self::new(ErrorCore::new( Self::new(ErrorCore::new(
errno, errno,
CompilerSystemError, CompilerSystemError,
loc, loc,
switch_lang!( switch_lang!(
"japanese" => format!("これはErg compilerのバグです、開発者に報告して下さい (https://github.com/erg-lang/erg)\n{fn_name}:{line}より発生"), "japanese" => format!("これはErg compilerのバグです、開発者に報告して下さい ({URL})\n{fn_name}:{line}より発生"),
"simplified_chinese" => format!("这是Erg编译器的一个错误请报告给https://github.com/erg-lang/erg\n原因来自: {fn_name}:{line}"), "simplified_chinese" => format!("这是Erg编译器的一个错误请报告给{URL}\n原因来自: {fn_name}:{line}"),
"traditional_chinese" => format!("這是Erg編譯器的一個錯誤請報告給https://github.com/erg-lang/erg\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 https://github.com/erg-lang/erg\ncaused from: {fn_name}:{line}"), "english" => format!("this is a bug of the Erg compiler, please report it to {URL}\ncaused from: {fn_name}:{line}"),
), ),
None, None,
)) ))
@ -115,15 +120,16 @@ impl LexError {
) )
.into() .into()
}); });
let name = StringSpan::new(name, Some(Color::Red), Some(Attribute::Underline));
Self::new(ErrorCore::new( Self::new(ErrorCore::new(
errno, errno,
NameError, NameError,
loc, loc,
switch_lang!( switch_lang!(
"japanese" => format!("{RED}{name}{RESET}という変数は定義されていません"), "japanese" => format!("{name}という変数は定義されていません"),
"simplified_chinese" => format!("{RED}{name}{RESET}未定义"), "simplified_chinese" => format!("{name}未定义"),
"traditional_chinese" => format!("{RED}{name}{RESET}未定義"), "traditional_chinese" => format!("{name}未定義"),
"english" => format!("{RED}{name}{RESET} is not defined"), "english" => format!("{name} is not defined"),
), ),
hint, hint,
)) ))
@ -158,6 +164,7 @@ pub type DesugaringResult<T> = Result<T, DesugaringError>;
pub struct ParserRunnerError { pub struct ParserRunnerError {
pub core: ErrorCore, pub core: ErrorCore,
pub input: Input, pub input: Input,
pub theme: Theme,
} }
impl_display_and_error!(ParserRunnerError); impl_display_and_error!(ParserRunnerError);
@ -169,6 +176,9 @@ impl ErrorDisplay for ParserRunnerError {
fn input(&self) -> &Input { fn input(&self) -> &Input {
&self.input &self.input
} }
fn theme(&self) -> &Theme {
&self.theme
}
fn caused_by(&self) -> &str { fn caused_by(&self) -> &str {
"" ""
} }
@ -178,8 +188,8 @@ impl ErrorDisplay for ParserRunnerError {
} }
impl ParserRunnerError { impl ParserRunnerError {
pub const fn new(core: ErrorCore, input: Input) -> Self { pub const fn new(core: ErrorCore, input: Input, theme: Theme) -> Self {
Self { core, input } Self { core, input, theme }
} }
} }
@ -191,10 +201,10 @@ impl_stream_for_wrapper!(ParserRunnerErrors, ParserRunnerError);
impl MultiErrorDisplay<ParserRunnerError> for ParserRunnerErrors {} impl MultiErrorDisplay<ParserRunnerError> for ParserRunnerErrors {}
impl ParserRunnerErrors { impl ParserRunnerErrors {
pub fn convert(input: &Input, errs: ParseErrors) -> Self { pub fn convert(input: &Input, errs: ParseErrors, theme: Theme) -> Self {
Self( Self(
errs.into_iter() errs.into_iter()
.map(|err| ParserRunnerError::new(*err.0, input.clone())) .map(|err| ParserRunnerError::new(*err.0, input.clone(), theme))
.collect(), .collect(),
) )
} }

View file

@ -4,6 +4,7 @@ use std::cmp::Ordering;
use erg_common::cache::CacheSet; use erg_common::cache::CacheSet;
use erg_common::config::ErgConfig; use erg_common::config::ErgConfig;
use erg_common::config::Input; use erg_common::config::Input;
use erg_common::style::THEME;
use erg_common::traits::{Locational, Runnable, Stream}; use erg_common::traits::{Locational, Runnable, Stream};
use erg_common::{debug_power_assert, fn_name_full, normalize_newline, switch_lang}; use erg_common::{debug_power_assert, fn_name_full, normalize_newline, switch_lang};
@ -41,7 +42,7 @@ impl Runnable for LexerRunner {
let lexer = Lexer::from_str(self.input().read()); let lexer = Lexer::from_str(self.input().read());
let ts = lexer let ts = lexer
.lex() .lex()
.map_err(|errs| LexerRunnerErrors::convert(self.input(), errs))?; .map_err(|errs| LexerRunnerErrors::convert(self.input(), errs, THEME))?;
println!("{ts}"); println!("{ts}");
Ok(0) Ok(0)
} }
@ -51,13 +52,13 @@ impl Runnable for LexerRunner {
if cfg!(feature = "debug") { if cfg!(feature = "debug") {
let ts = lexer let ts = lexer
.lex() .lex()
.map_err(|errs| LexerRunnerErrors::convert(self.input(), errs))?; .map_err(|errs| LexerRunnerErrors::convert(self.input(), errs, THEME))?;
println!("{ts}"); println!("{ts}");
Ok(ts.to_string()) Ok(ts.to_string())
} else { } else {
Ok(lexer Ok(lexer
.lex() .lex()
.map_err(|errs| LexerRunnerErrors::convert(self.input(), errs))? .map_err(|errs| LexerRunnerErrors::convert(self.input(), errs, THEME))?
.to_string()) .to_string())
} }
} }

View file

@ -11,6 +11,7 @@ use erg_common::error::Location;
use erg_common::option_enum_unwrap; use erg_common::option_enum_unwrap;
use erg_common::set::Set as HashSet; use erg_common::set::Set as HashSet;
use erg_common::str::Str; use erg_common::str::Str;
use erg_common::style::THEME;
use erg_common::traits::Runnable; use erg_common::traits::Runnable;
use erg_common::traits::{Locational, Stream}; use erg_common::traits::{Locational, Stream};
use erg_common::{ use erg_common::{
@ -206,16 +207,16 @@ impl ParserRunner {
pub fn parse_token_stream(&mut self, ts: TokenStream) -> Result<Module, ParserRunnerErrors> { pub fn parse_token_stream(&mut self, ts: TokenStream) -> Result<Module, ParserRunnerErrors> {
Parser::new(ts) Parser::new(ts)
.parse() .parse()
.map_err(|errs| ParserRunnerErrors::convert(self.input(), errs)) .map_err(|errs| ParserRunnerErrors::convert(self.input(), errs, THEME))
} }
pub fn parse(&mut self, src: String) -> Result<Module, ParserRunnerErrors> { pub fn parse(&mut self, src: String) -> Result<Module, ParserRunnerErrors> {
let ts = Lexer::new(Input::Str(src)) let ts = Lexer::new(Input::Str(src))
.lex() .lex()
.map_err(|errs| ParserRunnerErrors::convert(self.input(), errs))?; .map_err(|errs| ParserRunnerErrors::convert(self.input(), errs, THEME))?;
Parser::new(ts) Parser::new(ts)
.parse() .parse()
.map_err(|errs| ParserRunnerErrors::convert(self.input(), errs)) .map_err(|errs| ParserRunnerErrors::convert(self.input(), errs, THEME))
} }
} }

View file

@ -1,5 +1,6 @@
use erg_common::config::{ErgConfig, Input}; use erg_common::config::{ErgConfig, Input};
use erg_common::error::MultiErrorDisplay; use erg_common::error::MultiErrorDisplay;
use erg_common::style::THEME;
use erg_common::traits::Runnable; use erg_common::traits::Runnable;
use erg_parser::error::ParserRunnerErrors; use erg_parser::error::ParserRunnerErrors;
@ -58,7 +59,7 @@ fn parse_test_from_code(file_path: &'static str) -> Result<(), ParserRunnerError
match parser.parse_token_stream( match parser.parse_token_stream(
lexer lexer
.lex() .lex()
.map_err(|errs| ParserRunnerErrors::convert(&input, errs))?, .map_err(|errs| ParserRunnerErrors::convert(&input, errs, THEME))?,
) { ) {
Ok(module) => { Ok(module) => {
println!("{module}"); println!("{module}");