add warning to if-statement in check mode

warning is shown if the condition of an
if statement can never be true or false,
and the user used the check command.
warnings are not shown when you use `mers run`.
This commit is contained in:
Mark 2024-08-29 15:48:01 +02:00
parent 385019e43c
commit 817ed25f96
5 changed files with 56 additions and 6 deletions

View File

@ -125,7 +125,8 @@ fn main() {
exit(20); exit(20);
} }
Ok(parsed) => { Ok(parsed) => {
let (i1, _, i3) = config.infos(); let (i1, _, mut i3) = config.infos();
i3.global.show_warnings_to_stderr();
match compile(&*parsed, i1) { match compile(&*parsed, i1) {
Err(e) => { Err(e) => {
eprintln!("{e:?}"); eprintln!("{e:?}");

View File

@ -97,6 +97,8 @@ pub enum EColor {
MaximumRuntimeExceeded, MaximumRuntimeExceeded,
InCodePositionLine, InCodePositionLine,
Warning,
} }
pub trait ETheme: ThemeGen<C = EColor, T = String> {} pub trait ETheme: ThemeGen<C = EColor, T = String> {}

View File

@ -42,6 +42,10 @@ pub fn default_theme<C>(
_ => return None, _ => 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 hard_err = red;
let type_right = blue; let type_right = blue;
let type_wrong = magenta; let type_wrong = magenta;
@ -56,7 +60,7 @@ pub fn default_theme<C>(
drop(cyan_bright); drop(cyan_bright);
use EColor::*; use EColor::*;
Some(match color { Some(match color {
Indent(_) => unreachable!(), Indent(_) | Warning => unreachable!(),
// macros (#...) // macros (#...)
WhitespaceAfterHashtag WhitespaceAfterHashtag

View File

@ -1,7 +1,7 @@
use std::sync::Arc; use std::sync::Arc;
use crate::{ use crate::{
data::{self, tuple::TupleT, Data, MersType, Type}, data::{self, tuple::TupleT, Data, Type},
errors::{CheckError, EColor, SourceRange}, 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)? self.on_true.check(info, None)?
} else { } else {
Type::empty() 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 { if let Some(f) = &self.on_false {
t.add_all(&f.check(info, None)?); t.add_all(&f.check(info, None)?);
} else { } else {
t.add(Arc::new(TupleT(vec![]))); 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) Ok(t)
} }
fn run_custom(&self, info: &mut super::Info) -> Result<Data, CheckError> { fn run_custom(&self, info: &mut super::Info) -> Result<Data, CheckError> {

View File

@ -142,10 +142,11 @@ pub struct CheckLocal {
>, >,
>, >,
} }
#[derive(Clone, Debug, Default)] #[derive(Clone, Default)]
pub struct CheckLocalGlobalInfo { pub struct CheckLocalGlobalInfo {
pub depth: usize, pub depth: usize,
pub enable_hooks: bool, pub enable_hooks: bool,
pub show_warnings: Option<Arc<dyn Fn(CheckError) + Send + Sync>>,
/// ((results, byte_pos_in_src, deepest_statement)) /// ((results, byte_pos_in_src, deepest_statement))
/// you only have to set `byte_pos_in_src`. `deepest` is used internally. /// 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`). /// 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<Mutex<Vec<(SourceRange, Vec<Option<SourceRange>>)>>>, pub unused_try_statements: Arc<Mutex<Vec<(SourceRange, Vec<Option<SourceRange>>)>>>,
} }
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 { impl Debug for CheckLocal {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "CheckLocal {:?}, {:?}", self.vars, self.types.keys()) write!(f, "CheckLocal {:?}, {:?}", self.vars, self.types.keys())