diff --git a/src/archive/bsa.rs b/src/archive/bsa.rs index 60d328ee..dd4cea36 100644 --- a/src/archive/bsa.rs +++ b/src/archive/bsa.rs @@ -73,7 +73,11 @@ mod v103 { pub(super) const FOLDER_RECORD_SIZE: usize = 16; pub(super) fn read_folder_record(value: &[u8]) -> FolderRecord { - assert!(value.len() >= FOLDER_RECORD_SIZE); + assert!( + value.len() >= FOLDER_RECORD_SIZE, + "Folder record byte slice is too small, expected {FOLDER_RECORD_SIZE} bytes, got {}", + value.len() + ); FolderRecord { name_hash: to_u64(value), @@ -91,7 +95,11 @@ mod v105 { pub(super) const FOLDER_RECORD_SIZE: usize = 24; pub(super) fn read_folder_record(value: &[u8]) -> FolderRecord { - assert!(value.len() >= FOLDER_RECORD_SIZE); + assert!( + value.len() >= FOLDER_RECORD_SIZE, + "Folder record byte slice is too small, expected {FOLDER_RECORD_SIZE} bytes, got {}", + value.len() + ); FolderRecord { name_hash: to_u64(value), diff --git a/src/archive/parse.rs b/src/archive/parse.rs index c89f0bd1..c28cbc0f 100644 --- a/src/archive/parse.rs +++ b/src/archive/parse.rs @@ -147,10 +147,10 @@ mod tests { assert_eq!(1, assets.len()); assert_eq!(1, files_count); assert_eq!(expected_key, *assets.first_key_value().unwrap().0); - assert_eq!(1, assets.get(&expected_key).unwrap().len()); + assert_eq!(1, assets[&expected_key].len()); assert_eq!( 0x4670_B683_6C07_7365, - *assets.get(&expected_key).unwrap().first().unwrap() + *assets[&expected_key].first().unwrap() ); } @@ -165,10 +165,10 @@ mod tests { assert_eq!(1, assets.len()); assert_eq!(1, files_count); assert_eq!(expected_key, *assets.first_key_value().unwrap().0); - assert_eq!(1, assets.get(&expected_key).unwrap().len()); + assert_eq!(1, assets[&expected_key].len()); assert_eq!( 0x4670_B683_6C07_7365, - *assets.get(&expected_key).unwrap().first().unwrap() + *assets[&expected_key].first().unwrap() ); } @@ -183,10 +183,10 @@ mod tests { assert_eq!(1, assets.len()); assert_eq!(1, files_count); assert_eq!(expected_key, *assets.first_key_value().unwrap().0); - assert_eq!(1, assets.get(&expected_key).unwrap().len()); + assert_eq!(1, assets[&expected_key].len()); assert_eq!( 0x4670_B683_6C07_7365, - *assets.get(&expected_key).unwrap().first().unwrap() + *assets[&expected_key].first().unwrap() ); } @@ -286,15 +286,15 @@ mod tests { assert_eq!(3, assets.len()); assert_eq!(3, files_count); - let value = assets.get(&0).unwrap(); + let value = &assets[&0]; assert_eq!(1, value.len()); assert_eq!(0x4670_B683_6C07_7365, *value.first().unwrap()); - let value = assets.get(&0x2E01_002E).unwrap(); + let value = &assets[&0x2E01_002E]; assert_eq!(1, value.len()); assert_eq!(0x4670_B683_6C07_7365, *value.first().unwrap()); - let value = assets.get(&0xB681_02C9_6417_6E73).unwrap(); + let value = &assets[&0xB681_02C9_6417_6E73]; assert_eq!(1, value.len()); assert_eq!(0x4670_B683_6C07_7365, *value.first().unwrap()); } diff --git a/src/database/mod.rs b/src/database/mod.rs index 25f514cb..f71e4890 100644 --- a/src/database/mod.rs +++ b/src/database/mod.rs @@ -119,6 +119,10 @@ impl Database { let Ok(mut minimal_plugin) = PluginMetadata::new(plugin.name()) else { // This should never happen because the regex plugin name from // an existing PluginMetadata object should be valid. + logging::error!( + "Unexpectedly encountered a regex error trying to create a PluginMetadata object with the name {}", + plugin.name() + ); continue; }; minimal_plugin.set_tags(plugin.tags().to_vec()); diff --git a/src/game.rs b/src/game.rs index 0879e4e1..8a34330e 100644 --- a/src/game.rs +++ b/src/game.rs @@ -1707,8 +1707,8 @@ mod tests { .join("sub2") .join("archive") .with_extension(extension); - std::fs::create_dir(path1.parent().unwrap()).unwrap(); - std::fs::create_dir(path2.parent().unwrap()).unwrap(); + std::fs::create_dir_all(path1.parent().unwrap()).unwrap(); + std::fs::create_dir_all(path2.parent().unwrap()).unwrap(); std::fs::File::create(&path1).unwrap(); std::fs::File::create(&path2).unwrap(); diff --git a/src/lib.rs b/src/lib.rs index 9fcd0fa7..ef3e790b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,6 +19,93 @@ #![allow(clippy::doc_markdown)] #![allow(clippy::must_use_candidate)] #![allow(clippy::missing_errors_doc)] +// Selectively deny clippy restriction lints. +#![deny( + clippy::allow_attributes, + clippy::as_conversions, + clippy::as_underscore, + clippy::assertions_on_result_states, + clippy::big_endian_bytes, + clippy::cfg_not_test, + clippy::clone_on_ref_ptr, + clippy::create_dir, + clippy::dbg_macro, + clippy::decimal_literal_representation, + clippy::default_numeric_fallback, + clippy::doc_include_without_cfg, + clippy::empty_drop, + clippy::error_impl_error, + clippy::exit, + // clippy::exhaustive_enums, + // clippy::expect_used, + // clippy::filetype_is_file, + clippy::float_cmp_const, + clippy::fn_to_numeric_cast_any, + clippy::get_unwrap, + clippy::host_endian_bytes, + clippy::if_then_some_else_none, + // clippy::indexing_slicing, + clippy::infinite_loop, + clippy::integer_division, + clippy::integer_division_remainder_used, + clippy::iter_over_hash_type, + clippy::let_underscore_must_use, + clippy::lossy_float_literal, + clippy::map_err_ignore, + clippy::map_with_unused_argument_over_ranges, + clippy::mem_forget, + clippy::missing_assert_message, + clippy::missing_asserts_for_indexing, + clippy::missing_asserts_for_indexing, + clippy::mixed_read_write_in_expression, + clippy::multiple_inherent_impl, + clippy::multiple_unsafe_ops_per_block, + clippy::mutex_atomic, + clippy::mutex_integer, + clippy::needless_raw_strings, + clippy::non_ascii_literal, + clippy::non_zero_suggestions, + clippy::panic, + clippy::panic_in_result_fn, + clippy::partial_pub_fields, + clippy::pathbuf_init_then_push, + clippy::precedence_bits, + clippy::print_stderr, + clippy::print_stdout, + clippy::rc_buffer, + clippy::rc_mutex, + clippy::redundant_type_annotations, + clippy::ref_patterns, + clippy::rest_pat_in_fully_bound_structs, + // clippy::str_to_string, + clippy::string_lit_chars_any, + // clippy::string_slice, + clippy::string_to_string, + clippy::suspicious_xor_used_as_pow, + clippy::tests_outside_test_module, + clippy::todo, + clippy::try_err, + clippy::undocumented_unsafe_blocks, + clippy::unimplemented, + clippy::unnecessary_safety_comment, + clippy::unneeded_field_pattern, + clippy::unreachable, + clippy::unused_result_ok, + // clippy::unwrap_in_result, + clippy::unwrap_used, + // clippy::use_debug, + clippy::verbose_file_reads, + // clippy::wildcard_enum_match_arm, +)] +#![cfg_attr( + test, + allow( + clippy::assertions_on_result_states, + clippy::missing_asserts_for_indexing, + clippy::panic, + clippy::unwrap_used, + ) +)] mod archive; mod database; diff --git a/src/logging.rs b/src/logging.rs index 86639da8..86209eee 100644 --- a/src/logging.rs +++ b/src/logging.rs @@ -213,7 +213,7 @@ mod tests { let _lock = TEST_LOCK.lock().unwrap(); let messages = Arc::new(Mutex::new(Vec::new())); - let cloned_messages = messages.clone(); + let cloned_messages = Arc::clone(&messages); let callback = move |level, message: &str| { if let Ok(mut messages) = cloned_messages.lock() { messages.push((level, message.to_string())); @@ -247,7 +247,7 @@ mod tests { set_logging_callback(callback); let messages = Arc::new(Mutex::new(Vec::new())); - let cloned_messages = messages.clone(); + let cloned_messages = Arc::clone(&messages); let callback = move |level, message: &str| { if let Ok(mut messages) = cloned_messages.lock() { messages.push((level, message.to_string())); diff --git a/src/metadata/metadata_document.rs b/src/metadata/metadata_document.rs index c418a6f4..dc0a0968 100644 --- a/src/metadata/metadata_document.rs +++ b/src/metadata/metadata_document.rs @@ -456,7 +456,7 @@ plugins: #[test] fn load_from_str_should_resolve_aliases() { - let yaml = r" + let yaml = " prelude: - &anchor type: say @@ -489,7 +489,7 @@ plugins: #[test] fn load_from_str_should_error_if_a_plugin_has_two_exact_entries() { - let yaml = r" + let yaml = " plugins: - name: 'Blank.esm' msg: diff --git a/src/metadata/plugin_metadata.rs b/src/metadata/plugin_metadata.rs index 189152c7..9b5f8b3f 100644 --- a/src/metadata/plugin_metadata.rs +++ b/src/metadata/plugin_metadata.rs @@ -337,7 +337,7 @@ pub(crate) fn iends_with_ascii(string: &str, suffix: &str) -> bool { } fn is_regex_name(name: &str) -> bool { - name.contains(|c| ":\\*?|".chars().any(|n| c == n)) + name.contains([':', '\\', '*', '?', '|']) } fn merge_slices(target: &mut Box<[T]>, source: &[T]) { diff --git a/src/metadata/yaml/parse.rs b/src/metadata/yaml/parse.rs index 1605549c..787e2386 100644 --- a/src/metadata/yaml/parse.rs +++ b/src/metadata/yaml/parse.rs @@ -144,7 +144,7 @@ pub fn get_u32_value( ) -> Result, ParseMetadataError> { match mapping.get(&as_string_node(key)) { Some(n) => match n.data.as_integer() { - Some(i) => i.try_into().map(Some).map_err(|_| { + Some(i) => i.try_into().map(Some).map_err(|_e| { ParseMetadataError::new(n.span.start, MetadataParsingErrorReason::NonU32Number(i)) }), None => Err(ParseMetadataError::unexpected_value_type( diff --git a/src/sorting/dfs.rs b/src/sorting/dfs.rs index dee6798e..0484756c 100644 --- a/src/sorting/dfs.rs +++ b/src/sorting/dfs.rs @@ -179,11 +179,7 @@ impl<'a, N, F: FnMut(&N) -> String> CycleDetector<'a, N, F> { } fn into_cycle_path(self) -> Option> { - if self.found_cycle { - Some(self.trail) - } else { - None - } + self.found_cycle.then_some(self.trail) } } diff --git a/src/sorting/groups.rs b/src/sorting/groups.rs index 07cff385..dc380545 100644 --- a/src/sorting/groups.rs +++ b/src/sorting/groups.rs @@ -130,7 +130,7 @@ pub fn find_path( let to_vertex = find_node_by_weight(graph, to_group_name)?; let paths = - bellman_ford(&float_graph, from_vertex).map_err(|_| PathfindingError::NegativeCycle)?; + bellman_ford(&float_graph, from_vertex).map_err(|_e| PathfindingError::NegativeCycle)?; let mut path = vec![Vertex::new(graph[to_vertex].clone().into_string())]; let mut current = to_vertex; diff --git a/src/sorting/mod.rs b/src/sorting/mod.rs index 7c282860..9340a31f 100644 --- a/src/sorting/mod.rs +++ b/src/sorting/mod.rs @@ -14,10 +14,10 @@ mod test { pub struct TestPlugin { name: String, masters: Vec, - pub is_master: bool, - pub is_blueprint_plugin: bool, - pub override_record_count: usize, - pub asset_count: usize, + pub(super) is_master: bool, + pub(super) is_blueprint_plugin: bool, + pub(super) override_record_count: usize, + pub(super) asset_count: usize, overlapping_record_plugins: Vec, overlapping_asset_plugins: Vec, } diff --git a/src/sorting/plugins.rs b/src/sorting/plugins.rs index 601d6ba0..c0e3a3b3 100644 --- a/src/sorting/plugins.rs +++ b/src/sorting/plugins.rs @@ -275,8 +275,8 @@ impl<'a, T: SortingPlugin> PluginsGraph<'a, T> { early_loader_indices.sort_by_key(|e| e.0); for window in early_loader_indices.windows(2) { - if let [(_, from_index), (_, to_index)] = window { - self.add_edge(*from_index, *to_index, EdgeType::Hardcoded); + if let [(_, from_index), (_, to_index)] = *window { + self.add_edge(from_index, to_index, EdgeType::Hardcoded); } } @@ -506,9 +506,10 @@ impl<'a, T: SortingPlugin> PluginsGraph<'a, T> { nodes.sort_by_key(|a| self[*a].load_order_index); for window in nodes.windows(2) { - let (current, next) = match window { - [a, b] => (*a, *b), - _ => panic!("Window length should be 2, got {}", window.len()), + let [current, next] = *window else { + // This should never happen. + logging::error!("Unexpectedly encountered a window length that was not 2"); + continue; }; match self.find_path(next, current)? { @@ -700,8 +701,8 @@ impl<'a, T: SortingPlugin> PluginsGraph<'a, T> { logging::trace!("Checking uniqueness of path through plugin graph..."); - path.windows(2).find_map(|slice| match slice { - [a, b] => self.inner.contains_edge(*a, *b).not().then_some((*a, *b)), + path.windows(2).find_map(|slice| match *slice { + [a, b] => self.inner.contains_edge(a, b).not().then_some((a, b)), _ => None, }) } @@ -995,8 +996,10 @@ fn get_plugins_in_groups( if is_log_enabled(LogLevel::Debug) { logging::debug!("Found the following plugins in groups:"); - for (key, value) in &plugins_in_groups { - let plugin_names: Vec<_> = value + let mut keys = plugins_in_groups.keys().collect::>(); + keys.sort(); + for key in keys { + let plugin_names: Vec<_> = plugins_in_groups[key] .iter() .map(|i| format!("\"{}\"", graph[*i].name())) .collect(); @@ -1256,7 +1259,7 @@ mod tests { } fn get_plugin(&self, name: &str) -> &(TestPlugin, usize) { - self.plugins.get(name).unwrap() + &self.plugins[name] } fn get_plugin_mut(&mut self, name: &str) -> &mut TestPlugin { diff --git a/src/tests.rs b/src/tests.rs index af611f5e..e4246767 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -167,9 +167,9 @@ fn data_path(game_type: GameType, game_path: &Path) -> PathBuf { pub struct Fixture { _temp_dir: TempDir, - pub game_type: GameType, - pub game_path: PathBuf, - pub local_path: PathBuf, + pub(crate) game_type: GameType, + pub(crate) game_path: PathBuf, + pub(crate) local_path: PathBuf, } impl Fixture { diff --git a/src/version.rs b/src/version.rs index 0e95aafe..0bb260c9 100644 --- a/src/version.rs +++ b/src/version.rs @@ -28,6 +28,10 @@ pub fn is_compatible(major: u32, minor: u32, _patch: u32) -> bool { } } +#[expect( + clippy::as_conversions, + reason = "Can't const-convert the byte to a u32 a safer way" +)] const fn parse_u32(value: &str) -> u32 { let mut acc = 0; let mut i = 0;