diff --git a/mers/src/main.rs b/mers/src/main.rs index ede4906..69853b8 100755 --- a/mers/src/main.rs +++ b/mers/src/main.rs @@ -125,7 +125,8 @@ fn main() { exit(20); } Ok(parsed) => { - let (i1, _, i3) = config.infos(); + let (i1, _, mut i3) = config.infos(); + i3.global.show_warnings_to_stderr(); match compile(&*parsed, i1) { Err(e) => { eprintln!("{e:?}"); diff --git a/mers_lib/src/errors/mod.rs b/mers_lib/src/errors/mod.rs index 61eb64c..0ff07e0 100644 --- a/mers_lib/src/errors/mod.rs +++ b/mers_lib/src/errors/mod.rs @@ -97,6 +97,8 @@ pub enum EColor { MaximumRuntimeExceeded, InCodePositionLine, + + Warning, } pub trait ETheme: ThemeGen {} diff --git a/mers_lib/src/errors/themes.rs b/mers_lib/src/errors/themes.rs index 3b4f20d..8e7ded5 100644 --- a/mers_lib/src/errors/themes.rs +++ b/mers_lib/src/errors/themes.rs @@ -42,6 +42,10 @@ pub fn default_theme( _ => return None, }); } + if let Warning = color { + // same color as runtime errors, because warnings are only displayed in `check` mode, where the program won't run. + return Some(yellow); + } let hard_err = red; let type_right = blue; let type_wrong = magenta; @@ -56,7 +60,7 @@ pub fn default_theme( drop(cyan_bright); use EColor::*; Some(match color { - Indent(_) => unreachable!(), + Indent(_) | Warning => unreachable!(), // macros (#...) WhitespaceAfterHashtag diff --git a/mers_lib/src/program/run/if.rs b/mers_lib/src/program/run/if.rs index 33ebf67..d2ead0a 100755 --- a/mers_lib/src/program/run/if.rs +++ b/mers_lib/src/program/run/if.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use crate::{ - data::{self, tuple::TupleT, Data, MersType, Type}, + data::{self, tuple::TupleT, Data, Type}, errors::{CheckError, EColor, SourceRange}, }; @@ -47,18 +47,44 @@ impl MersStatement for If { ), ])); } - let mut t = if Type::new(data::bool::TrueT).is_included_in(&cond_return_type) { + let may_be_true = Type::new(data::bool::TrueT).is_included_in(&cond_return_type); + let may_be_false = Type::new(data::bool::FalseT).is_included_in(&cond_return_type); + let mut t = if may_be_true { self.on_true.check(info, None)? } else { Type::empty() }; - if Type::new(data::bool::FalseT).is_included_in(&cond_return_type) { + if may_be_false { if let Some(f) = &self.on_false { t.add_all(&f.check(info, None)?); } else { t.add(Arc::new(TupleT(vec![]))); } } + if let Some(show_warning) = &info.global.show_warnings { + if !may_be_false || !may_be_true { + let mut e = CheckError::new().src(vec![ + (self.pos_in_src.clone(), None), + ( + self.condition.source_range(), + Some(EColor::IfConditionNotBool), + ), + ]); + if !may_be_true { + e.msg_mut(vec![( + "Condition in this if-statement is never true".to_owned(), + Some(EColor::Warning), + )]); + } + if !may_be_false { + e.msg_mut(vec![( + "Condition in this if-statement is never false".to_owned(), + Some(EColor::Warning), + )]); + } + show_warning(e); + } + } Ok(t) } fn run_custom(&self, info: &mut super::Info) -> Result { diff --git a/mers_lib/src/program/run/mod.rs b/mers_lib/src/program/run/mod.rs index 3718091..91e6ceb 100755 --- a/mers_lib/src/program/run/mod.rs +++ b/mers_lib/src/program/run/mod.rs @@ -142,10 +142,11 @@ pub struct CheckLocal { >, >, } -#[derive(Clone, Debug, Default)] +#[derive(Clone, Default)] pub struct CheckLocalGlobalInfo { pub depth: usize, pub enable_hooks: bool, + pub show_warnings: Option>, /// ((results, byte_pos_in_src, deepest_statement)) /// you only have to set `byte_pos_in_src`. `deepest` is used internally. /// These values should be initialized to `(vec![], _, 0)`, but `0` can be replaced by a minimum statement depth, i.e. `2` to exclude the outer scope (which has depth `1`). @@ -160,6 +161,22 @@ pub struct CheckLocalGlobalInfo { >, pub unused_try_statements: Arc>)>>>, } +impl CheckLocalGlobalInfo { + pub fn show_warnings_to_stderr(&mut self) { + self.show_warnings = Some(Arc::new(|e| { + #[cfg(feature = "ecolor-term")] + let theme = crate::errors::themes::TermDefaultTheme; + #[cfg(not(feature = "ecolor-term"))] + let theme = crate::errors::themes::NoTheme; + eprintln!("{}", e.display(theme)); + })); + } +} +impl Debug for CheckLocalGlobalInfo { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "CheckLocalGlobalInfo {{ depth: {}, enable_hooks: {}, show_warnings: {}, unused_try_statements: {} }}", self.depth, self.enable_hooks, self.show_warnings.is_some(), self.unused_try_statements.lock().unwrap().len()) + } +} impl Debug for CheckLocal { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "CheckLocal {:?}, {:?}", self.vars, self.types.keys())