From e62228db35a974591f6622b93d85af238f11ecc9 Mon Sep 17 00:00:00 2001 From: zong-zhe Date: Mon, 29 Aug 2022 10:18:12 +0800 Subject: [PATCH 01/22] init error handler --- kclvm/compiler_base/error/Cargo.toml | 1 + kclvm/compiler_base/error/src/lib.rs | 70 ++++++++++++++++++++++++++-- 2 files changed, 68 insertions(+), 3 deletions(-) diff --git a/kclvm/compiler_base/error/Cargo.toml b/kclvm/compiler_base/error/Cargo.toml index f7ec91b00..c845f0d25 100644 --- a/kclvm/compiler_base/error/Cargo.toml +++ b/kclvm/compiler_base/error/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +compiler_base_span = {path = "../span", version = "0.1.0"} compiler_base_macros = {path = "../macros", version = "0.1.0"} rustc_errors = {path="../3rdparty/rustc_errors", version="0.1.0"} unic-langid = {version="0.9.0", features = ["macros"]} diff --git a/kclvm/compiler_base/error/src/lib.rs b/kclvm/compiler_base/error/src/lib.rs index 754d6b3a1..521751bc2 100644 --- a/kclvm/compiler_base/error/src/lib.rs +++ b/kclvm/compiler_base/error/src/lib.rs @@ -1,5 +1,69 @@ -pub mod diagnostic; +mod diagnostic; mod emitter; -pub use emitter::Emitter; -pub use emitter::TerminalEmitter; +use std::sync::Arc; + +use anyhow::{Context, Result}; +use compiler_base_span::SourceMap; +use diagnostic::{diagnostic_message::TemplateLoader, style::DiagnosticStyle, Diagnostic}; +use emitter::{Emitter, TerminalEmitter}; + +pub struct ErrorHandler { + source_map: Arc, + template_loader: Arc, + emitter: Box>, + diagnostics: Vec>, +} + +impl ErrorHandler { + pub fn new(source_map: Arc, template_dir: &str) -> Result { + let template_loader = TemplateLoader::new_with_template_dir(template_dir) + .with_context(|| format!("Failed to init `TemplateLoader` from '{}'", template_dir))?; + Ok(Self { + source_map, + template_loader: Arc::new(template_loader), + emitter: Box::new(TerminalEmitter::default()), + diagnostics: vec![], + }) + } + + pub fn add_diagnostic(&mut self, diag_builder: impl DiagnosticBuilder) { + self.diagnostics.push(diag_builder.into_diagnostic( + Arc::clone(&self.source_map), + Arc::clone(&self.template_loader), + )); + } + + pub fn emit_err(&mut self, diag_builder: impl DiagnosticBuilder) { + self.emitter.emit_diagnostic(&diag_builder.into_diagnostic( + Arc::clone(&self.source_map), + Arc::clone(&self.template_loader), + )); + } + + pub fn emit_all_errs(&mut self) { + for diag in &self.diagnostics { + self.emitter.emit_diagnostic(&diag) + } + } +} + +pub trait DiagnosticBuilder { + fn into_diagnostic( + self, + sm: Arc, + template_loader: Arc, // 那就应该只把template_loader做成对外的接口, 相反ErrorMessage不太需要给开发者用 + ) -> Diagnostic; +} + +struct InvalidSyntaxError; + +impl DiagnosticBuilder for InvalidSyntaxError { + fn into_diagnostic( + self, + sm: Arc, + template_loader: Arc, + ) -> Diagnostic { + todo!() + } +} From 95ecb28c39c5662457d96d671916d6df648b1c73 Mon Sep 17 00:00:00 2001 From: zong-zhe Date: Mon, 29 Aug 2022 17:49:30 +0800 Subject: [PATCH 02/22] feat(compiler-base): add diagnostic handler. add diagnostic handler in compiler_base/error/lib.rs. issue #115 --- .../src/diagnostic/diagnostic_message.rs | 148 ++---------- .../error/src/diagnostic/tests.rs | 2 +- kclvm/compiler_base/error/src/lib.rs | 219 +++++++++++++++--- kclvm/compiler_base/error/src/tests.rs | 58 +++++ 4 files changed, 264 insertions(+), 163 deletions(-) create mode 100644 kclvm/compiler_base/error/src/tests.rs diff --git a/kclvm/compiler_base/error/src/diagnostic/diagnostic_message.rs b/kclvm/compiler_base/error/src/diagnostic/diagnostic_message.rs index 5f00ad0f4..a381f9ae7 100644 --- a/kclvm/compiler_base/error/src/diagnostic/diagnostic_message.rs +++ b/kclvm/compiler_base/error/src/diagnostic/diagnostic_message.rs @@ -1,24 +1,26 @@ //! The crate provides `TemplateLoader` to load the diagnositc message displayed in diagnostics from "*.ftl" files, //! use anyhow::{bail, Context, Result}; -use fluent::{FluentArgs, FluentBundle, FluentResource}; +use fluent::{FluentBundle, FluentResource}; use std::{fs, sync::Arc}; use unic_langid::langid; use walkdir::{DirEntry, WalkDir}; -/// Struct `TemplateLoader` load template contents from "*.ftl" file. -/// -/// `TemplateLoader` will operate on files locally. -/// -/// In order to avoid the performance loss and thread safety problems that -/// may occur during the constructing the `TemplateLoader`, we close the constructor of `TemplateLoader`. -/// -/// You only need to pass the path of the "*.ftl" file to `DiagnosticHandler`, -/// and `DiagnosticHandler` will automatically construct `TemplateLoader` and load the template file. -/// -/// `TemplateLoader` is only useful for you, when you want to get message from template file by `get_msg_to_str()`. -/// For more information about how to use `get_msg_to_str()`, see the doc above `get_msg_to_str()`. -pub struct TemplateLoader { +use crate::MessageArgs; + +// Struct `TemplateLoader` load template contents from "*.ftl" file. +// +// `TemplateLoader` will operate on files locally. +// +// In order to avoid the performance loss and thread safety problems that +// may occur during the constructing the `TemplateLoader`, we close the constructor of `TemplateLoader`. +// +// You only need to pass the path of the "*.ftl" file to `DiagnosticHandler`, +// and `DiagnosticHandler` will automatically construct `TemplateLoader` and load the template file. +// +// `TemplateLoader` is only useful for you, when you want to get message from template file by `get_msg_to_str()`. +// For more information about how to use `get_msg_to_str()`, see the doc above `get_msg_to_str()`. +pub(crate) struct TemplateLoader { template_inner: Arc, } @@ -42,79 +44,11 @@ impl TemplateLoader { }) } - /// Get the message string from "*.ftl" file by `index`, `sub_index` and `MessageArgs`. - /// For more information about "*.ftl" file, see the doc above `TemplateLoader`. - /// - /// "*.ftl" file looks like, e.g. './src/diagnostic/locales/en-US/default.ftl' : - /// - /// ``` ignore - /// 1. invalid-syntax = Invalid syntax - /// 2. .expected = Expected one of `{$expected_items}` - /// ``` - /// - /// - In line 1, `invalid-syntax` is a `index`, `Invalid syntax` is the `Message String` to this `index`. - /// - In line 2, `.expected` is another `index`, it is a `sub_index` of `invalid-syntax`. - /// - In line 2, `sub_index` must start with a point `.` and it is optional. - /// - In line 2, `Expected one of `{$expected_items}`` is the `Message String` to `.expected`. It is an interpolated string. - /// - In line 2, `{$expected_items}` is a `MessageArgs` of the `Expected one of `{$expected_items}`` - /// and `MessageArgs` can be recognized as a Key-Value entry, it is optional. - /// - /// The pattern of above '*.ftl' file looks like: - /// ``` ignore - /// 1. <'index'> = <'message_string' with optional 'MessageArgs'> - /// 2. = <'message_string' with optional 'MessageArgs'> - /// ``` - /// And for the 'default.ftl' shown above, you can get messages as follow: - /// - /// 1. If you want the message 'Invalid syntax' in line 1. - /// - /// ```ignore rust - /// # use compiler_base_error::diagnostic::diagnostic_message::TemplateLoader; - /// # use compiler_base_error::diagnostic::diagnostic_message::MessageArgs; - /// # use std::borrow::Borrow; - /// - /// // 1. Prepare an empty `MessageArgs`, Message in line 1 is not an interpolated string. - /// let no_args = MessageArgs::new(); - /// - /// // 2. `index` is 'invalid-syntax' and has no `sub_index`. - /// let index = "invalid-syntax"; - /// let sub_index = None; - /// - /// // 3. Create the `TemplateLoader` with template (*.ftl) files directory. - /// // We cloesd the constructor of `TemplateLoader`. - /// // For more information, see the doc above the `TemplateLoader`. - /// let error_message = TemplateLoader::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); - /// let msg_in_line_1 = error_message.get_msg_to_str(index, sub_index, &no_args).unwrap(); - /// - /// assert_eq!(msg_in_line_1, "Invalid syntax"); - /// ``` - /// - /// 2. If you want the message 'Expected one of `{$expected_items}`' in line 2. - /// - /// ```ignore rust - /// # use compiler_base_error::diagnostic::diagnostic_message::TemplateLoader; - /// # use compiler_base_error::diagnostic::diagnostic_message::MessageArgs; - /// # use std::borrow::Borrow; - /// - /// // 1. Prepare the `MessageArgs` for `{$expected_items}`. - /// let mut args = MessageArgs::new(); - /// args.set("expected_items", "I am an expected item"); - /// - /// // 2. `index` is 'invalid-syntax'. - /// let index = "invalid-syntax"; - /// - /// // 3. `sub_index` is 'expected'. - /// let sub_index = "expected"; - /// - /// // 4. With the help of `TemplateLoader`, you can get the message in 'default.ftl'. - /// // We cloesd the constructor of `TemplateLoader`. - /// // For more information, see the doc above the `TemplateLoader`. - /// let error_message = TemplateLoader::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); - /// let msg_in_line_2 = error_message.get_msg_to_str(index, Some(sub_index), &args).unwrap(); - /// - /// assert_eq!(msg_in_line_2, "Expected one of `\u{2068}I am an expected item\u{2069}`"); - /// ``` - pub fn get_msg_to_str( + // Get the message string from "*.ftl" file by `index`, `sub_index` and `MessageArgs`. + // For more information about "*.ftl" file, see the doc above `DiagnosticHandler`. + // + // "*.ftl" file looks like, e.g. './src/diagnostic/locales/en-US/default.ftl' : + pub(crate) fn get_msg_to_str( &self, index: &str, sub_index: Option<&str>, @@ -146,46 +80,6 @@ impl TemplateLoader { } } -/// `MessageArgs` is the arguments of the interpolated string. -/// -/// `MessageArgs` is a Key-Value entry which only supports "set" and without "get". -/// You need getting nothing from `MessageArgs`. Only setting it and senting it to `TemplateLoader` is enough. -/// -/// Note: Currently both `Key` and `Value` of `MessageArgs` types only support string (&str). -/// -/// # Examples -/// -/// ```ignore rust -/// # use compiler_base_error::diagnostic::diagnostic_message::MessageArgs; -/// # use compiler_base_error::diagnostic::diagnostic_message::TemplateLoader; -/// # use std::borrow::Borrow; -/// -/// let index = "invalid-syntax"; -/// let sub_index = Some("expected"); -/// let mut msg_args = MessageArgs::new(); -/// // You only need "set()". -/// msg_args.set("This is Key", "This is Value"); -/// -/// // We cloesd the constructor of `TemplateLoader`. -/// // For more information, see the doc above the `TemplateLoader`. -/// let error_message = TemplateLoader::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); -/// -/// // When you use it, just sent it to `TemplateLoader`. -/// let msg_in_line_1 = error_message.get_msg_to_str(index, sub_index, &msg_args); -/// ``` -/// -/// For more information about the `TemplateLoader` see the doc above struct `TemplateLoader`. -pub struct MessageArgs<'a>(FluentArgs<'a>); -impl<'a> MessageArgs<'a> { - pub fn new() -> Self { - Self(FluentArgs::new()) - } - - pub fn set(&mut self, k: &'a str, v: &'a str) { - self.0.set(k, v); - } -} - // `TemplateLoaderInner` is used to privatize the default constructor of `TemplateLoader`. struct TemplateLoaderInner { template_bunder: FluentBundle, diff --git a/kclvm/compiler_base/error/src/diagnostic/tests.rs b/kclvm/compiler_base/error/src/diagnostic/tests.rs index a651d1f54..63ba29928 100644 --- a/kclvm/compiler_base/error/src/diagnostic/tests.rs +++ b/kclvm/compiler_base/error/src/diagnostic/tests.rs @@ -77,7 +77,7 @@ mod test_components { } mod test_error_message { - use crate::diagnostic::diagnostic_message::{MessageArgs, TemplateLoader}; + use crate::{diagnostic::diagnostic_message::TemplateLoader, MessageArgs}; #[test] fn test_template_message() { diff --git a/kclvm/compiler_base/error/src/lib.rs b/kclvm/compiler_base/error/src/lib.rs index 521751bc2..df13f600d 100644 --- a/kclvm/compiler_base/error/src/lib.rs +++ b/kclvm/compiler_base/error/src/lib.rs @@ -1,69 +1,218 @@ mod diagnostic; mod emitter; -use std::sync::Arc; - use anyhow::{Context, Result}; -use compiler_base_span::SourceMap; -use diagnostic::{diagnostic_message::TemplateLoader, style::DiagnosticStyle, Diagnostic}; +use diagnostic::diagnostic_message::TemplateLoader; use emitter::{Emitter, TerminalEmitter}; +use fluent::FluentArgs; +use std::sync::Arc; + +pub use diagnostic::{style::DiagnosticStyle, Diagnostic}; + +#[cfg(test)] +mod tests; -pub struct ErrorHandler { - source_map: Arc, +/// `DiagnosticHandler` supports diagnostic messages to terminal stderr. +/// +/// `DiagnosticHandler` will load template file directory when instantiating through the constructor `new()`. +pub struct DiagnosticHandler { template_loader: Arc, emitter: Box>, diagnostics: Vec>, } -impl ErrorHandler { - pub fn new(source_map: Arc, template_dir: &str) -> Result { +impl DiagnosticHandler { + /// Load all (*.ftl) template files under directory `template_dir`. + /// `DiagnosticHandler` will load all the files end with "*.ftl" under the directory recursively. + /// If directory `template_dir` does not exist, this method will return an error. + /// + /// template_files + /// | + /// |---- template.ftl + /// |---- sub_template_files + /// | + /// |---- sub_template.ftl + /// + /// 'template.ftl' and 'sub_template.ftl' can both loaded by the `new_with_template_dir()`. + /// + /// # Examples + /// + /// ```rust + /// # use compiler_base_error::DiagnosticHandler; + /// let diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/"); + /// match diag_handler{ + /// Ok(_) => {} + /// Err(_) => {panic!("`diag_handler` should be Ok(...)")} + /// } + /// + /// // './src_invalid/diagnostic/locales/en-US/' does not exist. + /// let diag_handler_invalid = DiagnosticHandler::new_with_template_dir("./src_invalid/diagnostic/locales/en-US/"); + /// match diag_handler_invalid{ + /// Ok(_) => {panic!("`diag_handler_invalid` should be Err(...)")} + /// Err(_) => {} + /// } + /// ``` + pub fn new_with_template_dir(template_dir: &str) -> Result { let template_loader = TemplateLoader::new_with_template_dir(template_dir) .with_context(|| format!("Failed to init `TemplateLoader` from '{}'", template_dir))?; Ok(Self { - source_map, template_loader: Arc::new(template_loader), emitter: Box::new(TerminalEmitter::default()), diagnostics: vec![], }) } - pub fn add_diagnostic(&mut self, diag_builder: impl DiagnosticBuilder) { - self.diagnostics.push(diag_builder.into_diagnostic( - Arc::clone(&self.source_map), - Arc::clone(&self.template_loader), - )); + /// Add a diagnostic to `DiagnosticHandler`. + /// `DiagnosticHandler` contains a set of `Diagnostic` + /// + /// # Examples + /// + /// ```rust + /// # use compiler_base_error::DiagnosticStyle; + /// # use compiler_base_error::DiagnosticHandler; + /// # use compiler_base_error::Diagnostic; + /// let diag_1 = Diagnostic::::new(); + /// let mut diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); + /// assert_eq!(diag_handler.diagnostics_count(), 0); + /// + /// diag_handler.add_diagnostic(diag_1); + /// assert_eq!(diag_handler.diagnostics_count(), 1); + /// ``` + pub fn add_diagnostic(&mut self, diag: Diagnostic) { + self.diagnostics.push(diag); } - pub fn emit_err(&mut self, diag_builder: impl DiagnosticBuilder) { - self.emitter.emit_diagnostic(&diag_builder.into_diagnostic( - Arc::clone(&self.source_map), - Arc::clone(&self.template_loader), - )); + /// Get count of diagnostics in `DiagnosticHandler`. + /// `DiagnosticHandler` contains a set of `Diagnostic` + pub fn diagnostics_count(&self) -> usize { + self.diagnostics.len() } - pub fn emit_all_errs(&mut self) { + /// Emit the diagnostic messages to to terminal stderr. + pub fn emit_diagnostic(&mut self, diag: Diagnostic) { + self.emitter.emit_diagnostic(&diag); + } + + /// Emit all the diagnostics messages to to terminal stderr. + /// `DiagnosticHandler` contains a set of `Diagnostic` + pub fn emit_all_diagnostics(&mut self) { for diag in &self.diagnostics { self.emitter.emit_diagnostic(&diag) } } -} -pub trait DiagnosticBuilder { - fn into_diagnostic( - self, - sm: Arc, - template_loader: Arc, // 那就应该只把template_loader做成对外的接口, 相反ErrorMessage不太需要给开发者用 - ) -> Diagnostic; + /// Get the message string from "*.ftl" file by `index`, `sub_index` and `MessageArgs`. + /// "*.ftl" file looks like, e.g. './src/diagnostic/locales/en-US/default.ftl' : + /// + /// ``` ignore + /// 1. invalid-syntax = Invalid syntax + /// 2. .expected = Expected one of `{$expected_items}` + /// ``` + /// + /// - In line 1, `invalid-syntax` is a `index`, `Invalid syntax` is the `Message String` to this `index`. + /// - In line 2, `.expected` is another `index`, it is a `sub_index` of `invalid-syntax`. + /// - In line 2, `sub_index` must start with a point `.` and it is optional. + /// - In line 2, `Expected one of `{$expected_items}`` is the `Message String` to `.expected`. It is an interpolated string. + /// - In line 2, `{$expected_items}` is a `MessageArgs` of the `Expected one of `{$expected_items}`` + /// and `MessageArgs` can be recognized as a Key-Value entry, it is optional. + /// + /// The pattern of above '*.ftl' file looks like: + /// ``` ignore + /// 1. <'index'> = <'message_string' with optional 'MessageArgs'> + /// 2. = <'message_string' with optional 'MessageArgs'> + /// ``` + /// And for the 'default.ftl' shown above, you can get messages as follow: + /// + /// 1. If you want the message 'Invalid syntax' in line 1. + /// + /// ``` rust + /// # use compiler_base_error::MessageArgs; + /// # use compiler_base_error::DiagnosticHandler; + /// + /// // 1. Prepare an empty `MessageArgs`, Message in line 1 is not an interpolated string. + /// let no_args = MessageArgs::new(); + /// + /// // 2. `index` is 'invalid-syntax' and has no `sub_index`. + /// let index = "invalid-syntax"; + /// let sub_index = None; + /// + /// // 3. Create the `DiagnosticHandler` with template (*.ftl) files directory. + /// let diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); + /// + /// // 4. Get the message. + /// let msg_in_line_1 = diag_handler.get_diagnostic_msg(index, sub_index, &no_args).unwrap(); + /// + /// assert_eq!(msg_in_line_1, "Invalid syntax"); + /// ``` + /// + /// 2. If you want the message 'Expected one of `{$expected_items}`' in line 2. + /// + /// ``` rust + /// # use compiler_base_error::MessageArgs; + /// # use compiler_base_error::DiagnosticHandler; + /// + /// // 1. Prepare the `MessageArgs` for `{$expected_items}`. + /// let mut args = MessageArgs::new(); + /// args.set("expected_items", "I am an expected item"); + /// + /// // 2. `index` is 'invalid-syntax'. + /// let index = "invalid-syntax"; + /// + /// // 3. `sub_index` is 'expected'. + /// let sub_index = "expected"; + /// + /// // 4. Create the `DiagnosticHandler` with template (*.ftl) files directory. + /// let diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); + /// + /// // 5. Get the message. + /// let msg_in_line_2 = diag_handler.get_diagnostic_msg(index, Some(sub_index), &args).unwrap(); + /// + /// assert_eq!(msg_in_line_2, "Expected one of `\u{2068}I am an expected item\u{2069}`"); + /// ``` + pub fn get_diagnostic_msg( + &self, + index: &str, + sub_index: Option<&str>, + args: &MessageArgs, + ) -> Result { + self.template_loader.get_msg_to_str(index, sub_index, &args) + } } -struct InvalidSyntaxError; +/// `MessageArgs` is the arguments of the interpolated string. +/// +/// `MessageArgs` is a Key-Value entry which only supports "set" and without "get". +/// You need getting nothing from `MessageArgs`. Only setting it and senting it to `DiagnosticHandler` is enough. +/// +/// Note: Currently both `Key` and `Value` of `MessageArgs` types only support string (&str). +/// +/// # Examples +/// +/// ``` rust +/// # use compiler_base_error::DiagnosticHandler; +/// # use compiler_base_error::MessageArgs; +/// +/// let index = "invalid-syntax"; +/// let sub_index = Some("expected"); +/// let mut msg_args = MessageArgs::new(); +/// // You only need "set()". +/// msg_args.set("This is Key", "This is Value"); +/// +/// // Create the `DiagnosticHandler` with template (*.ftl) files directory. +/// let diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); +/// +/// // When you use it, just sent it to `DiagnosticHandler`. +/// let msg_in_line_1 = diag_handler.get_diagnostic_msg(index, sub_index, &msg_args); +/// ``` +/// +/// For more information about the `DiagnosticHandler` see the doc above struct `DiagnosticHandler`. +pub struct MessageArgs<'a>(FluentArgs<'a>); +impl<'a> MessageArgs<'a> { + pub fn new() -> Self { + Self(FluentArgs::new()) + } -impl DiagnosticBuilder for InvalidSyntaxError { - fn into_diagnostic( - self, - sm: Arc, - template_loader: Arc, - ) -> Diagnostic { - todo!() + pub fn set(&mut self, k: &'a str, v: &'a str) { + self.0.set(k, v); } } diff --git a/kclvm/compiler_base/error/src/tests.rs b/kclvm/compiler_base/error/src/tests.rs new file mode 100644 index 000000000..a8dbbf567 --- /dev/null +++ b/kclvm/compiler_base/error/src/tests.rs @@ -0,0 +1,58 @@ +mod test_diagnostic_handler { + use crate::{Diagnostic, DiagnosticHandler, DiagnosticStyle, MessageArgs}; + + #[test] + fn test_diagnostic_handler_new_with_template_dir() { + let diag_handler = + DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/"); + match diag_handler { + Ok(_) => {} + Err(_) => { + panic!("`diag_handler` should be Ok(...)") + } + } + + let diag_handler_invalid = DiagnosticHandler::new_with_template_dir("./invalid_path"); + match diag_handler_invalid { + Ok(_) => { + panic!("`diag_handler_invalid` should be Err(...)") + } + Err(_) => {} + } + } + + #[test] + fn test_diagnostic_handler_add_diagnostic() { + let diag_1 = Diagnostic::::new(); + let mut diag_handler = + DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); + assert_eq!(diag_handler.diagnostics_count(), 0); + + diag_handler.add_diagnostic(diag_1); + assert_eq!(diag_handler.diagnostics_count(), 1); + } + + #[test] + fn test_diagnostic_handler_get_diagnostic_msg() { + let no_args = MessageArgs::new(); + let index = "invalid-syntax"; + let sub_index = None; + let diag_handler = + DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); + let msg_in_line_1 = diag_handler + .get_diagnostic_msg(index, sub_index, &no_args) + .unwrap(); + assert_eq!(msg_in_line_1, "Invalid syntax"); + + let mut args = MessageArgs::new(); + args.set("expected_items", "I am an expected item"); + let sub_index = "expected"; + let msg_in_line_2 = diag_handler + .get_diagnostic_msg(index, Some(sub_index), &args) + .unwrap(); + assert_eq!( + msg_in_line_2, + "Expected one of `\u{2068}I am an expected item\u{2069}`" + ); + } +} From cf37a6e179dd9aec1b600634759d9235ceba7f70 Mon Sep 17 00:00:00 2001 From: zong-zhe Date: Mon, 29 Aug 2022 17:56:45 +0800 Subject: [PATCH 03/22] rm useless comments --- .../error/src/diagnostic/diagnostic_message.rs | 9 --------- kclvm/compiler_base/error/src/lib.rs | 1 + 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/kclvm/compiler_base/error/src/diagnostic/diagnostic_message.rs b/kclvm/compiler_base/error/src/diagnostic/diagnostic_message.rs index a381f9ae7..b312ed2ee 100644 --- a/kclvm/compiler_base/error/src/diagnostic/diagnostic_message.rs +++ b/kclvm/compiler_base/error/src/diagnostic/diagnostic_message.rs @@ -11,15 +11,6 @@ use crate::MessageArgs; // Struct `TemplateLoader` load template contents from "*.ftl" file. // // `TemplateLoader` will operate on files locally. -// -// In order to avoid the performance loss and thread safety problems that -// may occur during the constructing the `TemplateLoader`, we close the constructor of `TemplateLoader`. -// -// You only need to pass the path of the "*.ftl" file to `DiagnosticHandler`, -// and `DiagnosticHandler` will automatically construct `TemplateLoader` and load the template file. -// -// `TemplateLoader` is only useful for you, when you want to get message from template file by `get_msg_to_str()`. -// For more information about how to use `get_msg_to_str()`, see the doc above `get_msg_to_str()`. pub(crate) struct TemplateLoader { template_inner: Arc, } diff --git a/kclvm/compiler_base/error/src/lib.rs b/kclvm/compiler_base/error/src/lib.rs index df13f600d..9fa08fe55 100644 --- a/kclvm/compiler_base/error/src/lib.rs +++ b/kclvm/compiler_base/error/src/lib.rs @@ -15,6 +15,7 @@ mod tests; /// `DiagnosticHandler` supports diagnostic messages to terminal stderr. /// /// `DiagnosticHandler` will load template file directory when instantiating through the constructor `new()`. +/// pub struct DiagnosticHandler { template_loader: Arc, emitter: Box>, From 1e949bb202be9514f1953082891922a584c1aa29 Mon Sep 17 00:00:00 2001 From: zong-zhe Date: Mon, 29 Aug 2022 19:48:37 +0800 Subject: [PATCH 04/22] add some comments and fix import mistake --- .../error/src/diagnostic/components.rs | 6 +- .../compiler_base/error/src/diagnostic/mod.rs | 14 ++-- .../error/src/diagnostic/style.rs | 2 +- kclvm/compiler_base/error/src/emitter.rs | 4 +- kclvm/compiler_base/error/src/lib.rs | 68 ++++++++++++++++++- 5 files changed, 80 insertions(+), 14 deletions(-) diff --git a/kclvm/compiler_base/error/src/diagnostic/components.rs b/kclvm/compiler_base/error/src/diagnostic/components.rs index 1f635285e..d64796639 100644 --- a/kclvm/compiler_base/error/src/diagnostic/components.rs +++ b/kclvm/compiler_base/error/src/diagnostic/components.rs @@ -8,9 +8,9 @@ use rustc_errors::styled_buffer::StyledBuffer; /// # Examples /// /// ```rust -/// # use crate::compiler_base_error::diagnostic::Component; -/// # use compiler_base_error::diagnostic::components::Label; -/// # use compiler_base_error::diagnostic::style::DiagnosticStyle; +/// # use compiler_base_error::Component; +/// # use compiler_base_error::components::Label; +/// # use compiler_base_error::DiagnosticStyle; /// # use rustc_errors::styled_buffer::StyledBuffer; /// /// let mut sb = StyledBuffer::::new(); diff --git a/kclvm/compiler_base/error/src/diagnostic/mod.rs b/kclvm/compiler_base/error/src/diagnostic/mod.rs index e7c11f5a9..8ccdc554f 100644 --- a/kclvm/compiler_base/error/src/diagnostic/mod.rs +++ b/kclvm/compiler_base/error/src/diagnostic/mod.rs @@ -1,7 +1,7 @@ pub use rustc_errors::styled_buffer::StyledBuffer; use rustc_errors::Style; -pub mod diagnostic_message; +pub mod diagnostic_message; pub mod components; pub mod style; @@ -22,9 +22,10 @@ where /// # Examples /// /// ```rust - /// # use compiler_base_error::diagnostic::style::DiagnosticStyle; - /// # use compiler_base_error::diagnostic::StyledBuffer; - /// # use compiler_base_error::diagnostic::Component; + /// # use compiler_base_error::Component; + /// # use compiler_base_error::DiagnosticStyle; + /// # use rustc_errors::styled_buffer::StyledBuffer; + /// /// struct ComponentWithStyleLogo { /// text: String /// } @@ -47,7 +48,10 @@ where /// /// ```rust /// # use rustc_errors::styled_buffer::StyledBuffer; -/// # use compiler_base_error::diagnostic::{Diagnostic, components::Label, style::DiagnosticStyle, Component}; +/// # use compiler_base_error::components::Label; +/// # use compiler_base_error::DiagnosticStyle; +/// # use compiler_base_error::Diagnostic; +/// # use compiler_base_error::Component; /// /// // If you want a diagnostic message “error[E3033]: this is an error!”. /// let mut diagnostic = Diagnostic::new(); diff --git a/kclvm/compiler_base/error/src/diagnostic/style.rs b/kclvm/compiler_base/error/src/diagnostic/style.rs index fca8e8985..5ed58af43 100644 --- a/kclvm/compiler_base/error/src/diagnostic/style.rs +++ b/kclvm/compiler_base/error/src/diagnostic/style.rs @@ -55,7 +55,7 @@ impl DiagnosticStyle { /// /// ```rust /// # use rustc_errors::Style; - /// # use compiler_base_error::diagnostic::style::DiagnosticStyle; + /// # use compiler_base_error::DiagnosticStyle; /// /// let mut color_spec = DiagnosticStyle::NeedFix.render_style_to_color_spec(); /// assert!(DiagnosticStyle::NeedFix.check_is_expected_colorspec(&color_spec)); diff --git a/kclvm/compiler_base/error/src/emitter.rs b/kclvm/compiler_base/error/src/emitter.rs index 148d80388..98149481f 100644 --- a/kclvm/compiler_base/error/src/emitter.rs +++ b/kclvm/compiler_base/error/src/emitter.rs @@ -107,8 +107,8 @@ where /// ```rust /// # use crate::compiler_base_error::Emitter; /// # use compiler_base_error::TerminalEmitter; -/// # use compiler_base_error::diagnostic::{components::Label, Diagnostic}; -/// # use compiler_base_error::diagnostic::style::DiagnosticStyle; +/// # use compiler_base_error::{components::Label, Diagnostic}; +/// # use compiler_base_error::DiagnosticStyle; /// /// // 1. Create a TerminalEmitter /// let mut term_emitter = TerminalEmitter::default(); diff --git a/kclvm/compiler_base/error/src/lib.rs b/kclvm/compiler_base/error/src/lib.rs index 9fa08fe55..b05792abf 100644 --- a/kclvm/compiler_base/error/src/lib.rs +++ b/kclvm/compiler_base/error/src/lib.rs @@ -3,11 +3,12 @@ mod emitter; use anyhow::{Context, Result}; use diagnostic::diagnostic_message::TemplateLoader; -use emitter::{Emitter, TerminalEmitter}; use fluent::FluentArgs; use std::sync::Arc; -pub use diagnostic::{style::DiagnosticStyle, Diagnostic}; +pub use emitter::{Emitter, TerminalEmitter}; +pub use diagnostic::{Component, components, style::DiagnosticStyle, Diagnostic}; + #[cfg(test)] mod tests; @@ -15,7 +16,68 @@ mod tests; /// `DiagnosticHandler` supports diagnostic messages to terminal stderr. /// /// `DiagnosticHandler` will load template file directory when instantiating through the constructor `new()`. -/// +/// +/// When your compiler needs to use `Compiler-Base-Error` to displaying diagnostics, you need to create a `DiagnosticHandler` at first. +/// For more information about how to create a `DiagnosticHandler`, see the doc above method `new_with_template_dir()`. +/// +/// Since creating `DiagnosticHandler` needs to load the locally template (*.ftl) file, it may cause I/O performance loss, +/// so we recommend you create `DiagnosticHandler` globally in the compiler and pass references to other modules that use `DiagnosticHandler`. +/// +/// And since `DiagnosticHandler` provides methods that need to change the contents of itself, +/// you need to pass mutable references, and if it is in a multi-threaded environment, you need to use `Arc>` +/// +/// For Example: +/// +/// 1. You can put `DiagnosticHandler` on the same level as `Lexer`, `Parser` and `CodeGenerator` in your compiler. +/// ```ignore +/// struct Compiler { +/// diag_handler: DiagnosticHandler, +/// lang_lexer: Lexer, +/// lang_parser: Parser, +/// code_generator: CodeGenerator +/// } +/// +/// // If it is in a multi-threaded environment, you can +/// struct Compiler { +/// diag_handler: Arc>, +/// lang_lexer: Lexer, +/// lang_parser: Parser, +/// code_generator: CodeGenerator +/// } +/// ``` +/// +/// 2. And send the mutable references to `Lexer`, `Parser` and `CodeGenerator` to displaying the diagnostic during compiling. +/// ```ignore +/// impl Compiler { +/// fn compile(&self) { +/// self.lang_lexer.lex(&mut self.diag_handler); +/// self.lang_parser.parse(&mut self.diag_handler); +/// self.code_generator.gen(&mut self.diag_handler); +/// } +/// } +/// ``` +/// // If it is in a multi-threaded environment, you can +/// ```ignore +/// impl Compiler { +/// fn compile(&self) { +/// self.lang_lexer.lex(Arc::clone(self.diag_handler)); +/// self.lang_parser.parse(Arc::clone(self.diag_handler)); +/// self.code_generator.gen(Arc::clone(self.diag_handler)); +/// } +/// } +/// ``` +/// +/// // If you use `Arc>`, maybe you need to `lock()` it before using it. +/// +/// ```ignore +/// impl Lexer { +/// fn lex(&self, diag_handler: Arc>){ +/// let handler = diag_handler.lock(); +/// handler.XXXX(); // do something to diaplay diagnostic. +/// } +/// } +/// ``` +/// pub struct DiagnosticHandler { template_loader: Arc, emitter: Box>, From 8127ccbf30906421e58ca07e603b7a2e207405e5 Mon Sep 17 00:00:00 2001 From: zong-zhe Date: Tue, 30 Aug 2022 11:19:41 +0800 Subject: [PATCH 05/22] try handler inner --- .../3rdparty/rustc_span/src/lib.rs | 2 +- .../compiler_base/error/src/diagnostic/mod.rs | 4 +- kclvm/compiler_base/error/src/lib.rs | 70 ++++++++++++++++--- kclvm/compiler_base/error/src/tests.rs | 34 ++++++++- kclvm/compiler_base/span/src/lib.rs | 1 + 5 files changed, 98 insertions(+), 13 deletions(-) diff --git a/kclvm/compiler_base/3rdparty/rustc_span/src/lib.rs b/kclvm/compiler_base/3rdparty/rustc_span/src/lib.rs index 83d6f1a24..548bafb36 100644 --- a/kclvm/compiler_base/3rdparty/rustc_span/src/lib.rs +++ b/kclvm/compiler_base/3rdparty/rustc_span/src/lib.rs @@ -14,7 +14,7 @@ //! This API is completely unstable and subject to change. mod caching_source_map_view; -mod fatal_error; +pub mod fatal_error; pub mod source_map; pub use self::caching_source_map_view::CachingSourceMapView; use rustc_data_structures::sync::Lrc; diff --git a/kclvm/compiler_base/error/src/diagnostic/mod.rs b/kclvm/compiler_base/error/src/diagnostic/mod.rs index 8ccdc554f..09db58120 100644 --- a/kclvm/compiler_base/error/src/diagnostic/mod.rs +++ b/kclvm/compiler_base/error/src/diagnostic/mod.rs @@ -1,8 +1,8 @@ pub use rustc_errors::styled_buffer::StyledBuffer; use rustc_errors::Style; -pub mod diagnostic_message; pub mod components; +pub mod diagnostic_message; pub mod style; #[cfg(test)] @@ -25,7 +25,7 @@ where /// # use compiler_base_error::Component; /// # use compiler_base_error::DiagnosticStyle; /// # use rustc_errors::styled_buffer::StyledBuffer; - /// + /// /// struct ComponentWithStyleLogo { /// text: String /// } diff --git a/kclvm/compiler_base/error/src/lib.rs b/kclvm/compiler_base/error/src/lib.rs index b05792abf..69b5f38d0 100644 --- a/kclvm/compiler_base/error/src/lib.rs +++ b/kclvm/compiler_base/error/src/lib.rs @@ -2,13 +2,13 @@ mod diagnostic; mod emitter; use anyhow::{Context, Result}; +use compiler_base_span::fatal_error::FatalError; use diagnostic::diagnostic_message::TemplateLoader; use fluent::FluentArgs; -use std::sync::Arc; +use std::{panic::UnwindSafe, sync::Arc}; +pub use diagnostic::{components, style::DiagnosticStyle, Component, Diagnostic}; pub use emitter::{Emitter, TerminalEmitter}; -pub use diagnostic::{Component, components, style::DiagnosticStyle, Diagnostic}; - #[cfg(test)] mod tests; @@ -82,6 +82,8 @@ pub struct DiagnosticHandler { template_loader: Arc, emitter: Box>, diagnostics: Vec>, + err_count: usize, + warn_count: usize, } impl DiagnosticHandler { @@ -119,13 +121,15 @@ impl DiagnosticHandler { let template_loader = TemplateLoader::new_with_template_dir(template_dir) .with_context(|| format!("Failed to init `TemplateLoader` from '{}'", template_dir))?; Ok(Self { + err_count: 0, + warn_count: 0, template_loader: Arc::new(template_loader), emitter: Box::new(TerminalEmitter::default()), diagnostics: vec![], }) } - /// Add a diagnostic to `DiagnosticHandler`. + /// Add a diagnostic generated from error to `DiagnosticHandler`. /// `DiagnosticHandler` contains a set of `Diagnostic` /// /// # Examples @@ -138,11 +142,33 @@ impl DiagnosticHandler { /// let mut diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); /// assert_eq!(diag_handler.diagnostics_count(), 0); /// - /// diag_handler.add_diagnostic(diag_1); + /// diag_handler.add_err_diagnostic(diag_1); /// assert_eq!(diag_handler.diagnostics_count(), 1); /// ``` - pub fn add_diagnostic(&mut self, diag: Diagnostic) { + pub fn add_err_diagnostic(&mut self, diag: Diagnostic) { self.diagnostics.push(diag); + self.err_count += 1; + } + + /// Add a diagnostic generated from warning to `DiagnosticHandler`. + /// `DiagnosticHandler` contains a set of `Diagnostic` + /// + /// # Examples + /// + /// ```rust + /// # use compiler_base_error::DiagnosticStyle; + /// # use compiler_base_error::DiagnosticHandler; + /// # use compiler_base_error::Diagnostic; + /// let diag_1 = Diagnostic::::new(); + /// let mut diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); + /// assert_eq!(diag_handler.diagnostics_count(), 0); + /// + /// diag_handler.add_warn_diagnostic(diag_1); + /// assert_eq!(diag_handler.diagnostics_count(), 1); + /// ``` + pub fn add_warn_diagnostic(&mut self, diag: Diagnostic) { + self.diagnostics.push(diag); + self.warn_count += 1; } /// Get count of diagnostics in `DiagnosticHandler`. @@ -151,19 +177,45 @@ impl DiagnosticHandler { self.diagnostics.len() } - /// Emit the diagnostic messages to to terminal stderr. - pub fn emit_diagnostic(&mut self, diag: Diagnostic) { + /// Emit the diagnostic messages generated from error to to terminal stderr. + pub fn emit_error_diagnostic(&mut self, diag: Diagnostic) { self.emitter.emit_diagnostic(&diag); + self.err_count += 1; + } + + /// Emit the diagnostic messages generated from warning to to terminal stderr. + pub fn emit_warn_diagnostic(&mut self, diag: Diagnostic) { + self.emitter.emit_diagnostic(&diag); + self.warn_count += 1; } /// Emit all the diagnostics messages to to terminal stderr. /// `DiagnosticHandler` contains a set of `Diagnostic` - pub fn emit_all_diagnostics(&mut self) { + pub fn emit_stashed_diagnostics(&mut self) { for diag in &self.diagnostics { self.emitter.emit_diagnostic(&diag) } } + /// If some diagnotsics generated by errors, `has_errors` returns `True`. + pub fn has_errors(&self) -> bool { + self.err_count > 0 + } + + /// If some diagnotsics generated by warnings, `has_errors` returns `True`. + pub fn has_warns(&self) -> bool { + self.warn_count > 0 + } + + /// After emitting all the diagnostics, it will panic. + pub fn abort_if_errors(&mut self) { + self.emit_stashed_diagnostics(); + + if self.has_errors() { + FatalError.raise(); + } + } + /// Get the message string from "*.ftl" file by `index`, `sub_index` and `MessageArgs`. /// "*.ftl" file looks like, e.g. './src/diagnostic/locales/en-US/default.ftl' : /// diff --git a/kclvm/compiler_base/error/src/tests.rs b/kclvm/compiler_base/error/src/tests.rs index a8dbbf567..d4f058e41 100644 --- a/kclvm/compiler_base/error/src/tests.rs +++ b/kclvm/compiler_base/error/src/tests.rs @@ -1,4 +1,6 @@ mod test_diagnostic_handler { + use std::panic; + use crate::{Diagnostic, DiagnosticHandler, DiagnosticStyle, MessageArgs}; #[test] @@ -28,7 +30,7 @@ mod test_diagnostic_handler { DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); assert_eq!(diag_handler.diagnostics_count(), 0); - diag_handler.add_diagnostic(diag_1); + diag_handler.add_err_diagnostic(diag_1); assert_eq!(diag_handler.diagnostics_count(), 1); } @@ -55,4 +57,34 @@ mod test_diagnostic_handler { "Expected one of `\u{2068}I am an expected item\u{2069}`" ); } + + #[test] + fn test_diagnostic_handler_has() { + let mut diag_handler = + DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); + // test has_errors() + assert_eq!(diag_handler.has_errors(), false); + diag_handler.add_err_diagnostic(Diagnostic::::new()); + assert_eq!(diag_handler.has_errors(), true); + + // test has_warns() + assert_eq!(diag_handler.has_warns(), false); + diag_handler.add_warn_diagnostic(Diagnostic::::new()); + assert_eq!(diag_handler.has_warns(), true); + } + + #[test] + fn test_abort_if_errors() { + let mut diag_handler = + DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); + diag_handler.abort_if_errors(); + diag_handler.add_warn_diagnostic(Diagnostic::::new()); + diag_handler.abort_if_errors(); + diag_handler.add_err_diagnostic(Diagnostic::::new()); + + let result = panic::catch_unwind(|| { + diag_handler.abort_if_errors(); + }); + assert!(result.is_err()); + } } diff --git a/kclvm/compiler_base/span/src/lib.rs b/kclvm/compiler_base/span/src/lib.rs index 44c2ed650..50c8ae215 100644 --- a/kclvm/compiler_base/span/src/lib.rs +++ b/kclvm/compiler_base/span/src/lib.rs @@ -8,6 +8,7 @@ //! Reference: https://github.com/rust-lang/rust/blob/master/compiler/rustc_span/src/lib.rs pub mod span; +pub use rustc_span::fatal_error; pub use span::{BytePos, Span, DUMMY_SP}; pub type SourceMap = rustc_span::SourceMap; From 0ab39dc11d3128367648095c0cfa36c829c14272 Mon Sep 17 00:00:00 2001 From: zong-zhe Date: Tue, 30 Aug 2022 14:52:50 +0800 Subject: [PATCH 06/22] fix uncatched panic --- .../error/src/diagnostic_handler.rs | 87 +++++++++++++++++++ kclvm/compiler_base/error/src/lib.rs | 78 +++++++++-------- kclvm/compiler_base/error/src/tests.rs | 6 +- 3 files changed, 130 insertions(+), 41 deletions(-) create mode 100644 kclvm/compiler_base/error/src/diagnostic_handler.rs diff --git a/kclvm/compiler_base/error/src/diagnostic_handler.rs b/kclvm/compiler_base/error/src/diagnostic_handler.rs new file mode 100644 index 000000000..1fb13e2c3 --- /dev/null +++ b/kclvm/compiler_base/error/src/diagnostic_handler.rs @@ -0,0 +1,87 @@ +use crate::{ + diagnostic::diagnostic_message::TemplateLoader, Diagnostic, DiagnosticStyle, Emitter, + MessageArgs, +}; +use anyhow::Result; +use compiler_base_span::fatal_error::FatalError; +use std::sync::Arc; + +pub(crate) struct DiagnosticHandlerInner { + pub(crate) emitter: Box>, + pub(crate) diagnostics: Vec>, + pub(crate) err_count: usize, + pub(crate) warn_count: usize, + pub(crate) template_loader: Arc, +} + +impl DiagnosticHandlerInner { + // Add a diagnostic generated from error to `DiagnosticHandler`. + // `DiagnosticHandler` contains a set of `Diagnostic` + pub(crate) fn add_err_diagnostic(&mut self, diag: Diagnostic) { + self.diagnostics.push(diag); + self.err_count += 1; + } + + // Add a diagnostic generated from warning to `DiagnosticHandler`. + // `DiagnosticHandler` contains a set of `Diagnostic` + pub(crate) fn add_warn_diagnostic(&mut self, diag: Diagnostic) { + self.diagnostics.push(diag); + self.warn_count += 1; + } + + // Get count of diagnostics in `DiagnosticHandler`. + // `DiagnosticHandler` contains a set of `Diagnostic` + pub(crate) fn diagnostics_count(&self) -> usize { + self.diagnostics.len() + } + + // Emit the diagnostic messages generated from error to to terminal stderr. + pub(crate) fn emit_error_diagnostic(&mut self, diag: Diagnostic) { + self.emitter.emit_diagnostic(&diag); + self.err_count += 1; + } + + // Emit the diagnostic messages generated from warning to to terminal stderr. + pub(crate) fn emit_warn_diagnostic(&mut self, diag: Diagnostic) { + self.emitter.emit_diagnostic(&diag); + self.warn_count += 1; + } + + // Emit all the diagnostics messages to to terminal stderr. + // `DiagnosticHandler` contains a set of `Diagnostic` + pub(crate) fn emit_stashed_diagnostics(&mut self) { + for diag in &self.diagnostics { + self.emitter.emit_diagnostic(&diag) + } + } + + // If some diagnotsics generated by errors, `has_errors` returns `True`. + pub(crate) fn has_errors(&self) -> bool { + self.err_count > 0 + } + + // If some diagnotsics generated by warnings, `has_errors` returns `True`. + pub(crate) fn has_warns(&self) -> bool { + self.warn_count > 0 + } + + // After emitting all the diagnostics, it will panic. + pub(crate) fn abort_if_errors(&mut self) { + self.emit_stashed_diagnostics(); + + if self.has_errors() { + FatalError.raise(); + } + } + + // Get the message string from "*.ftl" file by `index`, `sub_index` and `MessageArgs`. + // "*.ftl" file looks like, e.g. './src/diagnostic/locales/en-US/default.ftl' : + pub(crate) fn get_diagnostic_msg( + &self, + index: &str, + sub_index: Option<&str>, + args: &MessageArgs, + ) -> Result { + self.template_loader.get_msg_to_str(index, sub_index, &args) + } +} diff --git a/kclvm/compiler_base/error/src/lib.rs b/kclvm/compiler_base/error/src/lib.rs index 69b5f38d0..f2d83f881 100644 --- a/kclvm/compiler_base/error/src/lib.rs +++ b/kclvm/compiler_base/error/src/lib.rs @@ -1,11 +1,13 @@ mod diagnostic; +mod diagnostic_handler; mod emitter; use anyhow::{Context, Result}; -use compiler_base_span::fatal_error::FatalError; use diagnostic::diagnostic_message::TemplateLoader; +use diagnostic_handler::DiagnosticHandlerInner; use fluent::FluentArgs; -use std::{panic::UnwindSafe, sync::Arc}; +use std::sync::Arc; +use std::sync::Mutex; pub use diagnostic::{components, style::DiagnosticStyle, Component, Diagnostic}; pub use emitter::{Emitter, TerminalEmitter}; @@ -79,11 +81,7 @@ mod tests; /// ``` /// pub struct DiagnosticHandler { - template_loader: Arc, - emitter: Box>, - diagnostics: Vec>, - err_count: usize, - warn_count: usize, + handler_inner: Mutex, } impl DiagnosticHandler { @@ -121,11 +119,13 @@ impl DiagnosticHandler { let template_loader = TemplateLoader::new_with_template_dir(template_dir) .with_context(|| format!("Failed to init `TemplateLoader` from '{}'", template_dir))?; Ok(Self { - err_count: 0, - warn_count: 0, - template_loader: Arc::new(template_loader), - emitter: Box::new(TerminalEmitter::default()), - diagnostics: vec![], + handler_inner: Mutex::new(DiagnosticHandlerInner { + err_count: 0, + warn_count: 0, + emitter: Box::new(TerminalEmitter::default()), + diagnostics: vec![], + template_loader: Arc::new(template_loader), + }), }) } @@ -145,9 +145,8 @@ impl DiagnosticHandler { /// diag_handler.add_err_diagnostic(diag_1); /// assert_eq!(diag_handler.diagnostics_count(), 1); /// ``` - pub fn add_err_diagnostic(&mut self, diag: Diagnostic) { - self.diagnostics.push(diag); - self.err_count += 1; + pub fn add_err_diagnostic(&self, diag: Diagnostic) { + self.handler_inner.lock().unwrap().add_err_diagnostic(diag); } /// Add a diagnostic generated from warning to `DiagnosticHandler`. @@ -166,54 +165,54 @@ impl DiagnosticHandler { /// diag_handler.add_warn_diagnostic(diag_1); /// assert_eq!(diag_handler.diagnostics_count(), 1); /// ``` - pub fn add_warn_diagnostic(&mut self, diag: Diagnostic) { - self.diagnostics.push(diag); - self.warn_count += 1; + pub fn add_warn_diagnostic(&self, diag: Diagnostic) { + self.handler_inner.lock().unwrap().add_warn_diagnostic(diag); } /// Get count of diagnostics in `DiagnosticHandler`. /// `DiagnosticHandler` contains a set of `Diagnostic` pub fn diagnostics_count(&self) -> usize { - self.diagnostics.len() + self.handler_inner.lock().unwrap().diagnostics_count() } /// Emit the diagnostic messages generated from error to to terminal stderr. - pub fn emit_error_diagnostic(&mut self, diag: Diagnostic) { - self.emitter.emit_diagnostic(&diag); - self.err_count += 1; + pub fn emit_error_diagnostic(&self, diag: Diagnostic) { + self.handler_inner + .lock() + .unwrap() + .emit_error_diagnostic(diag); } /// Emit the diagnostic messages generated from warning to to terminal stderr. - pub fn emit_warn_diagnostic(&mut self, diag: Diagnostic) { - self.emitter.emit_diagnostic(&diag); - self.warn_count += 1; + pub fn emit_warn_diagnostic(&self, diag: Diagnostic) { + self.handler_inner + .lock() + .unwrap() + .emit_warn_diagnostic(diag); } /// Emit all the diagnostics messages to to terminal stderr. /// `DiagnosticHandler` contains a set of `Diagnostic` - pub fn emit_stashed_diagnostics(&mut self) { - for diag in &self.diagnostics { - self.emitter.emit_diagnostic(&diag) - } + pub fn emit_stashed_diagnostics(&self) { + self.handler_inner + .lock() + .unwrap() + .emit_stashed_diagnostics(); } /// If some diagnotsics generated by errors, `has_errors` returns `True`. pub fn has_errors(&self) -> bool { - self.err_count > 0 + self.handler_inner.lock().unwrap().has_errors() } /// If some diagnotsics generated by warnings, `has_errors` returns `True`. pub fn has_warns(&self) -> bool { - self.warn_count > 0 + self.handler_inner.lock().unwrap().has_warns() } /// After emitting all the diagnostics, it will panic. - pub fn abort_if_errors(&mut self) { - self.emit_stashed_diagnostics(); - - if self.has_errors() { - FatalError.raise(); - } + pub fn abort_if_errors(&self) { + self.handler_inner.lock().unwrap().abort_if_errors() } /// Get the message string from "*.ftl" file by `index`, `sub_index` and `MessageArgs`. @@ -290,7 +289,10 @@ impl DiagnosticHandler { sub_index: Option<&str>, args: &MessageArgs, ) -> Result { - self.template_loader.get_msg_to_str(index, sub_index, &args) + self.handler_inner + .lock() + .unwrap() + .get_diagnostic_msg(index, sub_index, args) } } diff --git a/kclvm/compiler_base/error/src/tests.rs b/kclvm/compiler_base/error/src/tests.rs index d4f058e41..de5cfb315 100644 --- a/kclvm/compiler_base/error/src/tests.rs +++ b/kclvm/compiler_base/error/src/tests.rs @@ -26,7 +26,7 @@ mod test_diagnostic_handler { #[test] fn test_diagnostic_handler_add_diagnostic() { let diag_1 = Diagnostic::::new(); - let mut diag_handler = + let diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); assert_eq!(diag_handler.diagnostics_count(), 0); @@ -60,7 +60,7 @@ mod test_diagnostic_handler { #[test] fn test_diagnostic_handler_has() { - let mut diag_handler = + let diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); // test has_errors() assert_eq!(diag_handler.has_errors(), false); @@ -75,7 +75,7 @@ mod test_diagnostic_handler { #[test] fn test_abort_if_errors() { - let mut diag_handler = + let diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); diag_handler.abort_if_errors(); diag_handler.add_warn_diagnostic(Diagnostic::::new()); From 05285f080a402c7ddef09e69d51db2f5fa94296e Mon Sep 17 00:00:00 2001 From: zong-zhe Date: Tue, 30 Aug 2022 15:21:03 +0800 Subject: [PATCH 07/22] use match replace unwrap --- .../error/src/diagnostic_handler.rs | 26 +++- kclvm/compiler_base/error/src/lib.rs | 120 +++++++++++------- kclvm/compiler_base/error/src/tests.rs | 12 +- 3 files changed, 99 insertions(+), 59 deletions(-) diff --git a/kclvm/compiler_base/error/src/diagnostic_handler.rs b/kclvm/compiler_base/error/src/diagnostic_handler.rs index 1fb13e2c3..254f851ac 100644 --- a/kclvm/compiler_base/error/src/diagnostic_handler.rs +++ b/kclvm/compiler_base/error/src/diagnostic_handler.rs @@ -1,20 +1,32 @@ use crate::{ diagnostic::diagnostic_message::TemplateLoader, Diagnostic, DiagnosticStyle, Emitter, - MessageArgs, + MessageArgs, TerminalEmitter, }; -use anyhow::Result; +use anyhow::{Context, Result}; use compiler_base_span::fatal_error::FatalError; use std::sync::Arc; pub(crate) struct DiagnosticHandlerInner { - pub(crate) emitter: Box>, - pub(crate) diagnostics: Vec>, - pub(crate) err_count: usize, - pub(crate) warn_count: usize, - pub(crate) template_loader: Arc, + emitter: Box>, + diagnostics: Vec>, + err_count: usize, + warn_count: usize, + template_loader: Arc, } impl DiagnosticHandlerInner { + pub(crate) fn new_with_template_dir(template_dir: &str) -> Result { + let template_loader = TemplateLoader::new_with_template_dir(template_dir) + .with_context(|| format!("Failed to init `TemplateLoader` from '{}'", template_dir))?; + + Ok(Self { + err_count: 0, + warn_count: 0, + emitter: Box::new(TerminalEmitter::default()), + diagnostics: vec![], + template_loader: Arc::new(template_loader), + }) + } // Add a diagnostic generated from error to `DiagnosticHandler`. // `DiagnosticHandler` contains a set of `Diagnostic` pub(crate) fn add_err_diagnostic(&mut self, diag: Diagnostic) { diff --git a/kclvm/compiler_base/error/src/lib.rs b/kclvm/compiler_base/error/src/lib.rs index f2d83f881..5ea49da70 100644 --- a/kclvm/compiler_base/error/src/lib.rs +++ b/kclvm/compiler_base/error/src/lib.rs @@ -2,11 +2,9 @@ mod diagnostic; mod diagnostic_handler; mod emitter; -use anyhow::{Context, Result}; -use diagnostic::diagnostic_message::TemplateLoader; +use anyhow::{bail, Context, Result}; use diagnostic_handler::DiagnosticHandlerInner; use fluent::FluentArgs; -use std::sync::Arc; use std::sync::Mutex; pub use diagnostic::{components, style::DiagnosticStyle, Component, Diagnostic}; @@ -116,16 +114,10 @@ impl DiagnosticHandler { /// } /// ``` pub fn new_with_template_dir(template_dir: &str) -> Result { - let template_loader = TemplateLoader::new_with_template_dir(template_dir) + let handler_inner = DiagnosticHandlerInner::new_with_template_dir(template_dir) .with_context(|| format!("Failed to init `TemplateLoader` from '{}'", template_dir))?; Ok(Self { - handler_inner: Mutex::new(DiagnosticHandlerInner { - err_count: 0, - warn_count: 0, - emitter: Box::new(TerminalEmitter::default()), - diagnostics: vec![], - template_loader: Arc::new(template_loader), - }), + handler_inner: Mutex::new(handler_inner), }) } @@ -140,13 +132,19 @@ impl DiagnosticHandler { /// # use compiler_base_error::Diagnostic; /// let diag_1 = Diagnostic::::new(); /// let mut diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); - /// assert_eq!(diag_handler.diagnostics_count(), 0); + /// assert_eq!(diag_handler.diagnostics_count().unwrap(), 0); /// /// diag_handler.add_err_diagnostic(diag_1); - /// assert_eq!(diag_handler.diagnostics_count(), 1); + /// assert_eq!(diag_handler.diagnostics_count().unwrap(), 1); /// ``` - pub fn add_err_diagnostic(&self, diag: Diagnostic) { - self.handler_inner.lock().unwrap().add_err_diagnostic(diag); + pub fn add_err_diagnostic(&self, diag: Diagnostic) -> Result<()> { + match self.handler_inner.lock() { + Ok(mut inner) => { + inner.add_err_diagnostic(diag); + Ok(()) + } + Err(_) => bail!("Add Error Diagnostic Failed."), + } } /// Add a diagnostic generated from warning to `DiagnosticHandler`. @@ -160,59 +158,89 @@ impl DiagnosticHandler { /// # use compiler_base_error::Diagnostic; /// let diag_1 = Diagnostic::::new(); /// let mut diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); - /// assert_eq!(diag_handler.diagnostics_count(), 0); + /// assert_eq!(diag_handler.diagnostics_count().unwrap(), 0); /// /// diag_handler.add_warn_diagnostic(diag_1); - /// assert_eq!(diag_handler.diagnostics_count(), 1); + /// assert_eq!(diag_handler.diagnostics_count().unwrap(), 1); /// ``` - pub fn add_warn_diagnostic(&self, diag: Diagnostic) { - self.handler_inner.lock().unwrap().add_warn_diagnostic(diag); + pub fn add_warn_diagnostic(&self, diag: Diagnostic) -> Result<()> { + match self.handler_inner.lock() { + Ok(mut inner) => { + inner.add_warn_diagnostic(diag); + Ok(()) + } + Err(_) => bail!("Add Warn Diagnostic Failed."), + } } /// Get count of diagnostics in `DiagnosticHandler`. /// `DiagnosticHandler` contains a set of `Diagnostic` - pub fn diagnostics_count(&self) -> usize { - self.handler_inner.lock().unwrap().diagnostics_count() + pub fn diagnostics_count(&self) -> Result { + match self.handler_inner.lock() { + Ok(inner) => Ok(inner.diagnostics_count()), + Err(_) => bail!("Diagnostics Counts Failed."), + } } /// Emit the diagnostic messages generated from error to to terminal stderr. - pub fn emit_error_diagnostic(&self, diag: Diagnostic) { - self.handler_inner - .lock() - .unwrap() - .emit_error_diagnostic(diag); + pub fn emit_error_diagnostic(&self, diag: Diagnostic) -> Result<()> { + match self.handler_inner.lock() { + Ok(mut inner) => { + inner.emit_error_diagnostic(diag); + Ok(()) + } + Err(_) => bail!("Emit Error Diagnostics Failed."), + } } /// Emit the diagnostic messages generated from warning to to terminal stderr. - pub fn emit_warn_diagnostic(&self, diag: Diagnostic) { - self.handler_inner - .lock() - .unwrap() - .emit_warn_diagnostic(diag); + pub fn emit_warn_diagnostic(&self, diag: Diagnostic) -> Result<()> { + match self.handler_inner.lock() { + Ok(mut inner) => { + inner.emit_warn_diagnostic(diag); + Ok(()) + } + Err(_) => bail!("Emit Warn Diagnostics Failed."), + } } /// Emit all the diagnostics messages to to terminal stderr. /// `DiagnosticHandler` contains a set of `Diagnostic` - pub fn emit_stashed_diagnostics(&self) { - self.handler_inner - .lock() - .unwrap() - .emit_stashed_diagnostics(); + pub fn emit_stashed_diagnostics(&self) -> Result<()> { + match self.handler_inner.lock() { + Ok(mut inner) => { + inner.emit_stashed_diagnostics(); + Ok(()) + } + Err(_) => bail!("Emit Stashed Diagnostics Failed."), + } } /// If some diagnotsics generated by errors, `has_errors` returns `True`. - pub fn has_errors(&self) -> bool { - self.handler_inner.lock().unwrap().has_errors() + pub fn has_errors(&self) -> Result { + match self.handler_inner.lock() { + Ok(inner) => Ok(inner.has_errors()), + Err(_) => bail!("Check Has Errors Failed."), + } } /// If some diagnotsics generated by warnings, `has_errors` returns `True`. - pub fn has_warns(&self) -> bool { - self.handler_inner.lock().unwrap().has_warns() + pub fn has_warns(&self) -> Result { + match self.handler_inner.lock() { + Ok(inner) => Ok(inner.has_warns()), + Err(_) => bail!("Check Has Warns Failed."), + } } /// After emitting all the diagnostics, it will panic. - pub fn abort_if_errors(&self) { - self.handler_inner.lock().unwrap().abort_if_errors() + pub fn abort_if_errors(&self) -> Result<()> { + match self.handler_inner.lock() { + Ok(mut inner) => { + inner.abort_if_errors(); + Ok(()) + } + Err(_) => bail!("Abort If Errors Failed."), + } } /// Get the message string from "*.ftl" file by `index`, `sub_index` and `MessageArgs`. @@ -289,10 +317,10 @@ impl DiagnosticHandler { sub_index: Option<&str>, args: &MessageArgs, ) -> Result { - self.handler_inner - .lock() - .unwrap() - .get_diagnostic_msg(index, sub_index, args) + match self.handler_inner.lock() { + Ok(inner) => inner.get_diagnostic_msg(index, sub_index, args), + Err(_) => bail!("Find Diagnostic Message Failed."), + } } } diff --git a/kclvm/compiler_base/error/src/tests.rs b/kclvm/compiler_base/error/src/tests.rs index de5cfb315..611952175 100644 --- a/kclvm/compiler_base/error/src/tests.rs +++ b/kclvm/compiler_base/error/src/tests.rs @@ -28,10 +28,10 @@ mod test_diagnostic_handler { let diag_1 = Diagnostic::::new(); let diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); - assert_eq!(diag_handler.diagnostics_count(), 0); + assert_eq!(diag_handler.diagnostics_count().unwrap(), 0); diag_handler.add_err_diagnostic(diag_1); - assert_eq!(diag_handler.diagnostics_count(), 1); + assert_eq!(diag_handler.diagnostics_count().unwrap(), 1); } #[test] @@ -63,14 +63,14 @@ mod test_diagnostic_handler { let diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); // test has_errors() - assert_eq!(diag_handler.has_errors(), false); + assert_eq!(diag_handler.has_errors().unwrap(), false); diag_handler.add_err_diagnostic(Diagnostic::::new()); - assert_eq!(diag_handler.has_errors(), true); + assert_eq!(diag_handler.has_errors().unwrap(), true); // test has_warns() - assert_eq!(diag_handler.has_warns(), false); + assert_eq!(diag_handler.has_warns().unwrap(), false); diag_handler.add_warn_diagnostic(Diagnostic::::new()); - assert_eq!(diag_handler.has_warns(), true); + assert_eq!(diag_handler.has_warns().unwrap(), true); } #[test] From 0935668cea453754c705b7a8101c64146541f7f4 Mon Sep 17 00:00:00 2001 From: zong-zhe Date: Tue, 30 Aug 2022 15:29:36 +0800 Subject: [PATCH 08/22] add some comments about mutex --- kclvm/compiler_base/error/src/lib.rs | 6 ++++-- kclvm/compiler_base/error/src/tests.rs | 16 ++++++++-------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/kclvm/compiler_base/error/src/lib.rs b/kclvm/compiler_base/error/src/lib.rs index 5ea49da70..ed3a244f3 100644 --- a/kclvm/compiler_base/error/src/lib.rs +++ b/kclvm/compiler_base/error/src/lib.rs @@ -15,11 +15,13 @@ mod tests; /// `DiagnosticHandler` supports diagnostic messages to terminal stderr. /// -/// `DiagnosticHandler` will load template file directory when instantiating through the constructor `new()`. +/// `DiagnosticHandler` will load template file directory when instantiating through the constructor `new_with_template_dir()`. +/// +/// Note: `DiagnosticHandler` uses `Mutex` to ensure thread safety, so when you use different threads to call methods provided in `DiagnosticHandler`, +/// they will compete for the lock, and the method that got the lock will be called first. /// /// When your compiler needs to use `Compiler-Base-Error` to displaying diagnostics, you need to create a `DiagnosticHandler` at first. /// For more information about how to create a `DiagnosticHandler`, see the doc above method `new_with_template_dir()`. -/// /// Since creating `DiagnosticHandler` needs to load the locally template (*.ftl) file, it may cause I/O performance loss, /// so we recommend you create `DiagnosticHandler` globally in the compiler and pass references to other modules that use `DiagnosticHandler`. /// diff --git a/kclvm/compiler_base/error/src/tests.rs b/kclvm/compiler_base/error/src/tests.rs index 611952175..1992d066d 100644 --- a/kclvm/compiler_base/error/src/tests.rs +++ b/kclvm/compiler_base/error/src/tests.rs @@ -30,7 +30,7 @@ mod test_diagnostic_handler { DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); assert_eq!(diag_handler.diagnostics_count().unwrap(), 0); - diag_handler.add_err_diagnostic(diag_1); + diag_handler.add_err_diagnostic(diag_1).unwrap(); assert_eq!(diag_handler.diagnostics_count().unwrap(), 1); } @@ -64,12 +64,12 @@ mod test_diagnostic_handler { DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); // test has_errors() assert_eq!(diag_handler.has_errors().unwrap(), false); - diag_handler.add_err_diagnostic(Diagnostic::::new()); + diag_handler.add_err_diagnostic(Diagnostic::::new()).unwrap(); assert_eq!(diag_handler.has_errors().unwrap(), true); // test has_warns() assert_eq!(diag_handler.has_warns().unwrap(), false); - diag_handler.add_warn_diagnostic(Diagnostic::::new()); + diag_handler.add_warn_diagnostic(Diagnostic::::new()).unwrap(); assert_eq!(diag_handler.has_warns().unwrap(), true); } @@ -77,13 +77,13 @@ mod test_diagnostic_handler { fn test_abort_if_errors() { let diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); - diag_handler.abort_if_errors(); - diag_handler.add_warn_diagnostic(Diagnostic::::new()); - diag_handler.abort_if_errors(); - diag_handler.add_err_diagnostic(Diagnostic::::new()); + diag_handler.abort_if_errors().unwrap(); + diag_handler.add_warn_diagnostic(Diagnostic::::new()).unwrap(); + diag_handler.abort_if_errors().unwrap(); + diag_handler.add_err_diagnostic(Diagnostic::::new()).unwrap(); let result = panic::catch_unwind(|| { - diag_handler.abort_if_errors(); + diag_handler.abort_if_errors().unwrap(); }); assert!(result.is_err()); } From 4e540e27a477e4b13b203405fefbeeedd6484f9c Mon Sep 17 00:00:00 2001 From: zong-zhe Date: Tue, 30 Aug 2022 15:55:04 +0800 Subject: [PATCH 09/22] add docstring in comments --- .../src/diagnostic/diagnostic_message.rs | 3 - .../error/src/diagnostic_handler.rs | 2 + kclvm/compiler_base/error/src/lib.rs | 146 ++++++++++++++---- kclvm/compiler_base/error/src/tests.rs | 16 +- 4 files changed, 129 insertions(+), 38 deletions(-) diff --git a/kclvm/compiler_base/error/src/diagnostic/diagnostic_message.rs b/kclvm/compiler_base/error/src/diagnostic/diagnostic_message.rs index b312ed2ee..645b17521 100644 --- a/kclvm/compiler_base/error/src/diagnostic/diagnostic_message.rs +++ b/kclvm/compiler_base/error/src/diagnostic/diagnostic_message.rs @@ -9,7 +9,6 @@ use walkdir::{DirEntry, WalkDir}; use crate::MessageArgs; // Struct `TemplateLoader` load template contents from "*.ftl" file. -// // `TemplateLoader` will operate on files locally. pub(crate) struct TemplateLoader { template_inner: Arc, @@ -18,7 +17,6 @@ pub(crate) struct TemplateLoader { impl TemplateLoader { // Create the `TemplateLoader` with template (*.ftl) files directory. // `TemplateLoader` will load all the files end with "*.ftl" under the directory recursively. - // // template_files // | // |---- template.ftl @@ -37,7 +35,6 @@ impl TemplateLoader { // Get the message string from "*.ftl" file by `index`, `sub_index` and `MessageArgs`. // For more information about "*.ftl" file, see the doc above `DiagnosticHandler`. - // // "*.ftl" file looks like, e.g. './src/diagnostic/locales/en-US/default.ftl' : pub(crate) fn get_msg_to_str( &self, diff --git a/kclvm/compiler_base/error/src/diagnostic_handler.rs b/kclvm/compiler_base/error/src/diagnostic_handler.rs index 254f851ac..e73237736 100644 --- a/kclvm/compiler_base/error/src/diagnostic_handler.rs +++ b/kclvm/compiler_base/error/src/diagnostic_handler.rs @@ -15,6 +15,7 @@ pub(crate) struct DiagnosticHandlerInner { } impl DiagnosticHandlerInner { + // Load all (*.ftl) template files under directory `template_dir`. pub(crate) fn new_with_template_dir(template_dir: &str) -> Result { let template_loader = TemplateLoader::new_with_template_dir(template_dir) .with_context(|| format!("Failed to init `TemplateLoader` from '{}'", template_dir))?; @@ -27,6 +28,7 @@ impl DiagnosticHandlerInner { template_loader: Arc::new(template_loader), }) } + // Add a diagnostic generated from error to `DiagnosticHandler`. // `DiagnosticHandler` contains a set of `Diagnostic` pub(crate) fn add_err_diagnostic(&mut self, diag: Diagnostic) { diff --git a/kclvm/compiler_base/error/src/lib.rs b/kclvm/compiler_base/error/src/lib.rs index ed3a244f3..9be14e9d5 100644 --- a/kclvm/compiler_base/error/src/lib.rs +++ b/kclvm/compiler_base/error/src/lib.rs @@ -16,17 +16,16 @@ mod tests; /// `DiagnosticHandler` supports diagnostic messages to terminal stderr. /// /// `DiagnosticHandler` will load template file directory when instantiating through the constructor `new_with_template_dir()`. -/// -/// Note: `DiagnosticHandler` uses `Mutex` to ensure thread safety, so when you use different threads to call methods provided in `DiagnosticHandler`, -/// they will compete for the lock, and the method that got the lock will be called first. +/// +/// Note: `DiagnosticHandler` uses `Mutex` internally to ensure thread safety, +/// so you don't need to use references like `Arc` or `Mutex` to make `DiagnosticHandler` thread safe. /// /// When your compiler needs to use `Compiler-Base-Error` to displaying diagnostics, you need to create a `DiagnosticHandler` at first. /// For more information about how to create a `DiagnosticHandler`, see the doc above method `new_with_template_dir()`. /// Since creating `DiagnosticHandler` needs to load the locally template (*.ftl) file, it may cause I/O performance loss, /// so we recommend you create `DiagnosticHandler` globally in the compiler and pass references to other modules that use `DiagnosticHandler`. /// -/// And since `DiagnosticHandler` provides methods that need to change the contents of itself, -/// you need to pass mutable references, and if it is in a multi-threaded environment, you need to use `Arc>` +/// And since `DiagnosticHandler` provides methods that do not supports mutable references "&mut self", so passing immutable references (&) is enough. /// /// For Example: /// @@ -38,43 +37,22 @@ mod tests; /// lang_parser: Parser, /// code_generator: CodeGenerator /// } -/// -/// // If it is in a multi-threaded environment, you can -/// struct Compiler { -/// diag_handler: Arc>, -/// lang_lexer: Lexer, -/// lang_parser: Parser, -/// code_generator: CodeGenerator -/// } /// ``` /// -/// 2. And send the mutable references to `Lexer`, `Parser` and `CodeGenerator` to displaying the diagnostic during compiling. -/// ```ignore -/// impl Compiler { -/// fn compile(&self) { -/// self.lang_lexer.lex(&mut self.diag_handler); -/// self.lang_parser.parse(&mut self.diag_handler); -/// self.code_generator.gen(&mut self.diag_handler); -/// } -/// } -/// ``` -/// // If it is in a multi-threaded environment, you can +/// 2. And send the immutable references to `Lexer`, `Parser` and `CodeGenerator` to displaying the diagnostic during compiling. /// ```ignore /// impl Compiler { /// fn compile(&self) { -/// self.lang_lexer.lex(Arc::clone(self.diag_handler)); -/// self.lang_parser.parse(Arc::clone(self.diag_handler)); -/// self.code_generator.gen(Arc::clone(self.diag_handler)); +/// self.lang_lexer.lex(&self.diag_handler); +/// self.lang_parser.parse(&self.diag_handler); +/// self.code_generator.gen(&self.diag_handler); /// } /// } /// ``` /// -/// // If you use `Arc>`, maybe you need to `lock()` it before using it. -/// /// ```ignore /// impl Lexer { -/// fn lex(&self, diag_handler: Arc>){ -/// let handler = diag_handler.lock(); +/// fn lex(&self, diag_handler: &DiagnosticHandler){ /// handler.XXXX(); // do something to diaplay diagnostic. /// } /// } @@ -177,6 +155,20 @@ impl DiagnosticHandler { /// Get count of diagnostics in `DiagnosticHandler`. /// `DiagnosticHandler` contains a set of `Diagnostic` + /// + /// # Examples + /// + /// ```rust + /// # use compiler_base_error::DiagnosticStyle; + /// # use compiler_base_error::DiagnosticHandler; + /// # use compiler_base_error::Diagnostic; + /// let diag_1 = Diagnostic::::new(); + /// let mut diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); + /// assert_eq!(diag_handler.diagnostics_count().unwrap(), 0); + /// + /// diag_handler.add_warn_diagnostic(diag_1); + /// assert_eq!(diag_handler.diagnostics_count().unwrap(), 1); + /// ``` pub fn diagnostics_count(&self) -> Result { match self.handler_inner.lock() { Ok(inner) => Ok(inner.diagnostics_count()), @@ -185,6 +177,20 @@ impl DiagnosticHandler { } /// Emit the diagnostic messages generated from error to to terminal stderr. + /// + /// # Examples + /// + /// ```rust + /// # use compiler_base_error::DiagnosticStyle; + /// # use compiler_base_error::DiagnosticHandler; + /// # use compiler_base_error::Diagnostic; + /// let diag_1 = Diagnostic::::new(); + /// let mut diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); + /// + /// assert_eq!(diag_handler.has_errors().unwrap(), false); + /// diag_handler.emit_error_diagnostic(diag_1); + /// assert_eq!(diag_handler.has_errors().unwrap(), true); + /// ``` pub fn emit_error_diagnostic(&self, diag: Diagnostic) -> Result<()> { match self.handler_inner.lock() { Ok(mut inner) => { @@ -196,6 +202,20 @@ impl DiagnosticHandler { } /// Emit the diagnostic messages generated from warning to to terminal stderr. + /// + /// # Examples + /// + /// ```rust + /// # use compiler_base_error::DiagnosticStyle; + /// # use compiler_base_error::DiagnosticHandler; + /// # use compiler_base_error::Diagnostic; + /// let diag_1 = Diagnostic::::new(); + /// let mut diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); + /// + /// assert_eq!(diag_handler.has_warns().unwrap(), false); + /// diag_handler.emit_warn_diagnostic(diag_1); + /// assert_eq!(diag_handler.has_warns().unwrap(), true); + /// ``` pub fn emit_warn_diagnostic(&self, diag: Diagnostic) -> Result<()> { match self.handler_inner.lock() { Ok(mut inner) => { @@ -208,6 +228,21 @@ impl DiagnosticHandler { /// Emit all the diagnostics messages to to terminal stderr. /// `DiagnosticHandler` contains a set of `Diagnostic` + /// + /// # Examples + /// + /// ```rust + /// # use compiler_base_error::DiagnosticStyle; + /// # use compiler_base_error::DiagnosticHandler; + /// # use compiler_base_error::Diagnostic; + /// let diag_1 = Diagnostic::::new(); + /// let diag_2 = Diagnostic::::new(); + /// let mut diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); + /// + /// diag_handler.add_err_diagnostic(diag_1); + /// diag_handler.add_err_diagnostic(diag_2); + /// diag_handler.emit_stashed_diagnostics(); + /// ``` pub fn emit_stashed_diagnostics(&self) -> Result<()> { match self.handler_inner.lock() { Ok(mut inner) => { @@ -219,6 +254,20 @@ impl DiagnosticHandler { } /// If some diagnotsics generated by errors, `has_errors` returns `True`. + /// + /// # Examples + /// + /// ```rust + /// # use compiler_base_error::DiagnosticStyle; + /// # use compiler_base_error::DiagnosticHandler; + /// # use compiler_base_error::Diagnostic; + /// let diag_1 = Diagnostic::::new(); + /// let mut diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); + /// + /// assert_eq!(diag_handler.has_errors().unwrap(), false); + /// diag_handler.emit_error_diagnostic(diag_1); + /// assert_eq!(diag_handler.has_errors().unwrap(), true); + /// ``` pub fn has_errors(&self) -> Result { match self.handler_inner.lock() { Ok(inner) => Ok(inner.has_errors()), @@ -227,6 +276,20 @@ impl DiagnosticHandler { } /// If some diagnotsics generated by warnings, `has_errors` returns `True`. + /// + /// # Examples + /// + /// ```rust + /// # use compiler_base_error::DiagnosticStyle; + /// # use compiler_base_error::DiagnosticHandler; + /// # use compiler_base_error::Diagnostic; + /// let diag_1 = Diagnostic::::new(); + /// let mut diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); + /// + /// assert_eq!(diag_handler.has_warns().unwrap(), false); + /// diag_handler.emit_warn_diagnostic(diag_1); + /// assert_eq!(diag_handler.has_warns().unwrap(), true); + /// ``` pub fn has_warns(&self) -> Result { match self.handler_inner.lock() { Ok(inner) => Ok(inner.has_warns()), @@ -235,6 +298,27 @@ impl DiagnosticHandler { } /// After emitting all the diagnostics, it will panic. + /// + /// # Examples + /// + /// ```rust + /// # use compiler_base_error::DiagnosticStyle; + /// # use compiler_base_error::Diagnostic; + /// # use compiler_base_error::DiagnosticHandler; + /// # use std::panic; + /// let diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); + /// + /// diag_handler.abort_if_errors().unwrap(); + /// diag_handler.add_warn_diagnostic(Diagnostic::::new()).unwrap(); + /// + /// diag_handler.abort_if_errors().unwrap(); + /// diag_handler.add_err_diagnostic(Diagnostic::::new()).unwrap(); + /// + /// let result = panic::catch_unwind(|| { + /// diag_handler.abort_if_errors().unwrap(); + /// }); + /// assert!(result.is_err()); + /// ``` pub fn abort_if_errors(&self) -> Result<()> { match self.handler_inner.lock() { Ok(mut inner) => { diff --git a/kclvm/compiler_base/error/src/tests.rs b/kclvm/compiler_base/error/src/tests.rs index 1992d066d..bcf075d3e 100644 --- a/kclvm/compiler_base/error/src/tests.rs +++ b/kclvm/compiler_base/error/src/tests.rs @@ -64,12 +64,16 @@ mod test_diagnostic_handler { DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); // test has_errors() assert_eq!(diag_handler.has_errors().unwrap(), false); - diag_handler.add_err_diagnostic(Diagnostic::::new()).unwrap(); + diag_handler + .add_err_diagnostic(Diagnostic::::new()) + .unwrap(); assert_eq!(diag_handler.has_errors().unwrap(), true); // test has_warns() assert_eq!(diag_handler.has_warns().unwrap(), false); - diag_handler.add_warn_diagnostic(Diagnostic::::new()).unwrap(); + diag_handler + .add_warn_diagnostic(Diagnostic::::new()) + .unwrap(); assert_eq!(diag_handler.has_warns().unwrap(), true); } @@ -78,9 +82,13 @@ mod test_diagnostic_handler { let diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); diag_handler.abort_if_errors().unwrap(); - diag_handler.add_warn_diagnostic(Diagnostic::::new()).unwrap(); + diag_handler + .add_warn_diagnostic(Diagnostic::::new()) + .unwrap(); diag_handler.abort_if_errors().unwrap(); - diag_handler.add_err_diagnostic(Diagnostic::::new()).unwrap(); + diag_handler + .add_err_diagnostic(Diagnostic::::new()) + .unwrap(); let result = panic::catch_unwind(|| { diag_handler.abort_if_errors().unwrap(); From 861051a98cb1a13a83f02165e1c1efba848b0a84 Mon Sep 17 00:00:00 2001 From: zong-zhe Date: Tue, 30 Aug 2022 15:57:51 +0800 Subject: [PATCH 10/22] fmt in docstring --- kclvm/compiler_base/error/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kclvm/compiler_base/error/src/lib.rs b/kclvm/compiler_base/error/src/lib.rs index 9be14e9d5..0ac3b2e90 100644 --- a/kclvm/compiler_base/error/src/lib.rs +++ b/kclvm/compiler_base/error/src/lib.rs @@ -81,14 +81,14 @@ impl DiagnosticHandler { /// ```rust /// # use compiler_base_error::DiagnosticHandler; /// let diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/"); - /// match diag_handler{ + /// match diag_handler { /// Ok(_) => {} /// Err(_) => {panic!("`diag_handler` should be Ok(...)")} /// } /// /// // './src_invalid/diagnostic/locales/en-US/' does not exist. /// let diag_handler_invalid = DiagnosticHandler::new_with_template_dir("./src_invalid/diagnostic/locales/en-US/"); - /// match diag_handler_invalid{ + /// match diag_handler_invalid { /// Ok(_) => {panic!("`diag_handler_invalid` should be Err(...)")} /// Err(_) => {} /// } From 11b9665b76d3ad2dc7b8689fd234b9822cc3e29d Mon Sep 17 00:00:00 2001 From: zong-zhe Date: Wed, 31 Aug 2022 15:24:37 +0800 Subject: [PATCH 11/22] fix comments mistakes --- .../src/diagnostic/diagnostic_message.rs | 32 +++++++++---------- .../error/src/diagnostic_handler.rs | 32 +++++++++---------- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/kclvm/compiler_base/error/src/diagnostic/diagnostic_message.rs b/kclvm/compiler_base/error/src/diagnostic/diagnostic_message.rs index 645b17521..bf4a15897 100644 --- a/kclvm/compiler_base/error/src/diagnostic/diagnostic_message.rs +++ b/kclvm/compiler_base/error/src/diagnostic/diagnostic_message.rs @@ -8,23 +8,23 @@ use walkdir::{DirEntry, WalkDir}; use crate::MessageArgs; -// Struct `TemplateLoader` load template contents from "*.ftl" file. -// `TemplateLoader` will operate on files locally. +/// Struct `TemplateLoader` load template contents from "*.ftl" file. +/// `TemplateLoader` will operate on files locally. pub(crate) struct TemplateLoader { template_inner: Arc, } impl TemplateLoader { - // Create the `TemplateLoader` with template (*.ftl) files directory. - // `TemplateLoader` will load all the files end with "*.ftl" under the directory recursively. - // template_files - // | - // |---- template.ftl - // |---- sub_template_files - // | - // |---- sub_template.ftl - // - // 'template.ftl' and 'sub_template.ftl' can both loaded by the `new_with_template_dir()`. + /// Create the `TemplateLoader` with template (*.ftl) files directory. + /// `TemplateLoader` will load all the files end with "*.ftl" under the directory recursively. + /// template_files + /// | + /// |---- template.ftl + /// |---- sub_template_files + /// | + /// |---- sub_template.ftl + /// + /// 'template.ftl' and 'sub_template.ftl' can both loaded by the `new_with_template_dir()`. pub(crate) fn new_with_template_dir(template_dir: &str) -> Result { let template_inner = TemplateLoaderInner::new_with_template_dir(template_dir) .with_context(|| format!("Failed to load '*.ftl' from '{}'", template_dir))?; @@ -33,9 +33,9 @@ impl TemplateLoader { }) } - // Get the message string from "*.ftl" file by `index`, `sub_index` and `MessageArgs`. - // For more information about "*.ftl" file, see the doc above `DiagnosticHandler`. - // "*.ftl" file looks like, e.g. './src/diagnostic/locales/en-US/default.ftl' : + /// Get the message string from "*.ftl" file by `index`, `sub_index` and `MessageArgs`. + /// For more information about "*.ftl" file, see the doc above `DiagnosticHandler`. + /// "*.ftl" file looks like, e.g. './src/diagnostic/locales/en-US/default.ftl' : pub(crate) fn get_msg_to_str( &self, index: &str, @@ -68,7 +68,7 @@ impl TemplateLoader { } } -// `TemplateLoaderInner` is used to privatize the default constructor of `TemplateLoader`. +/// `TemplateLoaderInner` is used to privatize the default constructor of `TemplateLoader`. struct TemplateLoaderInner { template_bunder: FluentBundle, } diff --git a/kclvm/compiler_base/error/src/diagnostic_handler.rs b/kclvm/compiler_base/error/src/diagnostic_handler.rs index e73237736..f0a4e3f8f 100644 --- a/kclvm/compiler_base/error/src/diagnostic_handler.rs +++ b/kclvm/compiler_base/error/src/diagnostic_handler.rs @@ -15,7 +15,7 @@ pub(crate) struct DiagnosticHandlerInner { } impl DiagnosticHandlerInner { - // Load all (*.ftl) template files under directory `template_dir`. + /// Load all (*.ftl) template files under directory `template_dir`. pub(crate) fn new_with_template_dir(template_dir: &str) -> Result { let template_loader = TemplateLoader::new_with_template_dir(template_dir) .with_context(|| format!("Failed to init `TemplateLoader` from '{}'", template_dir))?; @@ -29,57 +29,57 @@ impl DiagnosticHandlerInner { }) } - // Add a diagnostic generated from error to `DiagnosticHandler`. - // `DiagnosticHandler` contains a set of `Diagnostic` + /// Add a diagnostic generated from error to `DiagnosticHandler`. + /// `DiagnosticHandler` contains a set of `Diagnostic` pub(crate) fn add_err_diagnostic(&mut self, diag: Diagnostic) { self.diagnostics.push(diag); self.err_count += 1; } - // Add a diagnostic generated from warning to `DiagnosticHandler`. - // `DiagnosticHandler` contains a set of `Diagnostic` + /// Add a diagnostic generated from warning to `DiagnosticHandler`. + /// `DiagnosticHandler` contains a set of `Diagnostic` pub(crate) fn add_warn_diagnostic(&mut self, diag: Diagnostic) { self.diagnostics.push(diag); self.warn_count += 1; } - // Get count of diagnostics in `DiagnosticHandler`. - // `DiagnosticHandler` contains a set of `Diagnostic` + /// Get count of diagnostics in `DiagnosticHandler`. + /// `DiagnosticHandler` contains a set of `Diagnostic` pub(crate) fn diagnostics_count(&self) -> usize { self.diagnostics.len() } - // Emit the diagnostic messages generated from error to to terminal stderr. + /// Emit the diagnostic messages generated from error to to terminal stderr. pub(crate) fn emit_error_diagnostic(&mut self, diag: Diagnostic) { self.emitter.emit_diagnostic(&diag); self.err_count += 1; } - // Emit the diagnostic messages generated from warning to to terminal stderr. + /// Emit the diagnostic messages generated from warning to to terminal stderr. pub(crate) fn emit_warn_diagnostic(&mut self, diag: Diagnostic) { self.emitter.emit_diagnostic(&diag); self.warn_count += 1; } - // Emit all the diagnostics messages to to terminal stderr. - // `DiagnosticHandler` contains a set of `Diagnostic` + /// Emit all the diagnostics messages to to terminal stderr. + /// `DiagnosticHandler` contains a set of `Diagnostic` pub(crate) fn emit_stashed_diagnostics(&mut self) { for diag in &self.diagnostics { self.emitter.emit_diagnostic(&diag) } } - // If some diagnotsics generated by errors, `has_errors` returns `True`. + /// If some diagnotsics generated by errors, `has_errors` returns `True`. pub(crate) fn has_errors(&self) -> bool { self.err_count > 0 } - // If some diagnotsics generated by warnings, `has_errors` returns `True`. + /// If some diagnotsics generated by warnings, `has_errors` returns `True`. pub(crate) fn has_warns(&self) -> bool { self.warn_count > 0 } - // After emitting all the diagnostics, it will panic. + /// After emitting all the diagnostics, it will panic. pub(crate) fn abort_if_errors(&mut self) { self.emit_stashed_diagnostics(); @@ -88,8 +88,8 @@ impl DiagnosticHandlerInner { } } - // Get the message string from "*.ftl" file by `index`, `sub_index` and `MessageArgs`. - // "*.ftl" file looks like, e.g. './src/diagnostic/locales/en-US/default.ftl' : + /// Get the message string from "*.ftl" file by `index`, `sub_index` and `MessageArgs`. + /// "*.ftl" file looks like, e.g. './src/diagnostic/locales/en-US/default.ftl' : pub(crate) fn get_diagnostic_msg( &self, index: &str, From 33b279c7b09067b1d833752ba8245e9cb7868034 Mon Sep 17 00:00:00 2001 From: zong-zhe Date: Wed, 31 Aug 2022 17:27:01 +0800 Subject: [PATCH 12/22] move comments to lib.rs --- kclvm/compiler_base/error/src/lib.rs | 52 +++++++++++++++++----------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/kclvm/compiler_base/error/src/lib.rs b/kclvm/compiler_base/error/src/lib.rs index 0ac3b2e90..0574c6765 100644 --- a/kclvm/compiler_base/error/src/lib.rs +++ b/kclvm/compiler_base/error/src/lib.rs @@ -1,3 +1,29 @@ +//! This crate provides `DiagnosticHandler` supports diagnostic messages to terminal stderr. +//! +//! 'fluent0.16.0' is used to support diagnostic text template. +//! For more information about 'fluent0.16.0', see https://projectfluent.org/. +//! +//! 'fluent0.16.0' uses "*.ftl" as the template file. +//! "*.ftl" file looks like, e.g. './src/diagnostic/locales/en-US/default.ftl' : +//! +//! ``` ignore +//! invalid-syntax = Invalid syntax +//! .expected = Expected one of `{$expected_items}` +//! ``` +//! There are two lines in './src/diagnostic/locales/en-US/default.ftl'. +//! - In line 1, `invalid-syntax` is a `index`, `Invalid syntax` is the `Message String` to this `index`. +//! - In line 2, `.expected` is another `index`, it is a `sub_index` of `invalid-syntax`. +//! - In line 2, `sub_index` must start with a point `.` and it is optional and can be more than one. +//! - In line 2, `Expected one of `{$expected_items}`` is the `Message String` to `.expected`. It is an interpolated string. +//! - In line 2, `{$expected_items}` is a `MessageArgs` of the `Expected one of `{$expected_items}`` +//! and `MessageArgs` can be recognized as a Key-Value entry, it is optional. +//! +//! The pattern of above '*.ftl' file looks like: +//! ``` ignore +//! <'index'> = <'message_string' with optional 'MessageArgs'> +//! = <'message_string' with optional 'MessageArgs'>* +//! ``` + mod diagnostic; mod diagnostic_handler; mod emitter; @@ -330,27 +356,13 @@ impl DiagnosticHandler { } /// Get the message string from "*.ftl" file by `index`, `sub_index` and `MessageArgs`. - /// "*.ftl" file looks like, e.g. './src/diagnostic/locales/en-US/default.ftl' : - /// - /// ``` ignore - /// 1. invalid-syntax = Invalid syntax - /// 2. .expected = Expected one of `{$expected_items}` - /// ``` - /// - /// - In line 1, `invalid-syntax` is a `index`, `Invalid syntax` is the `Message String` to this `index`. - /// - In line 2, `.expected` is another `index`, it is a `sub_index` of `invalid-syntax`. - /// - In line 2, `sub_index` must start with a point `.` and it is optional. - /// - In line 2, `Expected one of `{$expected_items}`` is the `Message String` to `.expected`. It is an interpolated string. - /// - In line 2, `{$expected_items}` is a `MessageArgs` of the `Expected one of `{$expected_items}`` - /// and `MessageArgs` can be recognized as a Key-Value entry, it is optional. - /// - /// The pattern of above '*.ftl' file looks like: - /// ``` ignore - /// 1. <'index'> = <'message_string' with optional 'MessageArgs'> - /// 2. = <'message_string' with optional 'MessageArgs'> - /// ``` /// And for the 'default.ftl' shown above, you can get messages as follow: - /// + /// + /// ```ignore + /// invalid-syntax = Invalid syntax + /// .expected = Expected one of `{$expected_items}` + /// ``` + /// /// 1. If you want the message 'Invalid syntax' in line 1. /// /// ``` rust From eb2ef14d234f2c9c75209da43bd5585736eb5a70 Mon Sep 17 00:00:00 2001 From: zong-zhe Date: Wed, 31 Aug 2022 17:54:06 +0800 Subject: [PATCH 13/22] add default() for DiagnosticHandler --- kclvm/compiler_base/error/src/lib.rs | 31 +++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/kclvm/compiler_base/error/src/lib.rs b/kclvm/compiler_base/error/src/lib.rs index 0574c6765..05af94a7c 100644 --- a/kclvm/compiler_base/error/src/lib.rs +++ b/kclvm/compiler_base/error/src/lib.rs @@ -1,6 +1,6 @@ //! This crate provides `DiagnosticHandler` supports diagnostic messages to terminal stderr. -//! -//! 'fluent0.16.0' is used to support diagnostic text template. +//! +//! 'fluent0.16.0' is used to support diagnostic text template. //! For more information about 'fluent0.16.0', see https://projectfluent.org/. //! //! 'fluent0.16.0' uses "*.ftl" as the template file. @@ -15,7 +15,7 @@ //! - In line 2, `.expected` is another `index`, it is a `sub_index` of `invalid-syntax`. //! - In line 2, `sub_index` must start with a point `.` and it is optional and can be more than one. //! - In line 2, `Expected one of `{$expected_items}`` is the `Message String` to `.expected`. It is an interpolated string. -//! - In line 2, `{$expected_items}` is a `MessageArgs` of the `Expected one of `{$expected_items}`` +//! - In line 2, `{$expected_items}` is a `MessageArgs` of the `Expected one of `{$expected_items}`` //! and `MessageArgs` can be recognized as a Key-Value entry, it is optional. //! //! The pattern of above '*.ftl' file looks like: @@ -89,6 +89,27 @@ pub struct DiagnosticHandler { } impl DiagnosticHandler { + /// Load all (*.ftl) template files under default directory. + /// + /// Default directory "./src/diagnostic/locales/en-US/" + /// Call the constructor 'new_with_template_dir()' to load the file. + /// For more information about the constructor 'new_with_template_dir()', see the doc above 'new_with_template_dir()'. + pub fn default() -> Result { + const DEFAULT_TEMPLATE_RESOURCE: &'static str = "./src/diagnostic/locales/en-US/"; + let handler_inner = DiagnosticHandlerInner::new_with_template_dir( + DEFAULT_TEMPLATE_RESOURCE, + ) + .with_context(|| { + format!( + "Failed to init `TemplateLoader` from '{}'", + DEFAULT_TEMPLATE_RESOURCE + ) + })?; + Ok(Self { + handler_inner: Mutex::new(handler_inner), + }) + } + /// Load all (*.ftl) template files under directory `template_dir`. /// `DiagnosticHandler` will load all the files end with "*.ftl" under the directory recursively. /// If directory `template_dir` does not exist, this method will return an error. @@ -357,12 +378,12 @@ impl DiagnosticHandler { /// Get the message string from "*.ftl" file by `index`, `sub_index` and `MessageArgs`. /// And for the 'default.ftl' shown above, you can get messages as follow: - /// + /// /// ```ignore /// invalid-syntax = Invalid syntax /// .expected = Expected one of `{$expected_items}` /// ``` - /// + /// /// 1. If you want the message 'Invalid syntax' in line 1. /// /// ``` rust From 71fdb7acd08978d838096c8c7e58678af377f59c Mon Sep 17 00:00:00 2001 From: zong-zhe Date: Wed, 31 Aug 2022 18:27:55 +0800 Subject: [PATCH 14/22] add inlines --- kclvm/compiler_base/error/src/diagnostic_handler.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kclvm/compiler_base/error/src/diagnostic_handler.rs b/kclvm/compiler_base/error/src/diagnostic_handler.rs index f0a4e3f8f..d64fa0c00 100644 --- a/kclvm/compiler_base/error/src/diagnostic_handler.rs +++ b/kclvm/compiler_base/error/src/diagnostic_handler.rs @@ -45,6 +45,7 @@ impl DiagnosticHandlerInner { /// Get count of diagnostics in `DiagnosticHandler`. /// `DiagnosticHandler` contains a set of `Diagnostic` + #[inline] pub(crate) fn diagnostics_count(&self) -> usize { self.diagnostics.len() } @@ -70,11 +71,13 @@ impl DiagnosticHandlerInner { } /// If some diagnotsics generated by errors, `has_errors` returns `True`. + #[inline] pub(crate) fn has_errors(&self) -> bool { self.err_count > 0 } /// If some diagnotsics generated by warnings, `has_errors` returns `True`. + #[inline] pub(crate) fn has_warns(&self) -> bool { self.warn_count > 0 } From e276ae46047c684e84da7ec97477bf677808196e Mon Sep 17 00:00:00 2001 From: zong-zhe Date: Wed, 31 Aug 2022 19:11:21 +0800 Subject: [PATCH 15/22] add note comments for add_diagnostic() --- kclvm/compiler_base/error/src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/kclvm/compiler_base/error/src/lib.rs b/kclvm/compiler_base/error/src/lib.rs index 05af94a7c..8bc9c19c9 100644 --- a/kclvm/compiler_base/error/src/lib.rs +++ b/kclvm/compiler_base/error/src/lib.rs @@ -151,6 +151,9 @@ impl DiagnosticHandler { /// Add a diagnostic generated from error to `DiagnosticHandler`. /// `DiagnosticHandler` contains a set of `Diagnostic` /// + /// Note: `DiagnosticHandler` does not deduplicate diagnostics. + /// If you add two same diagnostics, you will see two same messages in the terminal. + /// /// # Examples /// /// ```rust @@ -177,6 +180,9 @@ impl DiagnosticHandler { /// Add a diagnostic generated from warning to `DiagnosticHandler`. /// `DiagnosticHandler` contains a set of `Diagnostic` /// + /// Note: `DiagnosticHandler` does not deduplicate diagnostics. + /// If you add two same diagnostics, you will see two same messages in the terminal. + /// /// # Examples /// /// ```rust From e1ed23295da224612de3006e9b4e54aa536d83d1 Mon Sep 17 00:00:00 2001 From: zong-zhe Date: Thu, 1 Sep 2022 10:01:32 +0800 Subject: [PATCH 16/22] add comments --- .../src/diagnostic/diagnostic_handler.rs | 561 ++++++++++++++++++ .../src/diagnostic/diagnostic_message.rs | 7 +- .../compiler_base/error/src/diagnostic/mod.rs | 1 + .../error/src/diagnostic/tests.rs | 2 +- .../error/src/diagnostic_handler.rs | 104 ---- kclvm/compiler_base/error/src/emitter.rs | 2 +- kclvm/compiler_base/error/src/lib.rs | 489 +-------------- kclvm/compiler_base/error/src/tests.rs | 5 +- 8 files changed, 583 insertions(+), 588 deletions(-) create mode 100644 kclvm/compiler_base/error/src/diagnostic/diagnostic_handler.rs delete mode 100644 kclvm/compiler_base/error/src/diagnostic_handler.rs diff --git a/kclvm/compiler_base/error/src/diagnostic/diagnostic_handler.rs b/kclvm/compiler_base/error/src/diagnostic/diagnostic_handler.rs new file mode 100644 index 000000000..7f64f2e32 --- /dev/null +++ b/kclvm/compiler_base/error/src/diagnostic/diagnostic_handler.rs @@ -0,0 +1,561 @@ +//! This crate provides `DiagnosticHandler` supports diagnostic messages to terminal stderr. +//! +//! `DiagnosticHandler` mainly consists of 4 parts: +//! - Emitter: Emit the styled string to terminal stderr. +//! - Template Loader: Load template files locally and find messages from file contents. +//! - A set for Diagnostics: All the diagnostic messages. +//! +//! For more information about diagnostic, see doc in "compiler_base/error/diagnostic/mod.rs". +//! For more information about emitter, see doc in "compiler_base/error/src/emitter.rs". +//! For more information about template loader, see doc in "compiler_base/error/src/diagnostic/diagnostic_message.rs". + +use crate::{ + diagnostic::diagnostic_message::TemplateLoader, Diagnostic, DiagnosticStyle, Emitter, + TerminalEmitter, +}; +use anyhow::{bail, Context, Result}; +use compiler_base_span::fatal_error::FatalError; +use fluent::FluentArgs; +use std::sync::{Arc, Mutex}; + +/// `DiagnosticHandler` supports diagnostic messages to terminal stderr. +/// +/// `DiagnosticHandler` will load template file(*ftl) directory when instantiating through the constructor `new_with_template_dir()`. +/// "*.ftl" file looks like, e.g. './src/diagnostic/locales/en-US/default.ftl' : +/// ``` ignore +/// invalid-syntax = Invalid syntax +/// .expected = Expected one of `{$expected_items}` +/// ``` +/// There are two lines in './src/diagnostic/locales/en-US/default.ftl'. +/// - In line 1, `invalid-syntax` is a `index`, `Invalid syntax` is the `Message String` to this `index`. +/// - In line 2, `.expected` is another `index`, it is a `sub_index` of `invalid-syntax`. +/// - In line 2, `sub_index` must start with a point `.` and it is optional and can be more than one. +/// - In line 2, `Expected one of `{$expected_items}`` is the `Message String` to `.expected`. It is an interpolated string. +/// - In line 2, `{$expected_items}` is a `MessageArgs` of the `Expected one of `{$expected_items}`` +/// and `MessageArgs` can be recognized as a Key-Value entry, it is optional. +/// +/// The pattern of above '*.ftl' file looks like: +/// ``` ignore +/// <'index'> = <'message_string' with optional 'MessageArgs'> +/// = <'message_string' with optional 'MessageArgs'>* +/// ``` +/// +/// Note: `DiagnosticHandler` uses `Mutex` internally to ensure thread safety, +/// so you don't need to use references like `Arc` or `Mutex` to make `DiagnosticHandler` thread safe. +/// +/// When your compiler needs to use `Compiler-Base-Error` to displaying diagnostics, you need to create a `DiagnosticHandler` at first. +/// For more information about how to create a `DiagnosticHandler`, see the doc above method `new_with_template_dir()`. +/// Since creating `DiagnosticHandler` needs to load the locally template (*.ftl) file, it may cause I/O performance loss, +/// so we recommend you create `DiagnosticHandler` globally in the compiler and pass references to other modules that use `DiagnosticHandler`. +/// +/// And since `DiagnosticHandler` provides methods that do not supports mutable references "&mut self", so passing immutable references (&) is enough. +/// +/// For Example: +/// +/// 1. You can put `DiagnosticHandler` on the same level as `Lexer`, `Parser` and `CodeGenerator` in your compiler. +/// ```ignore +/// struct Compiler { +/// diag_handler: DiagnosticHandler, +/// lang_lexer: Lexer, +/// lang_parser: Parser, +/// code_generator: CodeGenerator +/// } +/// ``` +/// +/// 2. And send the immutable references to `Lexer`, `Parser` and `CodeGenerator` to displaying the diagnostic during compiling. +/// ```ignore +/// impl Compiler { +/// fn compile(&self) { +/// self.lang_lexer.lex(&self.diag_handler); +/// self.lang_parser.parse(&self.diag_handler); +/// self.code_generator.gen(&self.diag_handler); +/// } +/// } +/// ``` +/// +/// ```ignore +/// impl Lexer { +/// fn lex(&self, diag_handler: &DiagnosticHandler){ +/// handler.XXXX(); // do something to diaplay diagnostic. +/// } +/// } +/// ``` +/// +pub struct DiagnosticHandler { + handler_inner: Mutex, +} + +impl DiagnosticHandler { + /// Load all (*.ftl) template files under directory `template_dir`. + /// `DiagnosticHandler` will load all the files end with "*.ftl" under the directory recursively. + /// If directory `template_dir` does not exist, this method will return an error. + /// + /// template_files + /// | + /// |---- template.ftl + /// |---- sub_template_files + /// | + /// |---- sub_template.ftl + /// + /// 'template.ftl' and 'sub_template.ftl' can both loaded by the `new_with_template_dir()`. + /// + /// # Examples + /// + /// ```rust + /// # use compiler_base_error::DiagnosticHandler; + /// let diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/"); + /// match diag_handler { + /// Ok(_) => {} + /// Err(_) => {panic!("`diag_handler` should be Ok(...)")} + /// } + /// + /// // './src_invalid/diagnostic/locales/en-US/' does not exist. + /// let diag_handler_invalid = DiagnosticHandler::new_with_template_dir("./src_invalid/diagnostic/locales/en-US/"); + /// match diag_handler_invalid { + /// Ok(_) => {panic!("`diag_handler_invalid` should be Err(...)")} + /// Err(_) => {} + /// } + /// ``` + pub fn new_with_template_dir(template_dir: &str) -> Result { + let handler_inner = DiagnosticHandlerInner::new_with_template_dir(template_dir) + .with_context(|| format!("Failed to init `TemplateLoader` from '{}'", template_dir))?; + Ok(Self { + handler_inner: Mutex::new(handler_inner), + }) + } + + /// Add a diagnostic generated from error to `DiagnosticHandler`. + /// `DiagnosticHandler` contains a set of `Diagnostic` + /// + /// Note: `DiagnosticHandler` does not deduplicate diagnostics. + /// If you add two same diagnostics, you will see two same messages in the terminal. + /// + /// # Examples + /// + /// ```rust + /// # use compiler_base_error::DiagnosticStyle; + /// # use compiler_base_error::DiagnosticHandler; + /// # use compiler_base_error::Diagnostic; + /// let diag_1 = Diagnostic::::new(); + /// let mut diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); + /// assert_eq!(diag_handler.diagnostics_count().unwrap(), 0); + /// + /// diag_handler.add_err_diagnostic(diag_1); + /// assert_eq!(diag_handler.diagnostics_count().unwrap(), 1); + /// ``` + pub fn add_err_diagnostic(&self, diag: Diagnostic) -> Result<()> { + match self.handler_inner.lock() { + Ok(mut inner) => { + inner.add_err_diagnostic(diag); + Ok(()) + } + Err(_) => bail!("Add Error Diagnostic Failed."), + } + } + + /// Add a diagnostic generated from warning to `DiagnosticHandler`. + /// `DiagnosticHandler` contains a set of `Diagnostic` + /// + /// Note: `DiagnosticHandler` does not deduplicate diagnostics. + /// If you add two same diagnostics, you will see two same messages in the terminal. + /// + /// # Examples + /// + /// ```rust + /// # use compiler_base_error::DiagnosticStyle; + /// # use compiler_base_error::DiagnosticHandler; + /// # use compiler_base_error::Diagnostic; + /// let diag_1 = Diagnostic::::new(); + /// let mut diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); + /// assert_eq!(diag_handler.diagnostics_count().unwrap(), 0); + /// + /// diag_handler.add_warn_diagnostic(diag_1); + /// assert_eq!(diag_handler.diagnostics_count().unwrap(), 1); + /// ``` + pub fn add_warn_diagnostic(&self, diag: Diagnostic) -> Result<()> { + match self.handler_inner.lock() { + Ok(mut inner) => { + inner.add_warn_diagnostic(diag); + Ok(()) + } + Err(_) => bail!("Add Warn Diagnostic Failed."), + } + } + + /// Get count of diagnostics in `DiagnosticHandler`. + /// `DiagnosticHandler` contains a set of `Diagnostic` + /// + /// # Examples + /// + /// ```rust + /// # use compiler_base_error::DiagnosticStyle; + /// # use compiler_base_error::DiagnosticHandler; + /// # use compiler_base_error::Diagnostic; + /// let diag_1 = Diagnostic::::new(); + /// let mut diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); + /// assert_eq!(diag_handler.diagnostics_count().unwrap(), 0); + /// + /// diag_handler.add_warn_diagnostic(diag_1); + /// assert_eq!(diag_handler.diagnostics_count().unwrap(), 1); + /// ``` + pub fn diagnostics_count(&self) -> Result { + match self.handler_inner.lock() { + Ok(inner) => Ok(inner.diagnostics_count()), + Err(_) => bail!("Diagnostics Counts Failed."), + } + } + + /// Emit the diagnostic messages generated from error to to terminal stderr. + /// + /// # Examples + /// + /// ```rust + /// # use compiler_base_error::DiagnosticStyle; + /// # use compiler_base_error::DiagnosticHandler; + /// # use compiler_base_error::Diagnostic; + /// let diag_1 = Diagnostic::::new(); + /// let mut diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); + /// + /// assert_eq!(diag_handler.has_errors().unwrap(), false); + /// diag_handler.emit_error_diagnostic(diag_1); + /// assert_eq!(diag_handler.has_errors().unwrap(), true); + /// ``` + pub fn emit_error_diagnostic(&self, diag: Diagnostic) -> Result<()> { + match self.handler_inner.lock() { + Ok(mut inner) => { + inner.emit_error_diagnostic(diag); + Ok(()) + } + Err(_) => bail!("Emit Error Diagnostics Failed."), + } + } + + /// Emit the diagnostic messages generated from warning to to terminal stderr. + /// + /// # Examples + /// + /// ```rust + /// # use compiler_base_error::DiagnosticStyle; + /// # use compiler_base_error::DiagnosticHandler; + /// # use compiler_base_error::Diagnostic; + /// let diag_1 = Diagnostic::::new(); + /// let mut diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); + /// + /// assert_eq!(diag_handler.has_warns().unwrap(), false); + /// diag_handler.emit_warn_diagnostic(diag_1); + /// assert_eq!(diag_handler.has_warns().unwrap(), true); + /// ``` + pub fn emit_warn_diagnostic(&self, diag: Diagnostic) -> Result<()> { + match self.handler_inner.lock() { + Ok(mut inner) => { + inner.emit_warn_diagnostic(diag); + Ok(()) + } + Err(_) => bail!("Emit Warn Diagnostics Failed."), + } + } + + /// Emit all the diagnostics messages to to terminal stderr. + /// `DiagnosticHandler` contains a set of `Diagnostic` + /// + /// # Examples + /// + /// ```rust + /// # use compiler_base_error::DiagnosticStyle; + /// # use compiler_base_error::DiagnosticHandler; + /// # use compiler_base_error::Diagnostic; + /// let diag_1 = Diagnostic::::new(); + /// let diag_2 = Diagnostic::::new(); + /// let mut diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); + /// + /// diag_handler.add_err_diagnostic(diag_1); + /// diag_handler.add_err_diagnostic(diag_2); + /// diag_handler.emit_stashed_diagnostics(); + /// ``` + pub fn emit_stashed_diagnostics(&self) -> Result<()> { + match self.handler_inner.lock() { + Ok(mut inner) => { + inner.emit_stashed_diagnostics(); + Ok(()) + } + Err(_) => bail!("Emit Stashed Diagnostics Failed."), + } + } + + /// If some diagnotsics generated by errors, `has_errors` returns `True`. + /// + /// # Examples + /// + /// ```rust + /// # use compiler_base_error::DiagnosticStyle; + /// # use compiler_base_error::DiagnosticHandler; + /// # use compiler_base_error::Diagnostic; + /// let diag_1 = Diagnostic::::new(); + /// let mut diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); + /// + /// assert_eq!(diag_handler.has_errors().unwrap(), false); + /// diag_handler.emit_error_diagnostic(diag_1); + /// assert_eq!(diag_handler.has_errors().unwrap(), true); + /// ``` + pub fn has_errors(&self) -> Result { + match self.handler_inner.lock() { + Ok(inner) => Ok(inner.has_errors()), + Err(_) => bail!("Check Has Errors Failed."), + } + } + + /// If some diagnotsics generated by warnings, `has_errors` returns `True`. + /// + /// # Examples + /// + /// ```rust + /// # use compiler_base_error::DiagnosticStyle; + /// # use compiler_base_error::DiagnosticHandler; + /// # use compiler_base_error::Diagnostic; + /// let diag_1 = Diagnostic::::new(); + /// let mut diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); + /// + /// assert_eq!(diag_handler.has_warns().unwrap(), false); + /// diag_handler.emit_warn_diagnostic(diag_1); + /// assert_eq!(diag_handler.has_warns().unwrap(), true); + /// ``` + pub fn has_warns(&self) -> Result { + match self.handler_inner.lock() { + Ok(inner) => Ok(inner.has_warns()), + Err(_) => bail!("Check Has Warns Failed."), + } + } + + /// After emitting all the diagnostics, it will panic. + /// + /// # Examples + /// + /// ```rust + /// # use compiler_base_error::DiagnosticStyle; + /// # use compiler_base_error::Diagnostic; + /// # use compiler_base_error::DiagnosticHandler; + /// # use std::panic; + /// let diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); + /// + /// diag_handler.abort_if_errors().unwrap(); + /// diag_handler.add_warn_diagnostic(Diagnostic::::new()).unwrap(); + /// + /// diag_handler.abort_if_errors().unwrap(); + /// diag_handler.add_err_diagnostic(Diagnostic::::new()).unwrap(); + /// + /// let result = panic::catch_unwind(|| { + /// diag_handler.abort_if_errors().unwrap(); + /// }); + /// assert!(result.is_err()); + /// ``` + pub fn abort_if_errors(&self) -> Result<()> { + match self.handler_inner.lock() { + Ok(mut inner) => { + inner.abort_if_errors(); + Ok(()) + } + Err(_) => bail!("Abort If Errors Failed."), + } + } + + /// Get the message string from "*.ftl" file by `index`, `sub_index` and `MessageArgs`. + /// And for the 'default.ftl' shown above, you can get messages as follow: + /// + /// ```ignore + /// invalid-syntax = Invalid syntax + /// .expected = Expected one of `{$expected_items}` + /// ``` + /// + /// 1. If you want the message 'Invalid syntax' in line 1. + /// + /// ``` rust + /// # use compiler_base_error::MessageArgs; + /// # use compiler_base_error::DiagnosticHandler; + /// + /// // 1. Prepare an empty `MessageArgs`, Message in line 1 is not an interpolated string. + /// let no_args = MessageArgs::new(); + /// + /// // 2. `index` is 'invalid-syntax' and has no `sub_index`. + /// let index = "invalid-syntax"; + /// let sub_index = None; + /// + /// // 3. Create the `DiagnosticHandler` with template (*.ftl) files directory. + /// let diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); + /// + /// // 4. Get the message. + /// let msg_in_line_1 = diag_handler.get_diagnostic_msg(index, sub_index, &no_args).unwrap(); + /// + /// assert_eq!(msg_in_line_1, "Invalid syntax"); + /// ``` + /// + /// 2. If you want the message 'Expected one of `{$expected_items}`' in line 2. + /// + /// ``` rust + /// # use compiler_base_error::MessageArgs; + /// # use compiler_base_error::DiagnosticHandler; + /// + /// // 1. Prepare the `MessageArgs` for `{$expected_items}`. + /// let mut args = MessageArgs::new(); + /// args.set("expected_items", "I am an expected item"); + /// + /// // 2. `index` is 'invalid-syntax'. + /// let index = "invalid-syntax"; + /// + /// // 3. `sub_index` is 'expected'. + /// let sub_index = "expected"; + /// + /// // 4. Create the `DiagnosticHandler` with template (*.ftl) files directory. + /// let diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); + /// + /// // 5. Get the message. + /// let msg_in_line_2 = diag_handler.get_diagnostic_msg(index, Some(sub_index), &args).unwrap(); + /// + /// assert_eq!(msg_in_line_2, "Expected one of `\u{2068}I am an expected item\u{2069}`"); + /// ``` + pub fn get_diagnostic_msg( + &self, + index: &str, + sub_index: Option<&str>, + args: &MessageArgs, + ) -> Result { + match self.handler_inner.lock() { + Ok(inner) => inner.get_diagnostic_msg(index, sub_index, args), + Err(_) => bail!("Find Diagnostic Message Failed."), + } + } +} + +/// `MessageArgs` is the arguments of the interpolated string. +/// +/// `MessageArgs` is a Key-Value entry which only supports "set" and without "get". +/// You need getting nothing from `MessageArgs`. Only setting it and senting it to `DiagnosticHandler` is enough. +/// +/// Note: Currently both `Key` and `Value` of `MessageArgs` types only support string (&str). +/// +/// # Examples +/// +/// ``` rust +/// # use compiler_base_error::DiagnosticHandler; +/// # use compiler_base_error::MessageArgs; +/// +/// let index = "invalid-syntax"; +/// let sub_index = Some("expected"); +/// let mut msg_args = MessageArgs::new(); +/// // You only need "set()". +/// msg_args.set("This is Key", "This is Value"); +/// +/// // Create the `DiagnosticHandler` with template (*.ftl) files directory. +/// let diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); +/// +/// // When you use it, just sent it to `DiagnosticHandler`. +/// let msg_in_line_1 = diag_handler.get_diagnostic_msg(index, sub_index, &msg_args); +/// ``` +/// +/// For more information about the `DiagnosticHandler` see the doc above struct `DiagnosticHandler`. +pub struct MessageArgs<'a>(pub(crate) FluentArgs<'a>); +impl<'a> MessageArgs<'a> { + pub fn new() -> Self { + Self(FluentArgs::new()) + } + + pub fn set(&mut self, k: &'a str, v: &'a str) { + self.0.set(k, v); + } +} + +pub(crate) struct DiagnosticHandlerInner { + emitter: Box>, + diagnostics: Vec>, + err_count: usize, + warn_count: usize, + template_loader: Arc, +} + +impl DiagnosticHandlerInner { + /// Load all (*.ftl) template files under directory `template_dir`. + pub(crate) fn new_with_template_dir(template_dir: &str) -> Result { + let template_loader = TemplateLoader::new_with_template_dir(template_dir) + .with_context(|| format!("Failed to init `TemplateLoader` from '{}'", template_dir))?; + + Ok(Self { + err_count: 0, + warn_count: 0, + emitter: Box::new(TerminalEmitter::default()), + diagnostics: vec![], + template_loader: Arc::new(template_loader), + }) + } + + /// Add a diagnostic generated from error to `DiagnosticHandler`. + /// `DiagnosticHandler` contains a set of `Diagnostic` + pub(crate) fn add_err_diagnostic(&mut self, diag: Diagnostic) { + self.diagnostics.push(diag); + self.err_count += 1; + } + + /// Add a diagnostic generated from warning to `DiagnosticHandler`. + /// `DiagnosticHandler` contains a set of `Diagnostic` + pub(crate) fn add_warn_diagnostic(&mut self, diag: Diagnostic) { + self.diagnostics.push(diag); + self.warn_count += 1; + } + + /// Get count of diagnostics in `DiagnosticHandler`. + /// `DiagnosticHandler` contains a set of `Diagnostic` + #[inline] + pub(crate) fn diagnostics_count(&self) -> usize { + self.diagnostics.len() + } + + /// Emit the diagnostic messages generated from error to to terminal stderr. + pub(crate) fn emit_error_diagnostic(&mut self, diag: Diagnostic) { + self.emitter.emit_diagnostic(&diag); + self.err_count += 1; + } + + /// Emit the diagnostic messages generated from warning to to terminal stderr. + pub(crate) fn emit_warn_diagnostic(&mut self, diag: Diagnostic) { + self.emitter.emit_diagnostic(&diag); + self.warn_count += 1; + } + + /// Emit all the diagnostics messages to to terminal stderr. + /// `DiagnosticHandler` contains a set of `Diagnostic` + pub(crate) fn emit_stashed_diagnostics(&mut self) { + for diag in &self.diagnostics { + self.emitter.emit_diagnostic(&diag) + } + } + + /// If some diagnotsics generated by errors, `has_errors` returns `True`. + #[inline] + pub(crate) fn has_errors(&self) -> bool { + self.err_count > 0 + } + + /// If some diagnotsics generated by warnings, `has_errors` returns `True`. + #[inline] + pub(crate) fn has_warns(&self) -> bool { + self.warn_count > 0 + } + + /// After emitting all the diagnostics, it will panic. + pub(crate) fn abort_if_errors(&mut self) { + self.emit_stashed_diagnostics(); + + if self.has_errors() { + FatalError.raise(); + } + } + + /// Get the message string from "*.ftl" file by `index`, `sub_index` and `MessageArgs`. + /// "*.ftl" file looks like, e.g. './src/diagnostic/locales/en-US/default.ftl' : + pub(crate) fn get_diagnostic_msg( + &self, + index: &str, + sub_index: Option<&str>, + args: &MessageArgs, + ) -> Result { + self.template_loader.get_msg_to_str(index, sub_index, &args) + } +} diff --git a/kclvm/compiler_base/error/src/diagnostic/diagnostic_message.rs b/kclvm/compiler_base/error/src/diagnostic/diagnostic_message.rs index bf4a15897..abadba7dc 100644 --- a/kclvm/compiler_base/error/src/diagnostic/diagnostic_message.rs +++ b/kclvm/compiler_base/error/src/diagnostic/diagnostic_message.rs @@ -1,13 +1,16 @@ //! The crate provides `TemplateLoader` to load the diagnositc message displayed in diagnostics from "*.ftl" files, +//! `TemplateLoader` relies on 'fluent0.16.0' to support loading diagnositc message from "*.ftl" files. //! +//! 'fluent0.16.0' is used to support diagnostic text template. +//! For more information about 'fluent0.16.0', see https://projectfluent.org/. + use anyhow::{bail, Context, Result}; use fluent::{FluentBundle, FluentResource}; use std::{fs, sync::Arc}; use unic_langid::langid; use walkdir::{DirEntry, WalkDir}; -use crate::MessageArgs; - +use crate::diagnostic_handler::MessageArgs; /// Struct `TemplateLoader` load template contents from "*.ftl" file. /// `TemplateLoader` will operate on files locally. pub(crate) struct TemplateLoader { diff --git a/kclvm/compiler_base/error/src/diagnostic/mod.rs b/kclvm/compiler_base/error/src/diagnostic/mod.rs index 09db58120..ce5e41d07 100644 --- a/kclvm/compiler_base/error/src/diagnostic/mod.rs +++ b/kclvm/compiler_base/error/src/diagnostic/mod.rs @@ -2,6 +2,7 @@ pub use rustc_errors::styled_buffer::StyledBuffer; use rustc_errors::Style; pub mod components; +pub mod diagnostic_handler; pub mod diagnostic_message; pub mod style; diff --git a/kclvm/compiler_base/error/src/diagnostic/tests.rs b/kclvm/compiler_base/error/src/diagnostic/tests.rs index 63ba29928..b6482865c 100644 --- a/kclvm/compiler_base/error/src/diagnostic/tests.rs +++ b/kclvm/compiler_base/error/src/diagnostic/tests.rs @@ -77,7 +77,7 @@ mod test_components { } mod test_error_message { - use crate::{diagnostic::diagnostic_message::TemplateLoader, MessageArgs}; + use crate::{diagnostic::diagnostic_message::TemplateLoader, diagnostic_handler::MessageArgs}; #[test] fn test_template_message() { diff --git a/kclvm/compiler_base/error/src/diagnostic_handler.rs b/kclvm/compiler_base/error/src/diagnostic_handler.rs deleted file mode 100644 index d64fa0c00..000000000 --- a/kclvm/compiler_base/error/src/diagnostic_handler.rs +++ /dev/null @@ -1,104 +0,0 @@ -use crate::{ - diagnostic::diagnostic_message::TemplateLoader, Diagnostic, DiagnosticStyle, Emitter, - MessageArgs, TerminalEmitter, -}; -use anyhow::{Context, Result}; -use compiler_base_span::fatal_error::FatalError; -use std::sync::Arc; - -pub(crate) struct DiagnosticHandlerInner { - emitter: Box>, - diagnostics: Vec>, - err_count: usize, - warn_count: usize, - template_loader: Arc, -} - -impl DiagnosticHandlerInner { - /// Load all (*.ftl) template files under directory `template_dir`. - pub(crate) fn new_with_template_dir(template_dir: &str) -> Result { - let template_loader = TemplateLoader::new_with_template_dir(template_dir) - .with_context(|| format!("Failed to init `TemplateLoader` from '{}'", template_dir))?; - - Ok(Self { - err_count: 0, - warn_count: 0, - emitter: Box::new(TerminalEmitter::default()), - diagnostics: vec![], - template_loader: Arc::new(template_loader), - }) - } - - /// Add a diagnostic generated from error to `DiagnosticHandler`. - /// `DiagnosticHandler` contains a set of `Diagnostic` - pub(crate) fn add_err_diagnostic(&mut self, diag: Diagnostic) { - self.diagnostics.push(diag); - self.err_count += 1; - } - - /// Add a diagnostic generated from warning to `DiagnosticHandler`. - /// `DiagnosticHandler` contains a set of `Diagnostic` - pub(crate) fn add_warn_diagnostic(&mut self, diag: Diagnostic) { - self.diagnostics.push(diag); - self.warn_count += 1; - } - - /// Get count of diagnostics in `DiagnosticHandler`. - /// `DiagnosticHandler` contains a set of `Diagnostic` - #[inline] - pub(crate) fn diagnostics_count(&self) -> usize { - self.diagnostics.len() - } - - /// Emit the diagnostic messages generated from error to to terminal stderr. - pub(crate) fn emit_error_diagnostic(&mut self, diag: Diagnostic) { - self.emitter.emit_diagnostic(&diag); - self.err_count += 1; - } - - /// Emit the diagnostic messages generated from warning to to terminal stderr. - pub(crate) fn emit_warn_diagnostic(&mut self, diag: Diagnostic) { - self.emitter.emit_diagnostic(&diag); - self.warn_count += 1; - } - - /// Emit all the diagnostics messages to to terminal stderr. - /// `DiagnosticHandler` contains a set of `Diagnostic` - pub(crate) fn emit_stashed_diagnostics(&mut self) { - for diag in &self.diagnostics { - self.emitter.emit_diagnostic(&diag) - } - } - - /// If some diagnotsics generated by errors, `has_errors` returns `True`. - #[inline] - pub(crate) fn has_errors(&self) -> bool { - self.err_count > 0 - } - - /// If some diagnotsics generated by warnings, `has_errors` returns `True`. - #[inline] - pub(crate) fn has_warns(&self) -> bool { - self.warn_count > 0 - } - - /// After emitting all the diagnostics, it will panic. - pub(crate) fn abort_if_errors(&mut self) { - self.emit_stashed_diagnostics(); - - if self.has_errors() { - FatalError.raise(); - } - } - - /// Get the message string from "*.ftl" file by `index`, `sub_index` and `MessageArgs`. - /// "*.ftl" file looks like, e.g. './src/diagnostic/locales/en-US/default.ftl' : - pub(crate) fn get_diagnostic_msg( - &self, - index: &str, - sub_index: Option<&str>, - args: &MessageArgs, - ) -> Result { - self.template_loader.get_msg_to_str(index, sub_index, &args) - } -} diff --git a/kclvm/compiler_base/error/src/emitter.rs b/kclvm/compiler_base/error/src/emitter.rs index 98149481f..ccfdd22a2 100644 --- a/kclvm/compiler_base/error/src/emitter.rs +++ b/kclvm/compiler_base/error/src/emitter.rs @@ -9,7 +9,7 @@ //! //!Besides, it's easy to define your customized `Emitter` by implementing `Emitter` trait. //! For more information about how to define your customized `Emitter`, see the doc above `Emitter` trait. -//! + use crate::diagnostic::{Component, Diagnostic}; use compiler_base_macros::bug; use rustc_errors::{ diff --git a/kclvm/compiler_base/error/src/lib.rs b/kclvm/compiler_base/error/src/lib.rs index 8bc9c19c9..727025121 100644 --- a/kclvm/compiler_base/error/src/lib.rs +++ b/kclvm/compiler_base/error/src/lib.rs @@ -1,488 +1,19 @@ -//! This crate provides `DiagnosticHandler` supports diagnostic messages to terminal stderr. +//! Compiler-Base-Error //! -//! 'fluent0.16.0' is used to support diagnostic text template. -//! For more information about 'fluent0.16.0', see https://projectfluent.org/. +//! The idea with `Compiler-Base-Error` is to make a reusable library, +//! by separating out error thorwing and diagnostic diaplaying or other error handling procedures. //! -//! 'fluent0.16.0' uses "*.ftl" as the template file. -//! "*.ftl" file looks like, e.g. './src/diagnostic/locales/en-US/default.ftl' : +//! - Compiler-Base-Error provides `DiagnosticHandler` to diaplay diagnostic. +//! For more information about `DiagnosticHandler`, see doc in 'compiler_base/error/diagnostic/diagnostic_handler.rs'. //! -//! ``` ignore -//! invalid-syntax = Invalid syntax -//! .expected = Expected one of `{$expected_items}` -//! ``` -//! There are two lines in './src/diagnostic/locales/en-US/default.ftl'. -//! - In line 1, `invalid-syntax` is a `index`, `Invalid syntax` is the `Message String` to this `index`. -//! - In line 2, `.expected` is another `index`, it is a `sub_index` of `invalid-syntax`. -//! - In line 2, `sub_index` must start with a point `.` and it is optional and can be more than one. -//! - In line 2, `Expected one of `{$expected_items}`` is the `Message String` to `.expected`. It is an interpolated string. -//! - In line 2, `{$expected_items}` is a `MessageArgs` of the `Expected one of `{$expected_items}`` -//! and `MessageArgs` can be recognized as a Key-Value entry, it is optional. -//! -//! The pattern of above '*.ftl' file looks like: -//! ``` ignore -//! <'index'> = <'message_string' with optional 'MessageArgs'> -//! = <'message_string' with optional 'MessageArgs'>* -//! ``` +//! - TODO(zongz): Compiler-Base-Error provides `ErrorRecover` to recover from errors. mod diagnostic; -mod diagnostic_handler; mod emitter; - -use anyhow::{bail, Context, Result}; -use diagnostic_handler::DiagnosticHandlerInner; -use fluent::FluentArgs; -use std::sync::Mutex; - -pub use diagnostic::{components, style::DiagnosticStyle, Component, Diagnostic}; -pub use emitter::{Emitter, TerminalEmitter}; - #[cfg(test)] mod tests; -/// `DiagnosticHandler` supports diagnostic messages to terminal stderr. -/// -/// `DiagnosticHandler` will load template file directory when instantiating through the constructor `new_with_template_dir()`. -/// -/// Note: `DiagnosticHandler` uses `Mutex` internally to ensure thread safety, -/// so you don't need to use references like `Arc` or `Mutex` to make `DiagnosticHandler` thread safe. -/// -/// When your compiler needs to use `Compiler-Base-Error` to displaying diagnostics, you need to create a `DiagnosticHandler` at first. -/// For more information about how to create a `DiagnosticHandler`, see the doc above method `new_with_template_dir()`. -/// Since creating `DiagnosticHandler` needs to load the locally template (*.ftl) file, it may cause I/O performance loss, -/// so we recommend you create `DiagnosticHandler` globally in the compiler and pass references to other modules that use `DiagnosticHandler`. -/// -/// And since `DiagnosticHandler` provides methods that do not supports mutable references "&mut self", so passing immutable references (&) is enough. -/// -/// For Example: -/// -/// 1. You can put `DiagnosticHandler` on the same level as `Lexer`, `Parser` and `CodeGenerator` in your compiler. -/// ```ignore -/// struct Compiler { -/// diag_handler: DiagnosticHandler, -/// lang_lexer: Lexer, -/// lang_parser: Parser, -/// code_generator: CodeGenerator -/// } -/// ``` -/// -/// 2. And send the immutable references to `Lexer`, `Parser` and `CodeGenerator` to displaying the diagnostic during compiling. -/// ```ignore -/// impl Compiler { -/// fn compile(&self) { -/// self.lang_lexer.lex(&self.diag_handler); -/// self.lang_parser.parse(&self.diag_handler); -/// self.code_generator.gen(&self.diag_handler); -/// } -/// } -/// ``` -/// -/// ```ignore -/// impl Lexer { -/// fn lex(&self, diag_handler: &DiagnosticHandler){ -/// handler.XXXX(); // do something to diaplay diagnostic. -/// } -/// } -/// ``` -/// -pub struct DiagnosticHandler { - handler_inner: Mutex, -} - -impl DiagnosticHandler { - /// Load all (*.ftl) template files under default directory. - /// - /// Default directory "./src/diagnostic/locales/en-US/" - /// Call the constructor 'new_with_template_dir()' to load the file. - /// For more information about the constructor 'new_with_template_dir()', see the doc above 'new_with_template_dir()'. - pub fn default() -> Result { - const DEFAULT_TEMPLATE_RESOURCE: &'static str = "./src/diagnostic/locales/en-US/"; - let handler_inner = DiagnosticHandlerInner::new_with_template_dir( - DEFAULT_TEMPLATE_RESOURCE, - ) - .with_context(|| { - format!( - "Failed to init `TemplateLoader` from '{}'", - DEFAULT_TEMPLATE_RESOURCE - ) - })?; - Ok(Self { - handler_inner: Mutex::new(handler_inner), - }) - } - - /// Load all (*.ftl) template files under directory `template_dir`. - /// `DiagnosticHandler` will load all the files end with "*.ftl" under the directory recursively. - /// If directory `template_dir` does not exist, this method will return an error. - /// - /// template_files - /// | - /// |---- template.ftl - /// |---- sub_template_files - /// | - /// |---- sub_template.ftl - /// - /// 'template.ftl' and 'sub_template.ftl' can both loaded by the `new_with_template_dir()`. - /// - /// # Examples - /// - /// ```rust - /// # use compiler_base_error::DiagnosticHandler; - /// let diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/"); - /// match diag_handler { - /// Ok(_) => {} - /// Err(_) => {panic!("`diag_handler` should be Ok(...)")} - /// } - /// - /// // './src_invalid/diagnostic/locales/en-US/' does not exist. - /// let diag_handler_invalid = DiagnosticHandler::new_with_template_dir("./src_invalid/diagnostic/locales/en-US/"); - /// match diag_handler_invalid { - /// Ok(_) => {panic!("`diag_handler_invalid` should be Err(...)")} - /// Err(_) => {} - /// } - /// ``` - pub fn new_with_template_dir(template_dir: &str) -> Result { - let handler_inner = DiagnosticHandlerInner::new_with_template_dir(template_dir) - .with_context(|| format!("Failed to init `TemplateLoader` from '{}'", template_dir))?; - Ok(Self { - handler_inner: Mutex::new(handler_inner), - }) - } - - /// Add a diagnostic generated from error to `DiagnosticHandler`. - /// `DiagnosticHandler` contains a set of `Diagnostic` - /// - /// Note: `DiagnosticHandler` does not deduplicate diagnostics. - /// If you add two same diagnostics, you will see two same messages in the terminal. - /// - /// # Examples - /// - /// ```rust - /// # use compiler_base_error::DiagnosticStyle; - /// # use compiler_base_error::DiagnosticHandler; - /// # use compiler_base_error::Diagnostic; - /// let diag_1 = Diagnostic::::new(); - /// let mut diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); - /// assert_eq!(diag_handler.diagnostics_count().unwrap(), 0); - /// - /// diag_handler.add_err_diagnostic(diag_1); - /// assert_eq!(diag_handler.diagnostics_count().unwrap(), 1); - /// ``` - pub fn add_err_diagnostic(&self, diag: Diagnostic) -> Result<()> { - match self.handler_inner.lock() { - Ok(mut inner) => { - inner.add_err_diagnostic(diag); - Ok(()) - } - Err(_) => bail!("Add Error Diagnostic Failed."), - } - } - - /// Add a diagnostic generated from warning to `DiagnosticHandler`. - /// `DiagnosticHandler` contains a set of `Diagnostic` - /// - /// Note: `DiagnosticHandler` does not deduplicate diagnostics. - /// If you add two same diagnostics, you will see two same messages in the terminal. - /// - /// # Examples - /// - /// ```rust - /// # use compiler_base_error::DiagnosticStyle; - /// # use compiler_base_error::DiagnosticHandler; - /// # use compiler_base_error::Diagnostic; - /// let diag_1 = Diagnostic::::new(); - /// let mut diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); - /// assert_eq!(diag_handler.diagnostics_count().unwrap(), 0); - /// - /// diag_handler.add_warn_diagnostic(diag_1); - /// assert_eq!(diag_handler.diagnostics_count().unwrap(), 1); - /// ``` - pub fn add_warn_diagnostic(&self, diag: Diagnostic) -> Result<()> { - match self.handler_inner.lock() { - Ok(mut inner) => { - inner.add_warn_diagnostic(diag); - Ok(()) - } - Err(_) => bail!("Add Warn Diagnostic Failed."), - } - } - - /// Get count of diagnostics in `DiagnosticHandler`. - /// `DiagnosticHandler` contains a set of `Diagnostic` - /// - /// # Examples - /// - /// ```rust - /// # use compiler_base_error::DiagnosticStyle; - /// # use compiler_base_error::DiagnosticHandler; - /// # use compiler_base_error::Diagnostic; - /// let diag_1 = Diagnostic::::new(); - /// let mut diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); - /// assert_eq!(diag_handler.diagnostics_count().unwrap(), 0); - /// - /// diag_handler.add_warn_diagnostic(diag_1); - /// assert_eq!(diag_handler.diagnostics_count().unwrap(), 1); - /// ``` - pub fn diagnostics_count(&self) -> Result { - match self.handler_inner.lock() { - Ok(inner) => Ok(inner.diagnostics_count()), - Err(_) => bail!("Diagnostics Counts Failed."), - } - } - - /// Emit the diagnostic messages generated from error to to terminal stderr. - /// - /// # Examples - /// - /// ```rust - /// # use compiler_base_error::DiagnosticStyle; - /// # use compiler_base_error::DiagnosticHandler; - /// # use compiler_base_error::Diagnostic; - /// let diag_1 = Diagnostic::::new(); - /// let mut diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); - /// - /// assert_eq!(diag_handler.has_errors().unwrap(), false); - /// diag_handler.emit_error_diagnostic(diag_1); - /// assert_eq!(diag_handler.has_errors().unwrap(), true); - /// ``` - pub fn emit_error_diagnostic(&self, diag: Diagnostic) -> Result<()> { - match self.handler_inner.lock() { - Ok(mut inner) => { - inner.emit_error_diagnostic(diag); - Ok(()) - } - Err(_) => bail!("Emit Error Diagnostics Failed."), - } - } - - /// Emit the diagnostic messages generated from warning to to terminal stderr. - /// - /// # Examples - /// - /// ```rust - /// # use compiler_base_error::DiagnosticStyle; - /// # use compiler_base_error::DiagnosticHandler; - /// # use compiler_base_error::Diagnostic; - /// let diag_1 = Diagnostic::::new(); - /// let mut diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); - /// - /// assert_eq!(diag_handler.has_warns().unwrap(), false); - /// diag_handler.emit_warn_diagnostic(diag_1); - /// assert_eq!(diag_handler.has_warns().unwrap(), true); - /// ``` - pub fn emit_warn_diagnostic(&self, diag: Diagnostic) -> Result<()> { - match self.handler_inner.lock() { - Ok(mut inner) => { - inner.emit_warn_diagnostic(diag); - Ok(()) - } - Err(_) => bail!("Emit Warn Diagnostics Failed."), - } - } - - /// Emit all the diagnostics messages to to terminal stderr. - /// `DiagnosticHandler` contains a set of `Diagnostic` - /// - /// # Examples - /// - /// ```rust - /// # use compiler_base_error::DiagnosticStyle; - /// # use compiler_base_error::DiagnosticHandler; - /// # use compiler_base_error::Diagnostic; - /// let diag_1 = Diagnostic::::new(); - /// let diag_2 = Diagnostic::::new(); - /// let mut diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); - /// - /// diag_handler.add_err_diagnostic(diag_1); - /// diag_handler.add_err_diagnostic(diag_2); - /// diag_handler.emit_stashed_diagnostics(); - /// ``` - pub fn emit_stashed_diagnostics(&self) -> Result<()> { - match self.handler_inner.lock() { - Ok(mut inner) => { - inner.emit_stashed_diagnostics(); - Ok(()) - } - Err(_) => bail!("Emit Stashed Diagnostics Failed."), - } - } - - /// If some diagnotsics generated by errors, `has_errors` returns `True`. - /// - /// # Examples - /// - /// ```rust - /// # use compiler_base_error::DiagnosticStyle; - /// # use compiler_base_error::DiagnosticHandler; - /// # use compiler_base_error::Diagnostic; - /// let diag_1 = Diagnostic::::new(); - /// let mut diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); - /// - /// assert_eq!(diag_handler.has_errors().unwrap(), false); - /// diag_handler.emit_error_diagnostic(diag_1); - /// assert_eq!(diag_handler.has_errors().unwrap(), true); - /// ``` - pub fn has_errors(&self) -> Result { - match self.handler_inner.lock() { - Ok(inner) => Ok(inner.has_errors()), - Err(_) => bail!("Check Has Errors Failed."), - } - } - - /// If some diagnotsics generated by warnings, `has_errors` returns `True`. - /// - /// # Examples - /// - /// ```rust - /// # use compiler_base_error::DiagnosticStyle; - /// # use compiler_base_error::DiagnosticHandler; - /// # use compiler_base_error::Diagnostic; - /// let diag_1 = Diagnostic::::new(); - /// let mut diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); - /// - /// assert_eq!(diag_handler.has_warns().unwrap(), false); - /// diag_handler.emit_warn_diagnostic(diag_1); - /// assert_eq!(diag_handler.has_warns().unwrap(), true); - /// ``` - pub fn has_warns(&self) -> Result { - match self.handler_inner.lock() { - Ok(inner) => Ok(inner.has_warns()), - Err(_) => bail!("Check Has Warns Failed."), - } - } - - /// After emitting all the diagnostics, it will panic. - /// - /// # Examples - /// - /// ```rust - /// # use compiler_base_error::DiagnosticStyle; - /// # use compiler_base_error::Diagnostic; - /// # use compiler_base_error::DiagnosticHandler; - /// # use std::panic; - /// let diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); - /// - /// diag_handler.abort_if_errors().unwrap(); - /// diag_handler.add_warn_diagnostic(Diagnostic::::new()).unwrap(); - /// - /// diag_handler.abort_if_errors().unwrap(); - /// diag_handler.add_err_diagnostic(Diagnostic::::new()).unwrap(); - /// - /// let result = panic::catch_unwind(|| { - /// diag_handler.abort_if_errors().unwrap(); - /// }); - /// assert!(result.is_err()); - /// ``` - pub fn abort_if_errors(&self) -> Result<()> { - match self.handler_inner.lock() { - Ok(mut inner) => { - inner.abort_if_errors(); - Ok(()) - } - Err(_) => bail!("Abort If Errors Failed."), - } - } - - /// Get the message string from "*.ftl" file by `index`, `sub_index` and `MessageArgs`. - /// And for the 'default.ftl' shown above, you can get messages as follow: - /// - /// ```ignore - /// invalid-syntax = Invalid syntax - /// .expected = Expected one of `{$expected_items}` - /// ``` - /// - /// 1. If you want the message 'Invalid syntax' in line 1. - /// - /// ``` rust - /// # use compiler_base_error::MessageArgs; - /// # use compiler_base_error::DiagnosticHandler; - /// - /// // 1. Prepare an empty `MessageArgs`, Message in line 1 is not an interpolated string. - /// let no_args = MessageArgs::new(); - /// - /// // 2. `index` is 'invalid-syntax' and has no `sub_index`. - /// let index = "invalid-syntax"; - /// let sub_index = None; - /// - /// // 3. Create the `DiagnosticHandler` with template (*.ftl) files directory. - /// let diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); - /// - /// // 4. Get the message. - /// let msg_in_line_1 = diag_handler.get_diagnostic_msg(index, sub_index, &no_args).unwrap(); - /// - /// assert_eq!(msg_in_line_1, "Invalid syntax"); - /// ``` - /// - /// 2. If you want the message 'Expected one of `{$expected_items}`' in line 2. - /// - /// ``` rust - /// # use compiler_base_error::MessageArgs; - /// # use compiler_base_error::DiagnosticHandler; - /// - /// // 1. Prepare the `MessageArgs` for `{$expected_items}`. - /// let mut args = MessageArgs::new(); - /// args.set("expected_items", "I am an expected item"); - /// - /// // 2. `index` is 'invalid-syntax'. - /// let index = "invalid-syntax"; - /// - /// // 3. `sub_index` is 'expected'. - /// let sub_index = "expected"; - /// - /// // 4. Create the `DiagnosticHandler` with template (*.ftl) files directory. - /// let diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); - /// - /// // 5. Get the message. - /// let msg_in_line_2 = diag_handler.get_diagnostic_msg(index, Some(sub_index), &args).unwrap(); - /// - /// assert_eq!(msg_in_line_2, "Expected one of `\u{2068}I am an expected item\u{2069}`"); - /// ``` - pub fn get_diagnostic_msg( - &self, - index: &str, - sub_index: Option<&str>, - args: &MessageArgs, - ) -> Result { - match self.handler_inner.lock() { - Ok(inner) => inner.get_diagnostic_msg(index, sub_index, args), - Err(_) => bail!("Find Diagnostic Message Failed."), - } - } -} - -/// `MessageArgs` is the arguments of the interpolated string. -/// -/// `MessageArgs` is a Key-Value entry which only supports "set" and without "get". -/// You need getting nothing from `MessageArgs`. Only setting it and senting it to `DiagnosticHandler` is enough. -/// -/// Note: Currently both `Key` and `Value` of `MessageArgs` types only support string (&str). -/// -/// # Examples -/// -/// ``` rust -/// # use compiler_base_error::DiagnosticHandler; -/// # use compiler_base_error::MessageArgs; -/// -/// let index = "invalid-syntax"; -/// let sub_index = Some("expected"); -/// let mut msg_args = MessageArgs::new(); -/// // You only need "set()". -/// msg_args.set("This is Key", "This is Value"); -/// -/// // Create the `DiagnosticHandler` with template (*.ftl) files directory. -/// let diag_handler = DiagnosticHandler::new_with_template_dir("./src/diagnostic/locales/en-US/").unwrap(); -/// -/// // When you use it, just sent it to `DiagnosticHandler`. -/// let msg_in_line_1 = diag_handler.get_diagnostic_msg(index, sub_index, &msg_args); -/// ``` -/// -/// For more information about the `DiagnosticHandler` see the doc above struct `DiagnosticHandler`. -pub struct MessageArgs<'a>(FluentArgs<'a>); -impl<'a> MessageArgs<'a> { - pub fn new() -> Self { - Self(FluentArgs::new()) - } - - pub fn set(&mut self, k: &'a str, v: &'a str) { - self.0.set(k, v); - } -} +pub use diagnostic::{ + components, diagnostic_handler, style::DiagnosticStyle, Component, Diagnostic, +}; +pub use emitter::{Emitter, TerminalEmitter}; diff --git a/kclvm/compiler_base/error/src/tests.rs b/kclvm/compiler_base/error/src/tests.rs index bcf075d3e..f235fc4e5 100644 --- a/kclvm/compiler_base/error/src/tests.rs +++ b/kclvm/compiler_base/error/src/tests.rs @@ -1,7 +1,10 @@ mod test_diagnostic_handler { use std::panic; - use crate::{Diagnostic, DiagnosticHandler, DiagnosticStyle, MessageArgs}; + use crate::{ + diagnostic_handler::{DiagnosticHandler, MessageArgs}, + Diagnostic, DiagnosticStyle, + }; #[test] fn test_diagnostic_handler_new_with_template_dir() { From 414a1981f8427c1a59fd36776d8e7bbeb5dd6038 Mon Sep 17 00:00:00 2001 From: zong-zhe Date: Thu, 1 Sep 2022 10:06:49 +0800 Subject: [PATCH 17/22] fix mistake --- .../src/diagnostic/diagnostic_handler.rs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/kclvm/compiler_base/error/src/diagnostic/diagnostic_handler.rs b/kclvm/compiler_base/error/src/diagnostic/diagnostic_handler.rs index 7f64f2e32..86e994ac4 100644 --- a/kclvm/compiler_base/error/src/diagnostic/diagnostic_handler.rs +++ b/kclvm/compiler_base/error/src/diagnostic/diagnostic_handler.rs @@ -18,6 +18,9 @@ use compiler_base_span::fatal_error::FatalError; use fluent::FluentArgs; use std::sync::{Arc, Mutex}; +// Default template resource file path. +const DEFAULT_TEMPLATE_RESOURCE: &'static str = "./src/diagnostic/locales/en-US/"; + /// `DiagnosticHandler` supports diagnostic messages to terminal stderr. /// /// `DiagnosticHandler` will load template file(*ftl) directory when instantiating through the constructor `new_with_template_dir()`. @@ -86,6 +89,26 @@ pub struct DiagnosticHandler { } impl DiagnosticHandler { + /// Load all (*.ftl) template files under default directory. + /// + /// Default directory "./src/diagnostic/locales/en-US/" + /// Call the constructor 'new_with_template_dir()' to load the file. + /// For more information about the constructor 'new_with_template_dir()', see the doc above 'new_with_template_dir()'. + pub fn default() -> Result { + let handler_inner = DiagnosticHandlerInner::new_with_template_dir( + DEFAULT_TEMPLATE_RESOURCE, + ) + .with_context(|| { + format!( + "Failed to init `TemplateLoader` from '{}'", + DEFAULT_TEMPLATE_RESOURCE + ) + })?; + Ok(Self { + handler_inner: Mutex::new(handler_inner), + }) + } + /// Load all (*.ftl) template files under directory `template_dir`. /// `DiagnosticHandler` will load all the files end with "*.ftl" under the directory recursively. /// If directory `template_dir` does not exist, this method will return an error. From 32bf5267f661c74c14097128174770aff0fed689 Mon Sep 17 00:00:00 2001 From: zong-zhe Date: Thu, 1 Sep 2022 10:09:03 +0800 Subject: [PATCH 18/22] fix mistake --- .../compiler_base/error/src/diagnostic/diagnostic_handler.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/kclvm/compiler_base/error/src/diagnostic/diagnostic_handler.rs b/kclvm/compiler_base/error/src/diagnostic/diagnostic_handler.rs index 86e994ac4..82cd8d1f9 100644 --- a/kclvm/compiler_base/error/src/diagnostic/diagnostic_handler.rs +++ b/kclvm/compiler_base/error/src/diagnostic/diagnostic_handler.rs @@ -95,7 +95,7 @@ impl DiagnosticHandler { /// Call the constructor 'new_with_template_dir()' to load the file. /// For more information about the constructor 'new_with_template_dir()', see the doc above 'new_with_template_dir()'. pub fn default() -> Result { - let handler_inner = DiagnosticHandlerInner::new_with_template_dir( + DiagnosticHandler::new_with_template_dir( DEFAULT_TEMPLATE_RESOURCE, ) .with_context(|| { @@ -103,9 +103,6 @@ impl DiagnosticHandler { "Failed to init `TemplateLoader` from '{}'", DEFAULT_TEMPLATE_RESOURCE ) - })?; - Ok(Self { - handler_inner: Mutex::new(handler_inner), }) } From f7bf469c09935c85b7c9be42a528725027d51364 Mon Sep 17 00:00:00 2001 From: zong-zhe Date: Thu, 1 Sep 2022 11:13:07 +0800 Subject: [PATCH 19/22] add comments for eager and lazy loading --- .../src/diagnostic/diagnostic_handler.rs | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/kclvm/compiler_base/error/src/diagnostic/diagnostic_handler.rs b/kclvm/compiler_base/error/src/diagnostic/diagnostic_handler.rs index 82cd8d1f9..1c9c47052 100644 --- a/kclvm/compiler_base/error/src/diagnostic/diagnostic_handler.rs +++ b/kclvm/compiler_base/error/src/diagnostic/diagnostic_handler.rs @@ -50,6 +50,12 @@ const DEFAULT_TEMPLATE_RESOURCE: &'static str = "./src/diagnostic/locales/en-US/ /// For more information about how to create a `DiagnosticHandler`, see the doc above method `new_with_template_dir()`. /// Since creating `DiagnosticHandler` needs to load the locally template (*.ftl) file, it may cause I/O performance loss, /// so we recommend you create `DiagnosticHandler` globally in the compiler and pass references to other modules that use `DiagnosticHandler`. +/// +/// Note: +/// - Eager loading: If you created a global instance of `DiagnosticHandler` by eager loading, +/// then this may affect the performance of your compiler at startup, because `DiagnosticHandler` contains IO to load file. +/// - Lazy loading: If you created a global instance of `DiagnosticHandler` by lazy loading, +/// you need to guarantee that the instantiation of `DiagnosticHandler` is done before passing references to other modules. /// /// And since `DiagnosticHandler` provides methods that do not supports mutable references "&mut self", so passing immutable references (&) is enough. /// @@ -579,3 +585,34 @@ impl DiagnosticHandlerInner { self.template_loader.get_msg_to_str(index, sub_index, &args) } } + + +#[cfg(test)] +mod test{ + use std::cell::RefCell; + + struct Test{ + s: String, + } + + // impl Test{ + // fn ttt(&mut self, a: &mut String){ + // self.ttt(&mut self.s); + // } + // } + + struct Test1{ + s: RefCell, + } + + struct TestInner{ + s: String + } + + impl Test1{ + fn ttt(&self, a: &mut String){ + self.ttt(&mut self.s.borrow_mut().s); + } + } + +} \ No newline at end of file From dc0e008d4b603a5e078db17d4c6a44cd7eebf7bb Mon Sep 17 00:00:00 2001 From: zong-zhe Date: Thu, 1 Sep 2022 11:14:30 +0800 Subject: [PATCH 20/22] fix typo --- kclvm/compiler_base/error/src/diagnostic/diagnostic_handler.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kclvm/compiler_base/error/src/diagnostic/diagnostic_handler.rs b/kclvm/compiler_base/error/src/diagnostic/diagnostic_handler.rs index 1c9c47052..eb57219bf 100644 --- a/kclvm/compiler_base/error/src/diagnostic/diagnostic_handler.rs +++ b/kclvm/compiler_base/error/src/diagnostic/diagnostic_handler.rs @@ -53,7 +53,7 @@ const DEFAULT_TEMPLATE_RESOURCE: &'static str = "./src/diagnostic/locales/en-US/ /// /// Note: /// - Eager loading: If you created a global instance of `DiagnosticHandler` by eager loading, -/// then this may affect the performance of your compiler at startup, because `DiagnosticHandler` contains IO to load file. +/// then this may loss the performance of your compiler at startup, because `DiagnosticHandler` contains IO to load file. /// - Lazy loading: If you created a global instance of `DiagnosticHandler` by lazy loading, /// you need to guarantee that the instantiation of `DiagnosticHandler` is done before passing references to other modules. /// From 20e4eb440a05b265fad916bae1e57b89bdabb35a Mon Sep 17 00:00:00 2001 From: zong-zhe Date: Thu, 1 Sep 2022 11:16:27 +0800 Subject: [PATCH 21/22] fix typo --- kclvm/compiler_base/error/src/diagnostic/diagnostic_handler.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kclvm/compiler_base/error/src/diagnostic/diagnostic_handler.rs b/kclvm/compiler_base/error/src/diagnostic/diagnostic_handler.rs index eb57219bf..090a79825 100644 --- a/kclvm/compiler_base/error/src/diagnostic/diagnostic_handler.rs +++ b/kclvm/compiler_base/error/src/diagnostic/diagnostic_handler.rs @@ -53,7 +53,7 @@ const DEFAULT_TEMPLATE_RESOURCE: &'static str = "./src/diagnostic/locales/en-US/ /// /// Note: /// - Eager loading: If you created a global instance of `DiagnosticHandler` by eager loading, -/// then this may loss the performance of your compiler at startup, because `DiagnosticHandler` contains IO to load file. +/// then this may loss the performance of your compiler at startup, because the constructor of `DiagnosticHandler` contains I/O to load file. /// - Lazy loading: If you created a global instance of `DiagnosticHandler` by lazy loading, /// you need to guarantee that the instantiation of `DiagnosticHandler` is done before passing references to other modules. /// From 6180debaaa5a553e4fce7c6f6d366f1133e7eb08 Mon Sep 17 00:00:00 2001 From: zong-zhe Date: Thu, 1 Sep 2022 11:33:17 +0800 Subject: [PATCH 22/22] fix comments --- .../error/src/diagnostic/diagnostic_handler.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/kclvm/compiler_base/error/src/diagnostic/diagnostic_handler.rs b/kclvm/compiler_base/error/src/diagnostic/diagnostic_handler.rs index 090a79825..09a191845 100644 --- a/kclvm/compiler_base/error/src/diagnostic/diagnostic_handler.rs +++ b/kclvm/compiler_base/error/src/diagnostic/diagnostic_handler.rs @@ -49,13 +49,7 @@ const DEFAULT_TEMPLATE_RESOURCE: &'static str = "./src/diagnostic/locales/en-US/ /// When your compiler needs to use `Compiler-Base-Error` to displaying diagnostics, you need to create a `DiagnosticHandler` at first. /// For more information about how to create a `DiagnosticHandler`, see the doc above method `new_with_template_dir()`. /// Since creating `DiagnosticHandler` needs to load the locally template (*.ftl) file, it may cause I/O performance loss, -/// so we recommend you create `DiagnosticHandler` globally in the compiler and pass references to other modules that use `DiagnosticHandler`. -/// -/// Note: -/// - Eager loading: If you created a global instance of `DiagnosticHandler` by eager loading, -/// then this may loss the performance of your compiler at startup, because the constructor of `DiagnosticHandler` contains I/O to load file. -/// - Lazy loading: If you created a global instance of `DiagnosticHandler` by lazy loading, -/// you need to guarantee that the instantiation of `DiagnosticHandler` is done before passing references to other modules. +/// so we recommend you create `DiagnosticHandler` eagerly and globally in the compiler and pass references to other modules that use `DiagnosticHandler`. /// /// And since `DiagnosticHandler` provides methods that do not supports mutable references "&mut self", so passing immutable references (&) is enough. ///