diff --git a/Cargo.toml b/Cargo.toml index de40d148..c73f688c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,9 +18,6 @@ publish = true exclude = ["samples/*", "demo/*"] license = "BSD-2-Clause" -[lints.clippy] -# Using the URI type to identify open files is kind of unavoidable, tell clippy everything's gonna be ok -mutable_key_type = "allow" [lib] name = "asm_lsp" @@ -29,6 +26,59 @@ path = "src/lib.rs" name = "asm-lsp" path = "src/bin/main.rs" +[lints.clippy] +dbg_macro = "deny" +todo = "deny" +pedantic = { level = "warn", priority = -1 } +nursery = { level = "warn", priority = -1 } +cargo = { level = "warn", priority = -1 } + +# Taken from tree-sitter's `Cargo.toml` +# The lints below are a specific subset of the pedantic+nursery lints +# that we explicitly allow in the tree-sitter codebase because they either: +# +# 1. Contain false positives, +# 2. Are unnecessary, or +# 3. Worsen the code + +# Using the URI type to identify open files is kind of unavoidable, tell clippy everything's gonna be ok +mutable_key_type = "allow" + +branches_sharing_code = "allow" +cast_lossless = "allow" +cast_possible_truncation = "allow" +cast_possible_wrap = "allow" +cast_precision_loss = "allow" +cast_sign_loss = "allow" +checked_conversions = "allow" +cognitive_complexity = "allow" +collection_is_never_read = "allow" +fallible_impl_from = "allow" +fn_params_excessive_bools = "allow" +inline_always = "allow" +if_not_else = "allow" +items_after_statements = "allow" +match_wildcard_for_single_variants = "allow" +missing_errors_doc = "allow" +missing_panics_doc = "allow" +module_name_repetitions = "allow" +multiple_crate_versions = "allow" +option_if_let_else = "allow" +or_fun_call = "allow" +range_plus_one = "allow" +redundant_clone = "allow" +redundant_closure_for_method_calls = "allow" +ref_option = "allow" +similar_names = "allow" +string_lit_as_bytes = "allow" +struct_excessive_bools = "allow" +struct_field_names = "allow" +transmute_undefined_repr = "allow" +too_many_lines = "allow" +unnecessary_wraps = "allow" +unused_self = "allow" +used_underscore_items = "allow" + [dependencies] anyhow = "1.0.70" # write to stderr instead of stdout diff --git a/src/bin/main.rs b/src/bin/main.rs index fdf22424..53bc7d9c 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -33,7 +33,17 @@ use log::{error, info}; use lsp_server::{Connection, Message, Notification, Request, RequestId}; use lsp_textdocument::TextDocuments; -// main ------------------------------------------------------------------------------------------- +/// Entry point of the server. Connects to the client, loads documentation resources, +/// and then enters the main loop +/// +/// # Errors +/// +/// Returns `Err` if the server fails to connect to the lsp client +/// +/// # Panics +/// +/// Panics if JSON serialization of the server capabilities fails +#[allow(clippy::too_many_lines)] pub fn main() -> Result<()> { // initialisation ----------------------------------------------------------------------------- // Set up logging. Because `stdio_transport` gets a lock on stdout and stdin, we must have our @@ -393,7 +403,7 @@ pub fn main() -> Result<()> { Ok(()) } -#[allow(clippy::too_many_arguments)] +#[allow(clippy::too_many_arguments, clippy::too_many_lines)] fn main_loop( connection: &Connection, config: &Config, diff --git a/src/handle.rs b/src/handle.rs index 0597ba36..5955078e 100644 --- a/src/handle.rs +++ b/src/handle.rs @@ -310,7 +310,6 @@ pub fn handle_references_request( /// Produces diagnostics and sends a `PublishDiagnostics` notification to the client /// Diagnostics are only produced for the file specified by `uri` -/// Returns 'Err' if the response fails to send via `connection` /// /// # Errors /// diff --git a/src/lsp.rs b/src/lsp.rs index 49a9fc06..f7361b8c 100644 --- a/src/lsp.rs +++ b/src/lsp.rs @@ -323,9 +323,12 @@ fn get_compilation_db_files(path: &Path) -> Option { None } -/// Returns a default `CompileCommand` for the provided `uri`. If the user specified -/// a compiler in their config, it will be used. Otherwise, the command will be constructed -/// with a single flag consisting of the provided `uri` +/// Returns a default `CompileCommand` for the provided `uri`. +/// +/// - If the user specified a compiler in their config, it will be used. +/// - Otherwise, the command will be constructed with a single flag consisting of +/// the provided `uri` +/// /// NOTE: Several fields within the returned `CompileCommand` are intentionally left /// uninitialized to avoid unnecessary allocations. If you're using this function /// in a new place, please reconsider this assumption @@ -915,6 +918,7 @@ macro_rules! cursor_matches { }}; } +#[allow(clippy::too_many_lines)] pub fn get_comp_resp( curr_doc: &str, tree_entry: &mut TreeEntry, @@ -1108,7 +1112,7 @@ pub fn get_comp_resp( None } -fn lsp_pos_of_point(pos: tree_sitter::Point) -> lsp_types::Position { +const fn lsp_pos_of_point(pos: tree_sitter::Point) -> lsp_types::Position { Position { line: pos.row as u32, character: pos.column as u32, diff --git a/src/parser.rs b/src/parser.rs index 0f80ac27..f93e48b3 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -477,7 +477,7 @@ fn parse_arm_alias(xml_contents: &str) -> Result { for attr in e.attributes() { let Attribute { key, value } = attr.unwrap(); - if let Ok("title") = str::from_utf8(key.into_inner()) { + if Ok("title") == str::from_utf8(key.into_inner()) { alias.title = (unsafe { str::from_utf8_unchecked(&value) }).to_string(); } } @@ -501,20 +501,18 @@ fn parse_arm_alias(xml_contents: &str) -> Result { - if let QName(b"docvar") = e.name() { + if QName(b"docvar") == e.name() { let mut alias_next = false; for attr in e.attributes() { let Attribute { key, value } = attr.unwrap(); - if alias_next { - if let Ok("value") = str::from_utf8(key.into_inner()) { - aliased_instr = Some(str::from_utf8(&value)?.to_owned()); - break; - } + if alias_next && Ok("value") == str::from_utf8(key.into_inner()) { + aliased_instr = Some(str::from_utf8(&value)?.to_owned()); + break; } - if let Ok("key") = str::from_utf8(key.into_inner()) { - if let Ok("alias_mnemonic") = str::from_utf8(&value) { - alias_next = true; - } + if Ok("key") == str::from_utf8(key.into_inner()) + && Ok("alias_mnemonic") == str::from_utf8(&value) + { + alias_next = true; } } } @@ -584,7 +582,7 @@ fn parse_arm_instruction(xml_contents: &str) -> Result> { }, Ok(Event::Empty(ref e)) => { // e.g. - if let QName(b"docvar") = e.name() { + if QName(b"docvar") == e.name() { // There are multiple entries like this in each opcode file, but // *all* of them are the same within each file, so it doesn't matter which // one we use @@ -592,7 +590,7 @@ fn parse_arm_instruction(xml_contents: &str) -> Result> { let mut mnemonic_next = false; for attr in e.attributes() { let Attribute { key: _, value } = attr.unwrap(); - if let Ok("mnemonic") = str::from_utf8(&value) { + if Ok("mnemonic") == str::from_utf8(&value) { mnemonic_next = true; } else if mnemonic_next { let name = @@ -662,6 +660,7 @@ fn parse_arm_instruction(xml_contents: &str) -> Result> { /// /// This function is highly specialized to parse a handful of files and will panic or return /// `Err` for most mal-formed/unexpected inputs +#[allow(clippy::too_many_lines)] pub fn populate_instructions(xml_contents: &str) -> Result> { // initialise the instruction set let mut instructions_map = HashMap::::new(); @@ -683,7 +682,7 @@ pub fn populate_instructions(xml_contents: &str) -> Result> { QName(b"InstructionSet") => { for attr in e.attributes() { let Attribute { key, value } = attr.unwrap(); - if let Ok("name") = str::from_utf8(key.into_inner()) { + if Ok("name") == str::from_utf8(key.into_inner()) { arch = Arch::from_str(unsafe { str::from_utf8_unchecked(&value) }) .ok(); } else { @@ -1053,50 +1052,6 @@ pub fn populate_name_to_instruction_map<'instruction>( } } -#[cfg(test)] -mod tests { - use mockito::ServerOpts; - - use crate::parser::{get_cache_dir, populate_instructions}; - #[test] - fn test_populate_instructions() { - let mut server = mockito::Server::new_with_opts(ServerOpts { - port: 8080, - ..Default::default() - }); - - let _ = server - .mock("GET", "/x86/") - .with_status(200) - .with_header("content-type", "text/html") - .with_body(include_str!( - "../docs_store/instr_info_cache/x86_instr_docs.html" - )) - .create(); - - // Need to clear the cache file (if there is one) - // to ensure a request is made for each test call - let mut x86_cache_path = get_cache_dir().unwrap(); - x86_cache_path.push("x86_instr_docs.html"); - if x86_cache_path.is_file() { - std::fs::remove_file(&x86_cache_path).unwrap(); - } - let xml_conts_x86 = include_str!("../docs_store/opcodes/raw/x86.xml"); - assert!(populate_instructions(xml_conts_x86).is_ok()); - - if x86_cache_path.is_file() { - std::fs::remove_file(&x86_cache_path).unwrap(); - } - let xml_conts_x86_64 = include_str!("../docs_store/opcodes/raw/x86_64.xml"); - assert!(populate_instructions(xml_conts_x86_64).is_ok()); - - // Clean things up so we don't have an empty cache file - if x86_cache_path.is_file() { - std::fs::remove_file(&x86_cache_path).unwrap(); - } - } -} - /// Parse the provided XML contents and return a vector of all the registers based on that. /// If parsing fails, the appropriate error will be returned instead. /// @@ -1112,6 +1067,7 @@ mod tests { /// /// This function is highly specialized to parse a handful of files and will panic or return /// `Err` for most mal-formed/unexpected inputs +#[allow(clippy::too_many_lines)] pub fn populate_registers(xml_contents: &str) -> Result> { let mut registers_map = HashMap::::new(); @@ -1132,7 +1088,7 @@ pub fn populate_registers(xml_contents: &str) -> Result> { QName(b"InstructionSet") => { for attr in e.attributes() { let Attribute { key, value } = attr.unwrap(); - if let Ok("name") = str::from_utf8(key.into_inner()) { + if Ok("name") == str::from_utf8(key.into_inner()) { arch = Arch::from_str(unsafe { str::from_utf8_unchecked(&value) }) .ok(); } @@ -1184,7 +1140,7 @@ pub fn populate_registers(xml_contents: &str) -> Result> { } } } - QName(b"Flags") => {} // it's just a wrapper... + //QName(b"Flags") => {} // it's just a wrapper... // Actual flag bit info QName(b"Flag") => { curr_bit_flag = RegisterBitInfo::default(); @@ -1327,9 +1283,9 @@ pub fn populate_masm_nasm_directives(xml_contents: &str) -> Result { - if let QName(b"directive") = e.name() { + if QName(b"directive") == e.name() { directives_map.insert(curr_directive.name.clone(), curr_directive.clone()); - } else if let QName(b"description") = e.name() { + } else if QName(b"description") == e.name() { in_desc = false; } } @@ -1381,7 +1337,7 @@ pub fn populate_gas_directives(xml_contents: &str) -> Result> { QName(b"Assembler") => { for attr in e.attributes() { let Attribute { key, value } = attr.unwrap(); - if let Ok("name") = str::from_utf8(key.into_inner()) { + if Ok("name") == str::from_utf8(key.into_inner()) { assembler = Assembler::from_str(unsafe { str::from_utf8_unchecked(&value) }) @@ -1426,11 +1382,11 @@ pub fn populate_gas_directives(xml_contents: &str) -> Result> { } } } - QName(b"Signatures") => {} // it's just a wrapper... + //QName(b"Signatures") => {} // it's just a wrapper... QName(b"Signature") => { for attr in e.attributes() { let Attribute { key, value } = attr.unwrap(); - if let Ok("sig") = str::from_utf8(key.into_inner()) { + if Ok("sig") == str::from_utf8(key.into_inner()) { let sig = String::from(unsafe { str::from_utf8_unchecked(&value) }); curr_directive .signatures @@ -1443,7 +1399,7 @@ pub fn populate_gas_directives(xml_contents: &str) -> Result> { } // end event -------------------------------------------------------------------------- Ok(Event::End(ref e)) => { - if let QName(b"Directive") = e.name() { + if QName(b"Directive") == e.name() { // finish directive directives_map.insert(curr_directive.name.clone(), curr_directive.clone()); } @@ -1546,9 +1502,11 @@ fn get_docs_body(x86_online_docs: &str) -> Option { Some(body) } -/// Searches for the asm-lsp cache directory. First checks for the `ASM_LSP_CACHE_DIR` -/// environment variable. If this variable is present and points to a valid directory, -/// this path is returned. Otherwise, the function returns `~/.config/asm-lsp/` +/// Searches for the asm-lsp cache directory. +/// +/// - First checks for the `ASM_LSP_CACHE_DIR` environment variable. If this variable +/// is present and points to a valid directory, this path is returned. +/// - Otherwise, the function returns `~/.config/asm-lsp/` /// /// # Errors /// @@ -1626,3 +1584,47 @@ fn set_x86_docs_cache(contents: &str, x86_cache_path: &PathBuf) { } } } + +#[cfg(test)] +mod tests { + use mockito::ServerOpts; + + use crate::parser::{get_cache_dir, populate_instructions}; + #[test] + fn test_populate_instructions() { + let mut server = mockito::Server::new_with_opts(ServerOpts { + port: 8080, + ..Default::default() + }); + + let _ = server + .mock("GET", "/x86/") + .with_status(200) + .with_header("content-type", "text/html") + .with_body(include_str!( + "../docs_store/instr_info_cache/x86_instr_docs.html" + )) + .create(); + + // Need to clear the cache file (if there is one) + // to ensure a request is made for each test call + let mut x86_cache_path = get_cache_dir().unwrap(); + x86_cache_path.push("x86_instr_docs.html"); + if x86_cache_path.is_file() { + std::fs::remove_file(&x86_cache_path).unwrap(); + } + let xml_conts_x86 = include_str!("../docs_store/opcodes/raw/x86.xml"); + assert!(populate_instructions(xml_conts_x86).is_ok()); + + if x86_cache_path.is_file() { + std::fs::remove_file(&x86_cache_path).unwrap(); + } + let xml_conts_x86_64 = include_str!("../docs_store/opcodes/raw/x86_64.xml"); + assert!(populate_instructions(xml_conts_x86_64).is_ok()); + + // Clean things up so we don't have an empty cache file + if x86_cache_path.is_file() { + std::fs::remove_file(&x86_cache_path).unwrap(); + } + } +} diff --git a/src/test.rs b/src/test.rs index d1f2a7b8..033a1fe1 100644 --- a/src/test.rs +++ b/src/test.rs @@ -252,7 +252,7 @@ mod tests { } impl GlobalInfo { - fn new() -> Self { + const fn new() -> Self { Self { x86_instructions: Vec::new(), x86_64_instructions: Vec::new(), @@ -399,10 +399,10 @@ mod tests { Vec::new() }; - return Ok(info); + Ok(info) } - fn init_test_store<'a>(info: &'a GlobalInfo) -> Result> { + fn init_test_store(info: &GlobalInfo) -> GlobalVars<'_> { let mut store = GlobalVars::new(); let mut x86_cache_path = get_cache_dir().unwrap(); @@ -504,12 +504,12 @@ mod tests { Some(CompletionItemKind::OPERATOR), ); - return Ok(store); + store } fn test_hover(source: &str, expected: &str, config: &Config) { let info = init_global_info(config).expect("Failed to load info"); - let globals = init_test_store(&info).expect("Failed to initialize test store"); + let globals = init_test_store(&info); let mut position: Option = None; for (line_num, line) in source.lines().enumerate() { @@ -572,7 +572,7 @@ mod tests { let resp = get_hover_resp( &hover_params, config, - &word, + word, &text_store, &mut tree_store, &globals.names_to_instructions, @@ -602,7 +602,7 @@ mod tests { trigger_character: Option, ) { let info = init_global_info(config).expect("Failed to load info"); - let globals = init_test_store(&info).expect("Failed to initialize test store"); + let globals = init_test_store(&info); let source_code = source.replace("", ""); @@ -814,13 +814,13 @@ Type: General Purpose Register", #[test] fn handle_autocomplete_it_provides_label_comps_as_instruction_arg() { test_label_autocomplete( - r#" + r" foo: mov eax, 0 bar: call f - "#, + ", CompletionTriggerKind::INVOKED, None, ); @@ -834,27 +834,27 @@ bar: "#, r#"`.string "(a & 0x0F): "`"#, &gas_test_config(), - ) + ); } #[test] fn handle_hover_gas_it_provides_label_data_2() { test_hover( - r#"data_items: + r"data_items: .long 1, 2, 3 - "#, - r#"`.long 1, 2, 3`"#, + ", + r"`.long 1, 2, 3`", &gas_test_config(), - ) + ); } #[test] fn handle_hover_gas_it_provides_label_data_3() { test_hover( - r#"data_items: + r"data_items: .float 1.1, 2.2, 3.3 - "#, - r#"`.float 1.1, 2.2, 3.3`"#, + ", + r"`.float 1.1, 2.2, 3.3`", &gas_test_config(), - ) + ); } // Demangling @@ -1259,7 +1259,7 @@ More info: https://sourceware.org/binutils/docs-2.41/as/Global.html", #[test] fn handle_autocomplete_masm_it_provides_directive_completes_1() { test_directive_autocomplete( - r#" ADD"#, + r" ADD", &masm_test_config(), CompletionTriggerKind::INVOKED, None, @@ -1318,7 +1318,7 @@ MASM64: Generates a UWOP_ALLOC_SMALL or a UWOP_ALLOC_LARGE with the specified si #[test] fn handle_autocomplete_nasm_it_provides_directive_completes_1() { test_directive_autocomplete( - r#" EQ"#, + r" EQ", &nasm_test_config(), CompletionTriggerKind::INVOKED, None, @@ -1973,7 +1973,7 @@ Width: 8 bits", let mut raw_vec = populate_registers(x86_regs_raw).unwrap(); // HACK: Windows line endings... - for reg in raw_vec.iter_mut() { + for reg in &mut raw_vec { if let Some(descr) = ®.description { reg.description = Some(descr.replace('\r', "")); } @@ -1984,18 +1984,19 @@ Width: 8 bits", } for reg in raw_vec { let entry = cmp_map.get_mut(®).unwrap(); - if *entry == 0 { - panic!( - "Expected at least one more instruction entry for {:?}, but the count is 0", - reg - ); - } + assert!( + *entry != 0, + "Expected at least one more instruction entry for {:?}, but the count is 0", + reg + ); *entry -= 1; } - for (reg, count) in cmp_map.iter() { - if *count != 0 { - panic!("Expected count to be 0, found {count} for {:?}", reg); - } + for (reg, count) in &cmp_map { + assert!( + *count == 0, + "Expected count to be 0, found {count} for {:?}", + reg + ); } } #[test] @@ -2008,7 +2009,7 @@ Width: 8 bits", let mut raw_vec = populate_registers(x86_64_regs_raw).unwrap(); // HACK: Windows line endings... - for reg in raw_vec.iter_mut() { + for reg in &mut raw_vec { if let Some(descr) = ®.description { reg.description = Some(descr.replace('\r', "")); } @@ -2019,18 +2020,19 @@ Width: 8 bits", } for reg in raw_vec { let entry = cmp_map.get_mut(®).unwrap(); - if *entry == 0 { - panic!( - "Expected at least one more instruction entry for {:?}, but the count is 0", - reg - ); - } + assert!( + *entry != 0, + "Expected at least one more instruction entry for {:?}, but the count is 0", + reg + ); *entry -= 1; } - for (reg, count) in cmp_map.iter() { - if *count != 0 { - panic!("Expected count to be 0, found {count} for {:?}", reg); - } + for (reg, count) in &cmp_map { + assert!( + *count == 0, + "Expected count to be 0, found {count} for {:?}", + reg + ); } } #[test] @@ -2043,7 +2045,7 @@ Width: 8 bits", let mut raw_vec = populate_registers(arm_regs_raw).unwrap(); // HACK: Windows line endings... - for reg in raw_vec.iter_mut() { + for reg in &mut raw_vec { if let Some(descr) = ®.description { reg.description = Some(descr.replace('\r', "")); } @@ -2054,18 +2056,19 @@ Width: 8 bits", } for reg in raw_vec { let entry = cmp_map.get_mut(®).unwrap(); - if *entry == 0 { - panic!( - "Expected at least one more instruction entry for {:?}, but the count is 0", - reg - ); - } + assert!( + *entry != 0, + "Expected at least one more instruction entry for {:?}, but the count is 0", + reg + ); *entry -= 1; } - for (reg, count) in cmp_map.iter() { - if *count != 0 { - panic!("Expected count to be 0, found {count} for {:?}", reg); - } + for (reg, count) in &cmp_map { + assert!( + *count == 0, + "Expected count to be 0, found {count} for {:?}", + reg + ); } } #[test] @@ -2082,18 +2085,19 @@ Width: 8 bits", } for reg in raw_vec { let entry = cmp_map.get_mut(®).unwrap(); - if *entry == 0 { - panic!( - "Expected at least one more instruction entry for {:?}, but the count is 0", - reg - ); - } + assert!( + *entry != 0, + "Expected at least one more instruction entry for {:?}, but the count is 0", + reg + ); *entry -= 1; } - for (reg, count) in cmp_map.iter() { - if *count != 0 { - panic!("Expected count to be 0, found {count} for {:?}", reg); - } + for (reg, count) in &cmp_map { + assert!( + *count == 0, + "Expected count to be 0, found {count} for {:?}", + reg + ); } } #[test] @@ -2107,10 +2111,10 @@ Width: 8 bits", // HACK: To work around the difference in extra info urls between testing // and production - for instr in ser_vec.iter_mut() { + for instr in &mut ser_vec { instr.url = None; } - for instr in raw_vec.iter_mut() { + for instr in &mut raw_vec { instr.url = None; } @@ -2119,18 +2123,19 @@ Width: 8 bits", } for instr in raw_vec { let entry = cmp_map.get_mut(&instr).unwrap(); - if *entry == 0 { - panic!( - "Expected at least one more instruction entry for {:?}, but the count is 0", - instr - ); - } + assert!( + *entry != 0, + "Expected at least one more instruction entry for {:?}, but the count is 0", + instr + ); *entry -= 1; } - for (instr, count) in cmp_map.iter() { - if *count != 0 { - panic!("Expected count to be 0, found {count} for {:?}", instr); - } + for (instr, count) in &cmp_map { + assert!( + *count == 0, + "Expected count to be 0, found {count} for {:?}", + instr + ); } } #[test] @@ -2144,10 +2149,10 @@ Width: 8 bits", // HACK: To work around the difference in extra info urls between testing // and production - for instr in ser_vec.iter_mut() { + for instr in &mut ser_vec { instr.url = None; } - for instr in raw_vec.iter_mut() { + for instr in &mut raw_vec { instr.url = None; } @@ -2156,18 +2161,19 @@ Width: 8 bits", } for instr in raw_vec { let entry = cmp_map.get_mut(&instr).unwrap(); - if *entry == 0 { - panic!( - "Expected at least one more instruction entry for {:?}, but the count is 0", - instr - ); - } + assert!( + *entry != 0, + "Expected at least one more instruction entry for {:?}, but the count is 0", + instr + ); *entry -= 1; } - for (instr, count) in cmp_map.iter() { - if *count != 0 { - panic!("Expected count to be 0, found {count} for {:?}", instr); - } + for (instr, count) in &cmp_map { + assert!( + *count == 0, + "Expected count to be 0, found {count} for {:?}", + instr + ); } } #[test] @@ -2186,18 +2192,19 @@ Width: 8 bits", } for instr in raw_vec { let entry = cmp_map.get_mut(&instr).unwrap(); - if *entry == 0 { - panic!( - "Expected at least one more instruction entry for {:?}, but the count is 0", - instr - ); - } + assert!( + *entry != 0, + "Expected at least one more instruction entry for {:?}, but the count is 0", + instr + ); *entry -= 1; } - for (instr, count) in cmp_map.iter() { - if *count != 0 { - panic!("Expected count to be 0, found {count} for {:?}", instr); - } + for (instr, count) in &cmp_map { + assert!( + *count == 0, + "Expected count to be 0, found {count} for {:?}", + instr + ); } } #[test] @@ -2214,18 +2221,19 @@ Width: 8 bits", } for instr in raw_vec { let entry = cmp_map.get_mut(&instr).unwrap(); - if *entry == 0 { - panic!( - "Expected at least one more instruction entry for {:?}, but the count is 0", - instr - ); - } + assert!( + *entry != 0, + "Expected at least one more instruction entry for {:?}, but the count is 0", + instr + ); *entry -= 1; } - for (instr, count) in cmp_map.iter() { - if *count != 0 { - panic!("Expected count to be 0, found {count} for {:?}", instr); - } + for (instr, count) in &cmp_map { + assert!( + *count == 0, + "Expected count to be 0, found {count} for {:?}", + instr + ); } } #[test] @@ -2242,18 +2250,19 @@ Width: 8 bits", } for dir in raw_vec { let entry = cmp_map.get_mut(&dir).unwrap(); - if *entry == 0 { - panic!( - "Expected at least one more instruction entry for {:?}, but the count is 0", - dir - ); - } + assert!( + *entry != 0, + "Expected at least one more instruction entry for {:?}, but the count is 0", + dir + ); *entry -= 1; } - for (dir, count) in cmp_map.iter() { - if *count != 0 { - panic!("Expected count to be 0, found {count} for {:?}", dir); - } + for (dir, count) in &cmp_map { + assert!( + *count == 0, + "Expected count to be 0, found {count} for {:?}", + dir + ); } } #[test] @@ -2270,18 +2279,19 @@ Width: 8 bits", } for dir in raw_vec { let entry = cmp_map.get_mut(&dir).unwrap(); - if *entry == 0 { - panic!( - "Expected at least one more instruction entry for {:?}, but the count is 0", - dir - ); - } + assert!( + *entry != 0, + "Expected at least one more instruction entry for {:?}, but the count is 0", + dir + ); *entry -= 1; } - for (dir, count) in cmp_map.iter() { - if *count != 0 { - panic!("Expected count to be 0, found {count} for {:?}", dir); - } + for (dir, count) in &cmp_map { + assert!( + *count == 0, + "Expected count to be 0, found {count} for {:?}", + dir + ); } } #[test] @@ -2298,18 +2308,19 @@ Width: 8 bits", } for dir in raw_vec { let entry = cmp_map.get_mut(&dir).unwrap(); - if *entry == 0 { - panic!( - "Expected at least one more instruction entry for {:?}, but the count is 0", - dir - ); - } + assert!( + *entry != 0, + "Expected at least one more instruction entry for {:?}, but the count is 0", + dir + ); *entry -= 1; } - for (dir, count) in cmp_map.iter() { - if *count != 0 { - panic!("Expected count to be 0, found {count} for {:?}", dir); - } + for (dir, count) in &cmp_map { + assert!( + *count == 0, + "Expected count to be 0, found {count} for {:?}", + dir + ); } } } diff --git a/src/types.rs b/src/types.rs index e6f59fc5..4b5debb0 100644 --- a/src/types.rs +++ b/src/types.rs @@ -99,12 +99,9 @@ impl std::fmt::Display for Instruction { // url let more_info: String; - match &self.url { - Some(url) => { - more_info = format!("\nMore info: {url}"); - v.push(&more_info); - } - None => {} + if let Some(url) = &self.url { + more_info = format!("\nMore info: {url}"); + v.push(&more_info); } let s = v.join("\n"); @@ -307,10 +304,10 @@ impl FromStr for Z80TimingValue { type Err = std::num::ParseIntError; fn from_str(s: &str) -> Result { if s.eq("?") { - Ok(Z80TimingValue::Unknown) + Ok(Self::Unknown) } else { match s.parse::() { - Ok(val) => Ok(Z80TimingValue::Val(val)), + Ok(val) => Ok(Self::Val(val)), Err(e) => Err(e), } } @@ -333,13 +330,13 @@ impl Default for Z80TimingInfo { impl Display for Z80TimingInfo { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Z80TimingInfo::OneNum(num) => { + Self::OneNum(num) => { write!(f, "{num}")?; } - Z80TimingInfo::TwoNum((num1, num2)) => { + Self::TwoNum((num1, num2)) => { write!(f, "{num1}/{num2}")?; } - Z80TimingInfo::ThreeNum((num1, num2, num3)) => { + Self::ThreeNum((num1, num2, num3)) => { write!(f, "{num1}/{num2}/{num3}")?; } } @@ -350,27 +347,24 @@ impl Display for Z80TimingInfo { impl FromStr for Z80TimingInfo { type Err = String; fn from_str(s: &str) -> Result { - if let Some('/') = s.chars().next() { - return Err(String::from("Cannot have an empty num value")); - } else if let Some('/') = s.chars().next_back() { + if s.starts_with('/') || s.ends_with('/') { return Err(String::from("Cannot have an empty num value")); } + let pieces: Vec> = s.split('/').map(str::parse).collect(); match pieces.len() { 1 => match pieces[0] { - Ok(num) => Ok(Z80TimingInfo::OneNum(num)), + Ok(num) => Ok(Self::OneNum(num)), Err(_) => Err(String::from("Failed to parse one timing value")), }, 2 => match (&pieces[0], &pieces[1]) { - (Ok(num1), Ok(num2)) => Ok(Z80TimingInfo::TwoNum((*num1, *num2))), + (Ok(num1), Ok(num2)) => Ok(Self::TwoNum((*num1, *num2))), _ => Err(String::from("Failed to parse one or more timing values")), }, 3 => match (&pieces[0], &pieces[1], &pieces[2]) { - (Ok(num1), Ok(num2), Ok(num3)) => { - Ok(Z80TimingInfo::ThreeNum((*num1, *num2, *num3))) - } + (Ok(num1), Ok(num2), Ok(num3)) => Ok(Self::ThreeNum((*num1, *num2, *num3))), _ => Err(String::from("Failed to parse one or more timing values")), }, n => Err(format!("Expected 1-3 timing values, got {n}")), @@ -819,7 +813,7 @@ pub struct Assemblers { impl Default for Assemblers { fn default() -> Self { - Assemblers { + Self { gas: Some(true), go: Some(true), masm: Some(false), @@ -841,7 +835,7 @@ pub struct InstructionSets { impl Default for InstructionSets { fn default() -> Self { - InstructionSets { + Self { x86: Some(true), x86_64: Some(true), z80: Some(false), @@ -878,7 +872,7 @@ pub struct Config { impl Default for Config { fn default() -> Self { - Config { + Self { version: String::from("0.1"), assemblers: Assemblers::default(), instruction_sets: InstructionSets::default(),