diff --git a/Cargo.lock b/Cargo.lock index f0e1237..62a972b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -44,6 +44,15 @@ dependencies = [ "libc", ] +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + [[package]] name = "ariadne" version = "0.4.1" @@ -54,12 +63,29 @@ dependencies = [ "yansi", ] +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + [[package]] name = "autocfg" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bumpalo" version = "3.16.0" @@ -105,6 +131,21 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -121,6 +162,24 @@ dependencies = [ "allocator-api2", ] +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + [[package]] name = "iana-time-zone" version = "0.1.61" @@ -153,6 +212,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + [[package]] name = "libc" version = "0.2.155" @@ -186,6 +251,30 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" version = "1.0.85" @@ -247,7 +336,7 @@ checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.66", ] [[package]] @@ -263,6 +352,47 @@ dependencies = [ "winapi", ] +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "structopt" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" +dependencies = [ + "clap", + "lazy_static", + "structopt-derive", +] + +[[package]] +name = "structopt-derive" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.66" @@ -274,18 +404,39 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + [[package]] name = "unicode-width" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + [[package]] name = "version_check" version = "0.9.4" @@ -314,7 +465,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.66", "wasm-bindgen-shared", ] @@ -336,7 +487,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.66", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -450,6 +601,7 @@ dependencies = [ "chrono", "chumsky", "log", + "structopt", ] [[package]] @@ -475,5 +627,5 @@ checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.66", ] diff --git a/Cargo.toml b/Cargo.toml index f3044e5..738ce14 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ name = "xs-check" version = "0.1.0" authors = ["Alian713"] edition = "2021" +description = "A linter for AoE2:DE's flavour of XS" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -13,6 +14,7 @@ chrono = "0.4.38" ariadne = "0.4.1" chumsky = "1.0.0-alpha.7" log = "0.4.21" +structopt = "0.3.26" [[bin]] name = "xs-check" diff --git a/README.md b/README.md index 584a637..044f901 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,18 @@ # XS Check -This tool is a linter for AoE2:DE's flavour of XS. +A linter for AoE2:DE's flavour of XS. + +## Installation + +You may download and use the prebuilt binaries from [here](https://github.com/Divy1211/xs-check/releases/latest) + +Or instead, if you have rust installed and prefer to build it from source, you can simply run: + +```sh +cargo install --git https://github.com/Divy1211/xs-check +``` + +This is recommended, as it will automatically add the binary to your system's path variable. ## Cool Maths diff --git a/src/cli.rs b/src/cli.rs new file mode 100644 index 0000000..6d6e401 --- /dev/null +++ b/src/cli.rs @@ -0,0 +1,61 @@ +use std::collections::HashSet; +use std::path::PathBuf; +use structopt::StructOpt; +use crate::r#static::xs_error::WarningKind; + +fn from_str(ignores: &str) -> Result, &str> { + ignores + .split(",") + .map(str::trim) + .map(|str| { + match WarningKind::from_str(str) { + None => { Err(str) } + Some(kind) => { Ok(kind.as_u32()) } + } + }).collect() +} + +#[derive(Debug, StructOpt)] +#[structopt(name = "xs-check", about = env!("CARGO_PKG_DESCRIPTION"))] +struct Opt { + #[structopt(parse(from_os_str))] + filepath: Option, + + #[structopt(short, long, help = "Show binary version & info")] + version: bool, + + #[structopt(short, long, help = "Comma separated list of names of warnings to ignore", parse(try_from_str = from_str))] + ignores: Option>, +} + +include!(concat!(env!("OUT_DIR"), "/build_date.rs")); + +fn print_info() { + let name = env!("CARGO_PKG_NAME"); + let version = env!("CARGO_PKG_VERSION"); + let authors = env!("CARGO_PKG_AUTHORS"); + let description = env!("CARGO_PKG_DESCRIPTION"); + + println!("{name} v{version}: {description}"); + println!("Author: {authors}"); + println!("Compiled: {BUILD_DATE}"); +} + +pub fn parse_args() -> Option<(PathBuf, HashSet)> { + let opt = Opt::from_args(); + if opt.version { + print_info(); + return None; + } + + match opt.filepath { + None => { + Opt::clap().print_help().unwrap(); + println!(); + None + } + Some(filepath) => { + Some((filepath, opt.ignores.unwrap_or_else(HashSet::new))) + } + } +} \ No newline at end of file diff --git a/src/lint/gen_errs.rs b/src/lint/gen_errs.rs index 9c05020..b6d1e19 100644 --- a/src/lint/gen_errs.rs +++ b/src/lint/gen_errs.rs @@ -1,3 +1,4 @@ +use std::collections::HashSet; use std::fmt::Display; use std::path::PathBuf; @@ -23,16 +24,19 @@ fn msg_fmt(mut msg: &str, keywords: &[String], color: &Color) -> String { } -pub fn gen_xs_errs(errs: &Vec, filename: &str, src: &str) { +pub fn gen_xs_errs(errs: &Vec, filename: &str, src: &str, ignores: &HashSet) { let kwds = Color::Fixed(5); let highlight = Color::Fixed(12); let names = Color::Fixed(13); let types = Color::Fixed(14); for error in errs.iter() { - let report = Report::build(error.kind(), filename, error.span().start) + if ignores.contains(&error.code()) { + continue; + } + let report = Report::build(error.report_kind(), filename, error.span().start) .with_code(error.code()) - .with_message(error.msg()); + .with_message(error.kind()); let report = match error { XSError::ExtraArg { fn_name, span } => { report.with_label( @@ -102,7 +106,7 @@ pub fn gen_xs_errs(errs: &Vec, filename: &str, src: &str) { ) } - XSError::Warning { span, msg, keywords } => { + XSError::Warning { span, msg, keywords, .. } => { report.with_label( Label::new((filename, span.start..span.end)) .with_message(msg_fmt(msg, keywords, &types)) diff --git a/src/lint/gen_info.rs b/src/lint/gen_info.rs index d6f4faa..f0d6e79 100644 --- a/src/lint/gen_info.rs +++ b/src/lint/gen_info.rs @@ -11,13 +11,14 @@ use crate::parsing::lexer::token::Token; use crate::parsing::parser::parser; use crate::r#static::type_check::{LocalEnv, TypeEnv}; use crate::r#static::type_check::statements::xs_tc; -use crate::r#static::xs_error::XSError; +use crate::r#static::xs_error::{XSError}; pub fn gen_info_from_path( type_env: &mut TypeEnv, local_envs: &mut LocalEnv, groups: &mut HashSet, - path: PathBuf + path: PathBuf, + ignores: &HashSet, ) { let src = match fs::read_to_string(&path) { Ok(src) => {src} @@ -31,10 +32,11 @@ pub fn gen_info_from_path( let errs = gen_info_from_src( type_env, local_envs, groups, - &path, &src + &path, &src, ignores ); - gen_xs_errs(&errs, filename, &src); + gen_xs_errs(&errs, filename, &src, ignores); + if errs.len() == 0 { println!("No errors found in file '{filename}'! Your code is free of the pitfalls of XS' quirks =)"); } @@ -46,7 +48,8 @@ pub fn gen_info_from_src( local_envs: &mut LocalEnv, groups: &mut HashSet, path: &PathBuf, - src: &str + src: &str, + ignores: &HashSet, ) -> Vec { let (tokens, errs) = lexer() .parse(src) @@ -73,7 +76,7 @@ pub fn gen_info_from_src( return tc_errs; }; - xs_tc(path, &ast, &mut None, type_env, local_envs, groups, &mut tc_errs); + xs_tc(path, &ast, &mut None, type_env, local_envs, groups, &mut tc_errs, ignores); tc_errs } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 5b1dc6c..4ee9d8c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,58 +1,27 @@ use std::collections::{HashMap, HashSet}; -use std::env; use std::path::PathBuf; +use crate::cli::parse_args; use crate::lint::gen_info::{gen_info_from_path, gen_info_from_src}; pub mod parsing; pub mod r#static; pub mod lint; - -include!(concat!(env!("OUT_DIR"), "/build_date.rs")); - -fn print_help() { - println!("Usage: xs-check path/to/script.xs"); - println!("lints the provided XS file with AoE2:DE's flavour of XS"); - println!(); - println!("Options:"); - println!(" -h, --help show this menu"); - println!(" -v, --version show binary version & info"); -} - -fn print_ver() { - let name = env!("CARGO_PKG_NAME"); - let version = env!("CARGO_PKG_VERSION"); - let authors = env!("CARGO_PKG_AUTHORS"); - - println!("{name} v{version}"); - println!("Author: {authors}"); - println!("Compiled: {BUILD_DATE}"); -} +pub mod cli; fn main() { - let filename = match env::args().nth(1) { - Some(arg) => { - match arg.as_str() { - "--help" | "-h" => { return print_help() } - "--version" | "-v" => { return print_ver() } - _ => { arg } - } - } - None => { return print_help() } + let (filepath, ignores) = match parse_args() { + Some(filepath) => { filepath } + None => { return; }, }; - let mut type_env= HashMap::new(); let mut local_envs = HashMap::new(); let mut groups = HashSet::new(); - let mut path = PathBuf::from(r"prelude.xs"); + let path = PathBuf::from(r"prelude.xs"); let prelude = include_str!(r"./prelude.xs"); - gen_info_from_src(&mut type_env, &mut local_envs, &mut groups, &path, prelude); - path.push(env::current_dir().unwrap()); - path.push(filename); - gen_info_from_path(&mut type_env, &mut local_envs, &mut groups, path); - // println!("tenv: {:?}", type_env); - // println!("lenvs: {:?}", local_envs); - // println!("grps: {:?}", groups); + gen_info_from_src(&mut type_env, &mut local_envs, &mut groups, &path, prelude, &ignores); + + gen_info_from_path(&mut type_env, &mut local_envs, &mut groups, filepath, &ignores); } diff --git a/src/prelude.xs b/src/prelude.xs index 5c6ba8c..74f5f95 100644 --- a/src/prelude.xs +++ b/src/prelude.xs @@ -1,5 +1,5 @@ // + --------------------------------- + -// | Generated on: 2024/09/09 23:00:28 | +// | Generated on: 2024/09/19 19:22:17 | // | Made by: Alian713 | // + --------------------------------- + @@ -1968,9 +1968,9 @@ int xsArrayCreateVector(int size = -1, vector defaultValue = vector(-1, -1, -1), * @param arrayID The ID of the array to get the value from * @param index The index to get the value of * -* @returns int +* @returns bool */ -int xsArrayGetBool(int arrayID = -1, int index = -1) {} +bool xsArrayGetBool(int arrayID = -1, int index = -1) {} /** * Gets and returns the value of the given float array at the specifed index. @@ -1978,9 +1978,9 @@ int xsArrayGetBool(int arrayID = -1, int index = -1) {} * @param arrayID The ID of the array to get the value from * @param index The index to get the value of * -* @returns int +* @returns float */ -int xsArrayGetFloat(int arrayID = -1, int index = -1) {} +float xsArrayGetFloat(int arrayID = -1, int index = -1) {} /** * Gets and returns the value of the given int array at the specifed index. @@ -2007,9 +2007,9 @@ int xsArrayGetSize(int arrayID = -1) {} * @param arrayID The ID of the array to get the value from * @param index The index to get the value of * -* @returns int +* @returns string */ -int xsArrayGetString(int arrayID = -1, int index = -1) {} +string xsArrayGetString(int arrayID = -1, int index = -1) {} /** * Gets and returns the value of the given Vector array at the specifed index. @@ -2017,9 +2017,9 @@ int xsArrayGetString(int arrayID = -1, int index = -1) {} * @param arrayID The ID of the array to get the value from * @param index The index to get the value of * -* @returns int +* @returns vector */ -int xsArrayGetVector(int arrayID = -1, int index = -1) {} +vector xsArrayGetVector(int arrayID = -1, int index = -1) {} /** * Resizes the the given bool array to the specifed size and returns 1. diff --git a/src/static/type_check/expression.rs b/src/static/type_check/expression.rs index 3cefddb..8c9ea4f 100644 --- a/src/static/type_check/expression.rs +++ b/src/static/type_check/expression.rs @@ -61,12 +61,13 @@ pub fn xs_tc_expr<'src>( }; type_cmp(param_type, arg_type, &arg_expr.1, errs, true, false); } - - for (_expr, span) in args[type_sign.len()-1..].iter() { - errs.push(XSError::extra_arg( - &name.0, - span, - )); + if args.len() > type_sign.len() { + for (_expr, span) in args[type_sign.len() - 1..].iter() { + errs.push(XSError::extra_arg( + &name.0, + span, + )); + } } type_sign.last() diff --git a/src/static/type_check/statement.rs b/src/static/type_check/statement.rs index 82579ff..71de5ae 100644 --- a/src/static/type_check/statement.rs +++ b/src/static/type_check/statement.rs @@ -1,4 +1,4 @@ -use std::collections::{HashMap}; +use std::collections::{HashMap, HashSet}; use std::path::PathBuf; use chumsky::container::Container; @@ -13,7 +13,7 @@ use crate::parsing::span::{Span, Spanned}; use crate::r#static::type_check::{env_get, env_set, Groups, LocalEnv, TypeEnv}; use crate::r#static::type_check::expression::xs_tc_expr; use crate::r#static::type_check::util::{chk_rule_opt, type_cmp}; -use crate::r#static::xs_error::{XSError}; +use crate::r#static::xs_error::{WarningKind, XSError}; pub fn xs_tc_stmt( path: &PathBuf, @@ -26,6 +26,7 @@ pub fn xs_tc_stmt( is_top_level: bool, is_breakable: bool, is_continuable: bool, + ignores: &HashSet, ) { match stmt { // an include statement is always parsed with a string literal ASTreeNode::Include((filename, _span)) => { @@ -45,6 +46,7 @@ pub fn xs_tc_stmt( local_envs, groups, inc_path, + ignores, ); } ASTreeNode::VarDef { @@ -98,6 +100,7 @@ pub fn xs_tc_stmt( expr_span, "Top level initialized {0} do not work correctly. yES", vec!["string"], + WarningKind::TopStrInit, )); } Expr::Literal(_) | Expr::Neg(_) | Expr::Vec { .. } => { } @@ -163,7 +166,6 @@ pub fn xs_tc_stmt( let mut opt_spans: HashMap<&str, &Span> = HashMap::with_capacity(rule_opts.len()); for (opt, opt_span) in rule_opts { - println!("{:?}", opt); match opt { RuleOpt::Active | RuleOpt::Inactive => { chk_rule_opt("activity", opt_span, &mut opt_spans, errs); @@ -212,7 +214,8 @@ pub fn xs_tc_stmt( for spanned_stmt in body.0.iter() { xs_tc_stmt( path, spanned_stmt, &mut local_type_env, type_env, local_envs, groups, errs, - false, is_breakable, is_continuable + false, is_breakable, is_continuable, + ignores, ); } local_envs @@ -328,7 +331,8 @@ pub fn xs_tc_stmt( for spanned_stmt in body.0.iter() { xs_tc_stmt( path, spanned_stmt, &mut local_type_env, type_env, local_envs, groups, errs, - false, is_breakable, is_continuable + false, is_breakable, is_continuable, + ignores ); } local_envs @@ -408,7 +412,8 @@ pub fn xs_tc_stmt( for spanned_stmt in consequent.0.0.iter() { xs_tc_stmt( path, spanned_stmt, local_env, type_env, local_envs, groups, errs, - false, is_breakable, is_continuable + false, is_breakable, is_continuable, + ignores ); } @@ -416,7 +421,8 @@ pub fn xs_tc_stmt( for spanned_stmt in alternate.0.0.iter() { xs_tc_stmt( path, spanned_stmt, local_env, type_env, local_envs, groups, errs, - false, is_breakable, is_continuable + false, is_breakable, is_continuable, + ignores ); } } @@ -444,7 +450,8 @@ pub fn xs_tc_stmt( for spanned_stmt in body.0.0.iter() { xs_tc_stmt( path, spanned_stmt, local_env, type_env, local_envs, groups, errs, - false, true, true + false, true, true, + ignores ); } }, @@ -492,7 +499,8 @@ pub fn xs_tc_stmt( for spanned_stmt in body.0.0.iter() { xs_tc_stmt( path, spanned_stmt, local_env, type_env, local_envs, groups, errs, - false, true, true + false, true, true, + ignores ); } }, @@ -518,7 +526,8 @@ pub fn xs_tc_stmt( for spanned_stmt in body.0.iter() { xs_tc_stmt( path, spanned_stmt, local_env, type_env, local_envs, groups, errs, - false, true, is_continuable + false, true, is_continuable, + ignores ); } let Some(spanned_case_expr) = case_clause else { @@ -529,12 +538,14 @@ pub fn xs_tc_stmt( errs.push(XSError::warning( og_span, "Only the first default block will run when case matching fails", - vec![] + vec![], + WarningKind::DupCase, )); errs.push(XSError::warning( body_span, "Only the first default block will run when case matching fails", - vec![] + vec![], + WarningKind::DupCase, )); continue; }; @@ -546,12 +557,14 @@ pub fn xs_tc_stmt( errs.push(XSError::warning( og_span, "Only the first case will run on a match", - vec![] + vec![], + WarningKind::DupCase, )); errs.push(XSError::warning( &spanned_case_expr.1, "Only the first case will run on a match", - vec![] + vec![], + WarningKind::DupCase, )); } else { case_spans.push((case_expr, case_expr_span)); @@ -698,6 +711,7 @@ pub fn xs_tc_stmt( expr_span, "The return value of this function call is being ignored", vec![], + WarningKind::DiscardedFn, )); }, ASTreeNode::Debug((id, id_span)) => { @@ -739,6 +753,7 @@ pub fn xs_tc_stmt( span, "Breakpoints cause XS execution to pause irrecoverably", vec![], + WarningKind::BreakPt, )); }, ASTreeNode::Class { name: (id, id_span), member_vars } => { @@ -816,6 +831,7 @@ pub fn xs_tc_stmt( span, "Classes are unusable in XS", vec![], + WarningKind::UnusableClasses, )); }, }} diff --git a/src/static/type_check/statements.rs b/src/static/type_check/statements.rs index 144b893..1f83680 100644 --- a/src/static/type_check/statements.rs +++ b/src/static/type_check/statements.rs @@ -1,3 +1,4 @@ +use std::collections::HashSet; use std::path::PathBuf; use crate::parsing::ast::astree::ASTreeNode; use crate::parsing::span::Spanned; @@ -13,11 +14,12 @@ pub fn xs_tc( local_envs: &mut LocalEnv, groups: &mut Groups, errs: &mut Vec, + ignores: &HashSet ) { for stmt in stmts { xs_tc_stmt( path, stmt, local_env, type_env, local_envs, groups, errs, - true, false, false + true, false, false, ignores ); } } diff --git a/src/static/type_check/util.rs b/src/static/type_check/util.rs index 8d395eb..d29be72 100644 --- a/src/static/type_check/util.rs +++ b/src/static/type_check/util.rs @@ -6,7 +6,7 @@ use crate::parsing::ast::type_::Type; use crate::parsing::span::{Span, Spanned}; use crate::r#static::type_check::expression::xs_tc_expr; use crate::r#static::type_check::TypeEnv; -use crate::r#static::xs_error::{XSError}; +use crate::r#static::xs_error::{WarningKind, XSError}; pub fn chk_int_lit(val: &i64, span: &Span) -> Vec { if *val < -999_999_999 || 999_999_999 < *val { @@ -84,7 +84,8 @@ pub fn arith_op<'src>( errs.push(XSError::warning( span, "This expression yields an {0}, not a {1}. The resulting type of an arithmetic operation depends on its first operand. yES", - vec!["int", "float"] + vec!["int", "float"], + WarningKind::FirstOprArith, )); Some(&Type::Int) } @@ -127,11 +128,12 @@ pub fn reln_op<'src>( (Type::Int | Type::Float, Type::Int | Type::Float) => { Some(&Type::Bool) } (Type::Str, Type::Str) => { Some(&Type::Bool) } (Type::Vec, Type::Vec) | (Type::Bool, Type::Bool) => { - if op_name != "eq" || op_name != "ne" { + if op_name != "eq" && op_name != "ne" { errs.push(XSError::warning( span, "This comparison will cause a silent XS crash", - vec![] + vec![], + WarningKind::CmpSilentCrash, )); } Some(&Type::Bool) @@ -196,7 +198,8 @@ pub fn type_cmp( errs.push(XSError::warning( actual_span, "Using booleans in a case's expression will cause a silent XS crash", - vec![] + vec![], + WarningKind::BoolCaseSilentCrash, )); } (Type::Int, Type::Bool) => {} // yES @@ -204,7 +207,8 @@ pub fn type_cmp( errs.push(XSError::warning( actual_span, "Possible loss of precision due to downcast from a {0} to an {1}", - vec!["float", "int"] + vec!["float", "int"], + WarningKind::NumDownCast, )); } (Type::Float, Type::Int | Type::Bool) => if is_fn_call { @@ -214,7 +218,8 @@ pub fn type_cmp( function call, floating point operations on this parameter will not work correctly. \ Consider explicitly assigning this expression to a temporary {3} variable \ before passing that as a parameter. yES", - vec!["int", "bool", "float", "float"] + vec!["int", "bool", "float", "float"], + WarningKind::NoNumPromo, )); } _ => { diff --git a/src/static/xs_error.rs b/src/static/xs_error.rs index 7949f3f..2bb7e5c 100644 --- a/src/static/xs_error.rs +++ b/src/static/xs_error.rs @@ -14,7 +14,21 @@ pub enum XSError { Syntax { span: Span, msg: String, keywords: Vec }, - Warning { span: Span, msg: String, keywords: Vec }, + Warning { span: Span, msg: String, keywords: Vec, kind: WarningKind }, +} + +#[derive(Debug, Clone)] +pub enum WarningKind { + TopStrInit = 7, + DupCase = 8, + DiscardedFn = 9, + BreakPt = 10, + UnusableClasses = 11, + FirstOprArith = 12, + CmpSilentCrash = 13, + BoolCaseSilentCrash = 14, + NumDownCast = 15, + NoNumPromo = 16, } impl XSError { @@ -76,24 +90,12 @@ impl XSError { } } - pub fn warning(span: &Span, msg: &str, keywords: Vec<&str>) -> XSError { + pub fn warning(span: &Span, msg: &str, keywords: Vec<&str>, kind: WarningKind) -> XSError { XSError::Warning { span: span.clone(), msg: String::from(msg), keywords: keywords.into_iter().map(String::from).collect(), - } - } - - pub fn code(&self) -> u32 { - match self { - XSError::ExtraArg { .. } => { 0 } - XSError::TypeMismatch { .. } => { 1 } - XSError::NotCallable { .. } => { 2 } - XSError::OpMismatch { .. } => { 3 } - XSError::UndefinedName { .. } => { 4 } - XSError::RedefinedName { .. } => { 5 } - XSError::Syntax { .. } => { 6 } - XSError::Warning { .. } => { 7 } + kind, } } @@ -110,14 +112,14 @@ impl XSError { } } - pub fn kind(&self) -> ReportKind { + pub fn report_kind(&self) -> ReportKind { match self { XSError::Warning { .. } => { ReportKind::Warning } _ => { ReportKind::Error } } } - pub fn msg(&self) -> &str { + pub fn kind(&self) -> &str { match self { XSError::ExtraArg { .. } => { "TypeError" } XSError::TypeMismatch { .. } => { "TypeError" } @@ -129,7 +131,57 @@ impl XSError { XSError::Syntax { .. } => { "SyntaxError" } - XSError::Warning { .. } => { "PotentialBug" } + XSError::Warning { kind: type_, .. } => { type_.as_str() } + } + } + + pub fn code(&self) -> u32 { + match self { + XSError::ExtraArg { .. } => { 0 } + XSError::TypeMismatch { .. } => { 1 } + XSError::NotCallable { .. } => { 2 } + XSError::OpMismatch { .. } => { 3 } + XSError::UndefinedName { .. } => { 4 } + XSError::RedefinedName { .. } => { 5 } + XSError::Syntax { .. } => { 6 } + XSError::Warning { kind, .. } => { kind.as_u32() } } } } + +impl WarningKind { + pub fn as_u32(&self) -> u32 { + self.clone() as u32 + } + + pub fn as_str(&self) -> &str { + match self { + WarningKind::TopStrInit => { "TopStrInit" } + WarningKind::DupCase => { "DupCase" } + WarningKind::DiscardedFn => { "DiscardedFn" } + WarningKind::BreakPt => { "BreakPt" } + WarningKind::UnusableClasses => { "UnusableClasses" } + WarningKind::FirstOprArith => { "FirstOprArith" } + WarningKind::CmpSilentCrash => { "CmpSilentCrash" } + WarningKind::BoolCaseSilentCrash => { "BoolCaseSilentCrash" } + WarningKind::NumDownCast => { "NumDownCast" } + WarningKind::NoNumPromo => { "NoNumPromo" } + } + } + + pub fn from_str(name: &str) -> Option { + match name { + "TopStrInit" => { Some(WarningKind::TopStrInit) } + "DupCase" => { Some(WarningKind::DupCase) } + "DiscardedFn" => { Some(WarningKind::DiscardedFn) } + "BreakPt" => { Some(WarningKind::BreakPt) } + "UnusableClasses" => { Some(WarningKind::UnusableClasses) } + "FirstOprArith" => { Some(WarningKind::FirstOprArith) } + "CmpSilentCrash" => { Some(WarningKind::CmpSilentCrash) } + "BoolCaseSilentCrash" => { Some(WarningKind::BoolCaseSilentCrash) } + "NumDownCast" => { Some(WarningKind::NumDownCast) } + "NoNumPromo" => { Some(WarningKind::NoNumPromo) } + _ => None + } + } +} \ No newline at end of file