diff --git a/.vscode/settings.json b/.vscode/settings.json index 1582aac..5792051 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,6 @@ { "[json][jsonc][markdown][yaml]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" + "editor.defaultFormatter": "prettier.prettier-vscode" }, "vscord.app.privacyMode.enable": true } diff --git a/clippy.toml b/clippy.toml new file mode 100644 index 0000000..8ae4a46 --- /dev/null +++ b/clippy.toml @@ -0,0 +1,9 @@ +absolute-paths-max-segments = 3 +max-fn-params-bools = 2 +max-struct-bools = 2 +single-char-binding-names-threshold = 3 +too-many-arguments-threshold = 3 + +disallowed-methods = [ + { path = "std::process::exit", reason = "this performs a dirty exit and prevents the macro from returning a `TokenStream`" }, +] diff --git a/impl/Cargo.toml b/impl/Cargo.toml index f73edde..c47b2b2 100644 --- a/impl/Cargo.toml +++ b/impl/Cargo.toml @@ -14,26 +14,243 @@ categories = ["development-tools", "rust-patterns"] [lib] proc-macro = true +[dependencies] +proc-macro2 = "1.0.104" +quote = "1.0.42" +regex = "1.12.2" +syn = { version = "2.0.112", features = [] } + +[dev-dependencies] +error-stack = "0.6.0" + [lints.rust] +ambiguous_glob_reexports = "deny" +ambiguous_negative_literals = "warn" +closure_returning_async_block = "warn" +coherence_leak_check = "deny" +const_evaluatable_unchecked = "forbid" +const_item_mutation = "deny" +deprecated_where_clause_location = "deny" +deref_into_dyn_supertrait = "deny" +explicit_outlives_requirements = "warn" +exported_private_dependencies = "deny" +forbidden_lint_groups = "forbid" +hidden_glob_reexports = "deny" +incomplete_features = "forbid" +internal_features = "deny" +late_bound_lifetime_arguments = "forbid" +malformed_diagnostic_attributes = "deny" +malformed_diagnostic_format_literals = "deny" missing_docs = "warn" +opaque_hidden_inferred_bound = "forbid" +redundant_imports = "warn" +redundant_lifetimes = "warn" +refining_impl_trait_internal = "deny" +refining_impl_trait_reachable = "deny" +self_constructor_from_outer_item = "forbid" single_use_lifetimes = "warn" -unconditional_recursion = "deny" -unit_bindings = "warn" +special_module_name = "deny" +unconditional_recursion = "forbid" +uncovered_param_in_projection = "forbid" +unexpected_cfgs = "deny" +unfulfilled_lint_expectations = "deny" +unknown_diagnostic_attributes = "forbid" +unknown_lints = "forbid" unnameable_types = "warn" unreachable_pub = "warn" -unsafe_code = "deny" +unsafe_code = "forbid" +unused_extern_crates = "warn" +unused_import_braces = "warn" unused_lifetimes = "warn" +unused_macro_rules = "warn" +unused_qualifications = "warn" +unused_results = "warn" +variant_size_differences = "warn" [lints.clippy] -unwrap_used = "warn" +absolute_paths = "warn" +allow_attributes = "warn" +arc_with_non_send_sync = "deny" +arithmetic_side_effects = "warn" +as_conversions = "warn" +as_underscore = "deny" +assertions_on_result_states = "warn" +assigning_clones = "warn" +blanket_clippy_restriction_lints = "deny" +cargo_common_metadata = "warn" +case_sensitive_file_extension_comparisons = "warn" +cfg_not_test = "warn" +clone_on_ref_ptr = "warn" +cloned_instead_of_copied = "warn" +comparison_chain = "warn" +copy_iterator = "warn" +declare_interior_mutable_const = "deny" +default_trait_access = "warn" +deprecated_cfg_attr = "deny" +deprecated_clippy_cfg_attr = "deny" +disallowed_methods = "deny" +doc_broken_link = "warn" +doc_comment_double_space_linebreaks = "warn" +doc_include_without_cfg = "warn" +duplicate_mod = "deny" +elidable_lifetime_names = "warn" +empty_drop = "warn" +empty_enum_variants_with_brackets = "warn" +empty_line_after_outer_attr = "deny" +empty_loop = "deny" +empty_structs_with_brackets = "warn" +enum_glob_use = "deny" +error_impl_error = "deny" +excessive_precision = "deny" expect_used = "warn" -panic = "warn" - -[dependencies] -proc-macro2 = "1.0.104" -quote = "1.0.42" -regex = "1.12.2" -syn = { version = "2.0.112", features = [] } - -[dev-dependencies] -error-stack = "0.6.0" +expl_impl_clone_on_copy = "deny" +explicit_deref_methods = "warn" +explicit_into_iter_loop = "warn" +explicit_iter_loop = "warn" +field_scoped_visibility_modifiers = "warn" +filetype_is_file = "warn" +filter_map_next = "warn" +flat_map_option = "warn" +float_cmp = "warn" +fn_params_excessive_bools = "warn" +format_collect = "warn" +format_push_string = "warn" +if_not_else = "warn" +if_then_some_else_none = "warn" +ignore_without_reason = "warn" +ignored_unit_patterns = "warn" +impl_trait_in_params = "warn" +implicit_clone = "warn" +implicit_hasher = "deny" +incompatible_msrv = "forbid" +inconsistent_struct_constructor = "warn" +indexing_slicing = "deny" +inefficient_to_string = "warn" +infinite_loop = "deny" +items_after_statements = "deny" +iter_filter_is_ok = "warn" +iter_not_returning_iterator = "warn" +iter_over_hash_type = "warn" +large_futures = "deny" +large_include_file = "warn" +large_stack_arrays = "warn" +large_types_passed_by_value = "warn" +legacy_numeric_constants = "deny" +lossy_float_literal = "deny" +macro_use_imports = "warn" +manual_assert = "warn" +manual_instant_elapsed = "warn" +manual_is_power_of_two = "warn" +manual_is_variant_and = "warn" +manual_let_else = "warn" +manual_midpoint = "warn" +manual_string_new = "warn" +many_single_char_names = "warn" +map_err_ignore = "deny" +map_unwrap_or = "warn" +map_with_unused_argument_over_ranges = "warn" +match_bool = "warn" +match_same_arms = "warn" +match_wild_err_arm = "warn" +match_wildcard_for_single_variants = "warn" +maybe_infinite_iter = "warn" +mem_forget = "deny" +mismatching_type_param_order = "warn" +missing_assert_message = "deny" +missing_enforced_import_renames = "deny" +missing_fields_in_debug = "warn" +missing_inline_in_public_items = "deny" +missing_panics_doc = "warn" +mixed_attributes_style = "deny" +mixed_read_write_in_expression = "warn" +module_name_repetitions = "warn" +multi_assignments = "deny" +multiple_crate_versions = "warn" +multiple_inherent_impl = "deny" +must_use_unit = "deny" +mut_mut = "warn" +mut_range_bound = "deny" +mutable_key_type = "deny" +naive_bytecount = "warn" +needless_bitwise_bool = "warn" +needless_continue = "warn" +needless_for_each = "warn" +needless_pass_by_value = "warn" +needless_raw_string_hashes = "warn" +needless_raw_strings = "warn" +negative_feature_names = "warn" +no_effect_underscore_binding = "warn" +non_std_lazy_statics = "warn" +non_zero_suggestions = "warn" +option_as_ref_cloned = "warn" +option_option = "warn" +panic = "deny" +partial_pub_fields = "deny" +pathbuf_init_then_push = "warn" +pattern_type_mismatch = "deny" +permissions_set_readonly_false = "deny" +pointer_format = "forbid" +precedence_bits = "warn" +print_in_format_impl = "deny" +print_stderr = "warn" +print_stdout = "warn" +pub_underscore_fields = "warn" +pub_without_shorthand = "warn" +range_minus_one = "warn" +range_plus_one = "warn" +rc_buffer = "warn" +rc_mutex = "deny" +redundant_closure_for_method_calls = "warn" +redundant_else = "warn" +redundant_feature_names = "warn" +redundant_test_prefix = "warn" +redundant_type_annotations = "warn" +ref_option = "deny" +renamed_function_params = "deny" +rest_pat_in_fully_bound_structs = "warn" +return_and_then = "warn" +same_functions_in_if_condition = "warn" +same_name_method = "deny" +self_named_module_files = "deny" +semicolon_inside_block = "warn" +shadow_reuse = "deny" +shadow_same = "deny" +shadow_unrelated = "deny" +should_panic_without_expect = "deny" +similar_names = "warn" +single_char_pattern = "warn" +single_match_else = "warn" +stable_sort_primitive = "warn" +str_split_at_newline = "warn" +str_to_string = "warn" +string_add = "warn" +string_add_assign = "warn" +string_slice = "deny" +struct_excessive_bools = "warn" +struct_field_names = "warn" +test_attr_in_doctest = "deny" +tests_outside_test_module = "deny" +todo = "warn" +trivially_copy_pass_by_ref = "warn" +try_err = "warn" +unicode_not_nfc = "warn" +unimplemented = "warn" +uninlined_format_args = "warn" +unnecessary_box_returns = "warn" +unnecessary_debug_formatting = "warn" +unneeded_field_pattern = "warn" +unnested_or_patterns = "warn" +unreadable_literal = "warn" +unseparated_literal_suffix = "warn" +unused_async = "warn" +unused_self = "warn" +unused_trait_names = "warn" +unwrap_used = "deny" +used_underscore_binding = "warn" +used_underscore_items = "warn" +verbose_bit_mask = "warn" +verbose_file_reads = "warn" +wildcard_dependencies = "forbid" +wildcard_imports = "deny" +zero_sized_map_values = "warn" +zombie_processes = "deny" diff --git a/impl/src/lib.rs b/impl/src/lib.rs index ea5179e..b2648e1 100644 --- a/impl/src/lib.rs +++ b/impl/src/lib.rs @@ -107,6 +107,8 @@ //! [`error-stack`]: https://crates.io/crates/error-stack //! [documentation]: https://docs.rs/error-stack-macros2 +#![deny(unstable_features)] + use proc_macro::TokenStream; use quote::quote; use syn::parse_macro_input; diff --git a/impl/src/types/fmt/input.rs b/impl/src/types/fmt/input.rs index a8bc643..75bc194 100644 --- a/impl/src/types/fmt/input.rs +++ b/impl/src/types/fmt/input.rs @@ -25,7 +25,7 @@ impl Debug for StructFormatInput { impl Parse for StructFormatInput { fn parse(input: ParseStream) -> syn::Result { - let lit_str: LitStr = input.parse()?; + let input_lit_str: LitStr = input.parse()?; if !input.is_empty() { return Err(syn::Error::new( input.span(), @@ -33,16 +33,22 @@ impl Parse for StructFormatInput { )); } - #[allow(clippy::unwrap_used)] + #[expect( + clippy::unwrap_used, + reason = "this pattern is valid and the regex is under the size limit" + )] let regex = Regex::new(r"\{(\w+)(?::.+?)?\}").unwrap(); - let mut fmt_string = lit_str.value(); + let mut fmt_string = input_lit_str.value(); let mut args = Punctuated::new(); - let lit_str_span = lit_str.span(); - drop(lit_str); + let lit_str_span = input_lit_str.span(); + drop(input_lit_str); while let Some(captures) = regex.captures(&fmt_string) { - #[allow(clippy::unwrap_used)] + #[expect( + clippy::unwrap_used, + reason = "the first capture group is guaranteed to appear" + )] let group = captures.get(1).unwrap(); drop(captures); @@ -73,7 +79,10 @@ impl Parse for StructFormatInput { impl ToTokens for StructFormatInput { fn to_tokens(&self, tokens: &mut TokenStream2) { - let Self { lit_str, args } = self; + let Self { + ref lit_str, + ref args, + } = *self; tokens.extend(quote! { #lit_str, #args @@ -95,7 +104,7 @@ impl Debug for VariantFormatInput { impl Parse for VariantFormatInput { fn parse(input: ParseStream) -> syn::Result { - let lit_str: LitStr = input.parse()?; + let input_lit_str: LitStr = input.parse()?; if !input.is_empty() { return Err(syn::Error::new( input.span(), @@ -103,23 +112,29 @@ impl Parse for VariantFormatInput { )); } - #[allow(clippy::unwrap_used)] + #[expect( + clippy::unwrap_used, + reason = "this pattern is valid and the regex is under the size limit" + )] let regex = Regex::new(r"\{(\w+)(?::.+?)?\}").unwrap(); - let mut fmt_string = lit_str.value(); + let mut fmt_string = input_lit_str.value(); let mut args = Punctuated::new(); - let lit_str_span = lit_str.span(); - drop(lit_str); + let lit_str_span = input_lit_str.span(); + drop(input_lit_str); while let Some(captures) = regex.captures(&fmt_string) { - #[allow(clippy::unwrap_used)] + #[expect( + clippy::unwrap_used, + reason = "the first capture group is guaranteed to appear" + )] let group = captures.get(1).unwrap(); drop(captures); let inline_arg_str = group.as_str(); let ident_str = if inline_arg_str.parse::().is_ok() { - &format!("_field{}", inline_arg_str) + &format!("_field{inline_arg_str}") } else { inline_arg_str }; @@ -141,7 +156,10 @@ impl Parse for VariantFormatInput { impl ToTokens for VariantFormatInput { fn to_tokens(&self, tokens: &mut TokenStream2) { - let Self { lit_str, args } = self; + let Self { + ref lit_str, + ref args, + } = *self; tokens.extend(quote! { #lit_str, #args @@ -150,7 +168,10 @@ impl ToTokens for VariantFormatInput { } #[cfg(test)] -#[allow(clippy::expect_used)] +#[expect( + clippy::expect_used, + reason = "this is a test module with calls to `.expect()`" +)] mod tests { use super::*; diff --git a/impl/src/types/fmt/mod.rs b/impl/src/types/fmt/mod.rs index cc4be8a..abf57e7 100644 --- a/impl/src/types/fmt/mod.rs +++ b/impl/src/types/fmt/mod.rs @@ -4,7 +4,7 @@ use std::fmt::{self, Debug, Formatter}; use proc_macro2::{Span, TokenStream as TokenStream2}; use quote::{ToTokens, quote}; -use syn::{Attribute, Data, Fields, Ident, LitStr, spanned::Spanned}; +use syn::{Attribute, Data, Fields, Ident, LitStr, spanned::Spanned as _}; mod input; use input::{StructFormatInput, VariantFormatInput}; @@ -21,6 +21,7 @@ pub(crate) enum TypeData { variant_display_inputs: Vec, }, + // TODO: also use for structs with never (!) field EmptyEnum, } @@ -61,7 +62,7 @@ impl TypeData { default_display_input, variant_display_inputs: variant_display_inputs .into_iter() - .filter_map(|state| state.data()) + .filter_map(VariantState::data) .collect(), }); }; @@ -85,7 +86,7 @@ impl TypeData { if !none_spans.is_empty() { drop(valid_variants); - #[allow(clippy::unwrap_used)] + #[expect(clippy::unwrap_used, reason="this call to `Iterator::reduce()` returns `Some` because `none_spans` is not empty")] return Err(none_spans .into_iter() .map(|span| { @@ -107,7 +108,7 @@ impl TypeData { }) } - _ => { + Data::Union(_) => { drop(input_data); drop(default_display_attr); @@ -129,16 +130,16 @@ impl Debug for TypeData { impl ToTokens for TypeData { fn to_tokens(&self, tokens: &mut TokenStream2) { - match self { - Self::Struct { display_input } => { + match *self { + Self::Struct { ref display_input } => { tokens.extend(quote! { ::core::write!(f, #display_input) }); } Self::Enum { - default_display_input, - variant_display_inputs, + ref default_display_input, + ref variant_display_inputs, } => { let branches = variant_display_inputs .iter() @@ -159,6 +160,7 @@ impl ToTokens for TypeData { } Self::EmptyEnum => { + // TODO: fully qualify macro path tokens.extend(quote! { unreachable!("attempted to format an empty enum") }); @@ -175,12 +177,11 @@ enum VariantState { impl VariantState { fn data(self) -> Option { - match self { - Self::Valid(data) => Some(data), - _ => { - drop(self); - None - } + if let Self::Valid(data) = self { + Some(data) + } else { + drop(self); + None } } } @@ -197,19 +198,19 @@ pub(crate) struct VariantData { impl ToTokens for VariantData { fn to_tokens(&self, tokens: &mut TokenStream2) { let Self { - other_attrs, - ident, - fields, - display_input, - } = self; + ref other_attrs, + ref ident, + ref fields, + ref display_input, + } = *self; let field_idents = fields.iter().enumerate().map(|(i, field)| { field.ident.clone().unwrap_or_else(|| { - Ident::new(&format!("_field{}", i), field.span()) + Ident::new(&format!("_field{i}"), field.span()) }) }); - let field_tokens = match fields { + let field_tokens = match *fields { Fields::Named(_) => quote! { { #(#field_idents),* } }, Fields::Unnamed(_) => quote! { ( #(#field_idents),* ) }, Fields::Unit => { @@ -226,7 +227,10 @@ impl ToTokens for VariantData { } #[cfg(test)] -#[allow(clippy::expect_used)] +#[expect( + clippy::expect_used, + reason = "this is a test module with calls to `.expect()`" +)] mod tests { use crate::ErrorStackDeriveInput; diff --git a/impl/src/types/fmt/util.rs b/impl/src/types/fmt/util.rs index 0757e3a..447d330 100644 --- a/impl/src/types/fmt/util.rs +++ b/impl/src/types/fmt/util.rs @@ -3,10 +3,10 @@ use std::convert::Infallible; use proc_macro2::Span; use syn::{ Attribute, Meta, Variant, parse::Parse, punctuated::Punctuated, - spanned::Spanned, token::Comma, + spanned::Spanned as _, token::Comma, }; -use super::{ValidVariantState, VariantData, VariantState}; +use super::{super::util, ValidVariantState, VariantData, VariantState}; pub(crate) fn get_format_input(display_attr: Attribute) -> syn::Result where @@ -53,13 +53,14 @@ pub(crate) fn collect_valid_variant_states( variants: Punctuated, ) -> Result, syn::Error> { let mut variant_states_iter = variants.into_iter().map(|variant| { + use VariantState as VS; + let variant_span = variant.span(); drop(variant.discriminant); let mut attrs = variant.attrs; - let display_attr = crate::types::util::take_display_attr(&mut attrs); + let display_attr = util::take_display_attr(&mut attrs); - use VariantState as VS; match display_attr { None => { drop(variant.fields); diff --git a/impl/src/types/mod.rs b/impl/src/types/mod.rs index e23ea1e..07e7b99 100644 --- a/impl/src/types/mod.rs +++ b/impl/src/types/mod.rs @@ -52,11 +52,11 @@ impl Parse for ErrorStackDeriveInput { impl ToTokens for ErrorStackDeriveInput { fn to_tokens(&self, tokens: &mut TokenStream2) { let Self { - other_attrs, - ident, - generics, - display_data, - } = self; + ref other_attrs, + ref ident, + ref generics, + ref display_data, + } = *self; let where_clause = &generics.where_clause; @@ -73,6 +73,7 @@ impl ToTokens for ErrorStackDeriveInput { .map(util::generic_reduced_to_ident) .collect(); + // TODO: move `other_attrs` down to avoid overwriting users' attrs tokens.extend(quote! { #(#other_attrs)* #[allow(single_use_lifetimes)] @@ -95,7 +96,10 @@ impl ToTokens for ErrorStackDeriveInput { } #[cfg(test)] -#[allow(clippy::expect_used)] +#[expect( + clippy::expect_used, + reason = "this is a test module with calls to `.expect()`" +)] mod tests { use quote::quote; diff --git a/impl/src/types/util.rs b/impl/src/types/util.rs index 4303054..42ef080 100644 --- a/impl/src/types/util.rs +++ b/impl/src/types/util.rs @@ -4,7 +4,7 @@ use syn::{ Attribute, GenericParam, Ident, Lifetime, Path, TraitBound, TraitBoundModifier, TypeParamBound, punctuated::Punctuated, - spanned::Spanned, + spanned::Spanned as _, token::{Colon, Comma}, }; @@ -16,9 +16,9 @@ pub(crate) enum ReducedGenericParam { impl ToTokens for ReducedGenericParam { fn to_tokens(&self, tokens: &mut TokenStream2) { use ReducedGenericParam as RGP; - match self { - RGP::ConstOrType(ident) => tokens.extend(quote! { #ident }), - RGP::Lifetime(lifetime) => tokens.extend(quote! { #lifetime }), + match *self { + RGP::ConstOrType(ref ident) => tokens.extend(quote! { #ident }), + RGP::Lifetime(ref lifetime) => tokens.extend(quote! { #lifetime }), } } } @@ -57,18 +57,18 @@ pub(crate) fn take_display_attr( pub(crate) fn remove_generic_default(param: &mut GenericParam) { use GenericParam as GP; - match param { - GP::Const(const_p) => { + match *param { + GP::Const(ref mut const_p) => { const_p.eq_token = None; const_p.default = None; } - GP::Type(type_p) => { + GP::Type(ref mut type_p) => { type_p.eq_token = None; type_p.default = None; } - _ => {} + GP::Lifetime(_) => {} } } @@ -105,8 +105,8 @@ pub(crate) fn generic_reduced_to_ident( pub(crate) fn add_debug_trait_bound(param: &mut GenericParam) { use GenericParam as GP; - if let GP::Type(type_p) = param { - #[allow(clippy::unwrap_used)] + if let GP::Type(ref mut type_p) = *param { + #[expect(clippy::unwrap_used, reason = "this `TokenStream` is valid")] let trait_path: Path = syn::parse2(quote! { ::core::fmt::Debug }).unwrap(); diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..fb57a2f --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,3 @@ +[toolchain] +channel = "stable" +components = ["cargo", "clippy", "rust-std", "rustc", "rustfmt"] diff --git a/rustfmt.toml b/rustfmt.toml index 337c043..a05c094 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,2 +1,4 @@ -newline_style = "Unix" max_width = 80 +newline_style = "Unix" +use_field_init_shorthand = true +use_try_shorthand = true diff --git a/tests/Cargo.toml b/tests/Cargo.toml index a7a79c6..c38222e 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -11,15 +11,231 @@ error-stack = "0.6.0" error-stack-macros2 = { path = "../impl" } [lints.rust] +ambiguous_glob_reexports = "deny" +ambiguous_negative_literals = "warn" +closure_returning_async_block = "warn" +coherence_leak_check = "deny" +const_evaluatable_unchecked = "forbid" +const_item_mutation = "deny" +deprecated_where_clause_location = "deny" +deref_into_dyn_supertrait = "deny" +explicit_outlives_requirements = "warn" +exported_private_dependencies = "deny" +forbidden_lint_groups = "forbid" +hidden_glob_reexports = "deny" +incomplete_features = "forbid" +internal_features = "deny" +late_bound_lifetime_arguments = "forbid" +malformed_diagnostic_attributes = "deny" +malformed_diagnostic_format_literals = "deny" +opaque_hidden_inferred_bound = "forbid" +redundant_imports = "warn" +redundant_lifetimes = "warn" +refining_impl_trait_internal = "deny" +refining_impl_trait_reachable = "deny" +self_constructor_from_outer_item = "forbid" single_use_lifetimes = "warn" -unconditional_recursion = "deny" -unit_bindings = "warn" +special_module_name = "deny" +unconditional_recursion = "forbid" +uncovered_param_in_projection = "forbid" +unexpected_cfgs = "deny" +unfulfilled_lint_expectations = "deny" +unknown_diagnostic_attributes = "forbid" +unknown_lints = "forbid" unnameable_types = "warn" unreachable_pub = "warn" -unsafe_code = "deny" +unsafe_code = "forbid" +unused_extern_crates = "warn" +unused_import_braces = "warn" unused_lifetimes = "warn" +unused_macro_rules = "warn" +unused_qualifications = "warn" +unused_results = "warn" +variant_size_differences = "warn" [lints.clippy] -unwrap_used = "warn" +absolute_paths = "warn" +allow_attributes = "warn" +arc_with_non_send_sync = "deny" +arithmetic_side_effects = "warn" +as_conversions = "warn" +as_underscore = "deny" +assertions_on_result_states = "warn" +assigning_clones = "warn" +blanket_clippy_restriction_lints = "deny" +case_sensitive_file_extension_comparisons = "warn" +cfg_not_test = "warn" +clone_on_ref_ptr = "warn" +cloned_instead_of_copied = "warn" +comparison_chain = "warn" +copy_iterator = "warn" +declare_interior_mutable_const = "deny" +default_trait_access = "warn" +deprecated_cfg_attr = "deny" +deprecated_clippy_cfg_attr = "deny" +disallowed_methods = "deny" +doc_broken_link = "warn" +doc_comment_double_space_linebreaks = "warn" +doc_include_without_cfg = "warn" +duplicate_mod = "deny" +elidable_lifetime_names = "warn" +empty_drop = "warn" +empty_enum_variants_with_brackets = "warn" +empty_line_after_outer_attr = "deny" +empty_loop = "deny" +empty_structs_with_brackets = "warn" +enum_glob_use = "deny" +error_impl_error = "deny" +excessive_precision = "deny" expect_used = "warn" -panic = "warn" +expl_impl_clone_on_copy = "deny" +explicit_deref_methods = "warn" +explicit_into_iter_loop = "warn" +explicit_iter_loop = "warn" +field_scoped_visibility_modifiers = "warn" +filetype_is_file = "warn" +filter_map_next = "warn" +flat_map_option = "warn" +float_cmp = "warn" +fn_params_excessive_bools = "warn" +format_collect = "warn" +format_push_string = "warn" +if_not_else = "warn" +if_then_some_else_none = "warn" +ignore_without_reason = "warn" +ignored_unit_patterns = "warn" +impl_trait_in_params = "warn" +implicit_clone = "warn" +implicit_hasher = "deny" +incompatible_msrv = "forbid" +inconsistent_struct_constructor = "warn" +indexing_slicing = "deny" +inefficient_to_string = "warn" +infinite_loop = "deny" +items_after_statements = "deny" +iter_filter_is_ok = "warn" +iter_not_returning_iterator = "warn" +iter_over_hash_type = "warn" +large_futures = "deny" +large_include_file = "warn" +large_stack_arrays = "warn" +large_types_passed_by_value = "warn" +legacy_numeric_constants = "deny" +lossy_float_literal = "deny" +macro_use_imports = "warn" +manual_assert = "warn" +manual_instant_elapsed = "warn" +manual_is_power_of_two = "warn" +manual_is_variant_and = "warn" +manual_let_else = "warn" +manual_midpoint = "warn" +manual_string_new = "warn" +many_single_char_names = "warn" +map_err_ignore = "deny" +map_unwrap_or = "warn" +map_with_unused_argument_over_ranges = "warn" +match_bool = "warn" +match_same_arms = "warn" +match_wild_err_arm = "warn" +match_wildcard_for_single_variants = "warn" +maybe_infinite_iter = "warn" +mem_forget = "deny" +mismatching_type_param_order = "warn" +missing_assert_message = "deny" +missing_enforced_import_renames = "deny" +missing_fields_in_debug = "warn" +missing_inline_in_public_items = "deny" +missing_panics_doc = "warn" +mixed_attributes_style = "deny" +mixed_read_write_in_expression = "warn" +module_name_repetitions = "warn" +multi_assignments = "deny" +multiple_crate_versions = "warn" +multiple_inherent_impl = "deny" +must_use_unit = "deny" +mut_mut = "warn" +mut_range_bound = "deny" +mutable_key_type = "deny" +naive_bytecount = "warn" +needless_bitwise_bool = "warn" +needless_continue = "warn" +needless_for_each = "warn" +needless_pass_by_value = "warn" +needless_raw_string_hashes = "warn" +needless_raw_strings = "warn" +negative_feature_names = "warn" +no_effect_underscore_binding = "warn" +non_std_lazy_statics = "warn" +non_zero_suggestions = "warn" +option_as_ref_cloned = "warn" +option_option = "warn" +panic = "deny" +partial_pub_fields = "deny" +pathbuf_init_then_push = "warn" +pattern_type_mismatch = "deny" +permissions_set_readonly_false = "deny" +pointer_format = "forbid" +precedence_bits = "warn" +print_in_format_impl = "deny" +print_stderr = "warn" +print_stdout = "warn" +pub_underscore_fields = "warn" +pub_without_shorthand = "warn" +range_minus_one = "warn" +range_plus_one = "warn" +rc_buffer = "warn" +rc_mutex = "deny" +redundant_closure_for_method_calls = "warn" +redundant_else = "warn" +redundant_feature_names = "warn" +redundant_test_prefix = "warn" +redundant_type_annotations = "warn" +ref_option = "deny" +renamed_function_params = "deny" +rest_pat_in_fully_bound_structs = "warn" +return_and_then = "warn" +same_functions_in_if_condition = "warn" +same_name_method = "deny" +self_named_module_files = "deny" +semicolon_inside_block = "warn" +shadow_reuse = "deny" +shadow_same = "deny" +shadow_unrelated = "deny" +should_panic_without_expect = "deny" +similar_names = "warn" +single_char_pattern = "warn" +single_match_else = "warn" +stable_sort_primitive = "warn" +str_split_at_newline = "warn" +str_to_string = "warn" +string_add = "warn" +string_add_assign = "warn" +string_slice = "deny" +struct_excessive_bools = "warn" +struct_field_names = "warn" +test_attr_in_doctest = "deny" +tests_outside_test_module = "deny" +todo = "warn" +trivially_copy_pass_by_ref = "warn" +try_err = "warn" +unicode_not_nfc = "warn" +unimplemented = "warn" +uninlined_format_args = "warn" +unnecessary_box_returns = "warn" +unnecessary_debug_formatting = "warn" +unneeded_field_pattern = "warn" +unnested_or_patterns = "warn" +unreadable_literal = "warn" +unseparated_literal_suffix = "warn" +unused_async = "warn" +unused_self = "warn" +unused_trait_names = "warn" +unwrap_used = "deny" +used_underscore_binding = "warn" +used_underscore_items = "warn" +verbose_bit_mask = "warn" +verbose_file_reads = "warn" +wildcard_dependencies = "forbid" +wildcard_imports = "deny" +zero_sized_map_values = "warn" +zombie_processes = "deny" diff --git a/tests/src/enum_variants.rs b/tests/src/enum_variants.rs index f3c9d23..dac46ec 100644 --- a/tests/src/enum_variants.rs +++ b/tests/src/enum_variants.rs @@ -17,6 +17,7 @@ mod tests { fn named_field_variant_works_without_interpolation() { #[derive(Debug, Error)] enum EnumType { + // TODO: rename all variants in all `EnumType`s #[display("named field variant")] NamedFieldVariant { _length: usize, @@ -109,7 +110,6 @@ mod tests { } #[test] - #[allow(dead_code)] fn tuple_variant_works_with_interpolation_of_all_fields() { #[derive(Debug, Error)] enum EnumType { diff --git a/tests/src/enums.rs b/tests/src/enums.rs index c6d0ac1..32dc1a2 100644 --- a/tests/src/enums.rs +++ b/tests/src/enums.rs @@ -16,49 +16,50 @@ mod tests { } #[test] - #[allow(dead_code)] - #[allow(clippy::enum_variant_names)] + #[expect( + dead_code, + reason = "this test requires `EnumType::TupleVariant`'s fields to exist, even though they won't be read" + )] fn enum_works_with_display_attr_default() { #[derive(Debug, Error)] #[display("enum type")] enum EnumType { - UnitVariant, + Unit, - NamedFieldVariant { + NamedFields { _length: usize, _is_ascii: bool, _inner: String, }, - TupleVariant(isize, isize, isize), + Tuple(isize, isize, isize), } - let unit = EnumType::UnitVariant; + let unit = EnumType::Unit; assert_eq!(unit.to_string(), "enum type"); - let named_field = EnumType::NamedFieldVariant { + let named_field = EnumType::NamedFields { _length: 5, _is_ascii: true, _inner: String::from("hello"), }; assert_eq!(named_field.to_string(), "enum type"); - let tuple = EnumType::TupleVariant(5, 10, 15); + let tuple = EnumType::Tuple(5, 10, 15); assert_eq!(tuple.to_string(), "enum type"); } #[test] - #[allow(clippy::enum_variant_names)] fn enum_works_with_display_attr_default_and_some_variants() { #[derive(Debug, Error)] #[display("enum type")] enum EnumType { - UnitVariant, + Unit, #[display( "named field variant: {inner:?} has {length} characters and is ascii={is_ascii}" )] - NamedFieldVariant { + NamedFields { length: usize, is_ascii: bool, inner: String, @@ -67,13 +68,13 @@ mod tests { #[display( "tuple variant: point {2} units in front of the origin, and with x and y coords ({0}, {1})" )] - TupleVariant(isize, isize, isize), + Tuple(isize, isize, isize), } - let unit = EnumType::UnitVariant; + let unit = EnumType::Unit; assert_eq!(unit.to_string(), "enum type"); - let named_field = EnumType::NamedFieldVariant { + let named_field = EnumType::NamedFields { length: 5, is_ascii: true, inner: String::from("hello"), @@ -83,7 +84,7 @@ mod tests { "named field variant: \"hello\" has 5 characters and is ascii=true" ); - let tuple = EnumType::TupleVariant(5, 10, 15); + let tuple = EnumType::Tuple(5, 10, 15); assert_eq!( tuple.to_string(), "tuple variant: point 15 units in front of the origin, and with x and y coords (5, 10)" @@ -91,18 +92,17 @@ mod tests { } #[test] - #[allow(clippy::enum_variant_names)] fn enum_works_with_display_attr_default_and_all_variants() { #[derive(Debug, Error)] #[display("enum type")] enum EnumType { #[display("unit variant")] - UnitVariant, + Unit, #[display( "named field variant: {inner:?} has {length} characters and is ascii={is_ascii}" )] - NamedFieldVariant { + NamedFields { length: usize, is_ascii: bool, inner: String, @@ -111,13 +111,13 @@ mod tests { #[display( "tuple variant: point {2} units in front of the origin, and with x and y coords ({0}, {1})" )] - TupleVariant(isize, isize, isize), + Tuple(isize, isize, isize), } - let unit = EnumType::UnitVariant; + let unit = EnumType::Unit; assert_eq!(unit.to_string(), "unit variant"); - let named_field = EnumType::NamedFieldVariant { + let named_field = EnumType::NamedFields { length: 5, is_ascii: true, inner: String::from("hello"), @@ -127,7 +127,7 @@ mod tests { "named field variant: \"hello\" has 5 characters and is ascii=true" ); - let tuple = EnumType::TupleVariant(5, 10, 15); + let tuple = EnumType::Tuple(5, 10, 15); assert_eq!( tuple.to_string(), "tuple variant: point 15 units in front of the origin, and with x and y coords (5, 10)" @@ -135,17 +135,16 @@ mod tests { } #[test] - #[allow(clippy::enum_variant_names)] fn enum_works_with_display_attr_all_variants() { #[derive(Debug, Error)] enum EnumType { #[display("unit variant")] - UnitVariant, + Unit, #[display( "named field variant: {inner:?} has {length} characters and is ascii={is_ascii}" )] - NamedFieldVariant { + NamedFields { length: usize, is_ascii: bool, inner: String, @@ -154,13 +153,13 @@ mod tests { #[display( "tuple variant: point {2} units in front of the origin, and with x and y coords ({0}, {1})" )] - TupleVariant(isize, isize, isize), + Tuple(isize, isize, isize), } - let unit = EnumType::UnitVariant; + let unit = EnumType::Unit; assert_eq!(unit.to_string(), "unit variant"); - let named_field = EnumType::NamedFieldVariant { + let named_field = EnumType::NamedFields { length: 5, is_ascii: true, inner: String::from("hello"), @@ -170,7 +169,7 @@ mod tests { "named field variant: \"hello\" has 5 characters and is ascii=true" ); - let tuple = EnumType::TupleVariant(5, 10, 15); + let tuple = EnumType::Tuple(5, 10, 15); assert_eq!( tuple.to_string(), "tuple variant: point 15 units in front of the origin, and with x and y coords (5, 10)" diff --git a/tests/src/lib.rs b/tests/src/lib.rs index 529231b..00a4cfb 100644 --- a/tests/src/lib.rs +++ b/tests/src/lib.rs @@ -1,3 +1,5 @@ +#![deny(unstable_features)] + mod enum_variants; mod enums; mod structs; diff --git a/tests/src/structs.rs b/tests/src/structs.rs index 8e0d1ca..9f45926 100644 --- a/tests/src/structs.rs +++ b/tests/src/structs.rs @@ -126,7 +126,12 @@ mod tests { assert_eq!(test_val.to_string(), "inner = 8"); } + // FIXME: #[expect(redundant_lifetimes)] on type fails to remove warning #[test] + #[expect( + redundant_lifetimes, + reason = "this test requires a where clause with both lifetime and trait bounds" + )] fn named_field_struct_works_with_where_clause() { const STRING: &str = "t ref"; @@ -145,7 +150,10 @@ mod tests { } #[test] - #[allow(dead_code)] + #[expect( + dead_code, + reason = "this test requires `TupleStructType`'s fields to exist, even though they won't be read" + )] fn tuple_struct_works_without_interpolation() { #[derive(Debug, Error)] #[display("tuple struct")] @@ -156,7 +164,10 @@ mod tests { } #[test] - #[allow(dead_code)] + #[expect( + dead_code, + reason = "this test requires `TupleStructType`to have multiple fields, even though not all of them will be read" + )] fn tuple_struct_works_with_interpolation_of_some_fields() { #[derive(Debug, Error)] #[display("tuple struct: point with y value {1}")] @@ -167,7 +178,6 @@ mod tests { } #[test] - #[allow(dead_code)] fn tuple_struct_works_with_interpolation_of_all_fields() { #[derive(Debug, Error)] #[display(