diff --git a/CHANGELOG.md b/CHANGELOG.md index d66e6cf7fb65..2d5ac6e9c227 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1267,7 +1267,7 @@ Released 2020-11-19 * [`manual_strip`] [#6038](https://github.com/rust-lang/rust-clippy/pull/6038) * [`map_err_ignore`] [#5998](https://github.com/rust-lang/rust-clippy/pull/5998) * [`rc_buffer`] [#6044](https://github.com/rust-lang/rust-clippy/pull/6044) -* [`to_string_in_display`] [#5831](https://github.com/rust-lang/rust-clippy/pull/5831) +* `to_string_in_display` [#5831](https://github.com/rust-lang/rust-clippy/pull/5831) * `single_char_push_str` [#5881](https://github.com/rust-lang/rust-clippy/pull/5881) ### Moves and Deprecations @@ -1310,7 +1310,7 @@ Released 2020-11-19 [#5949](https://github.com/rust-lang/rust-clippy/pull/5949) * [`doc_markdown`]: allow using "GraphQL" without backticks [#5996](https://github.com/rust-lang/rust-clippy/pull/5996) -* [`to_string_in_display`]: avoid linting when calling `to_string()` on anything that is not `self` +* `to_string_in_display`: avoid linting when calling `to_string()` on anything that is not `self` [#5971](https://github.com/rust-lang/rust-clippy/pull/5971) * [`indexing_slicing`] and [`out_of_bounds_indexing`] treat references to arrays as arrays [#6034](https://github.com/rust-lang/rust-clippy/pull/6034) @@ -3213,6 +3213,7 @@ Released 2018-09-13 [`range_zip_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_zip_with_len [`rc_buffer`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_buffer [`rc_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_mutex +[`recursive_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#recursive_format_impl [`redundant_allocation`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation [`redundant_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone [`redundant_closure`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure @@ -3287,7 +3288,6 @@ Released 2018-09-13 [`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments [`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment [`to_digit_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some -[`to_string_in_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_display [`to_string_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_format_args [`todo`]: https://rust-lang.github.io/rust-clippy/master/index.html#todo [`too_many_arguments`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index ae423d799d71..1174fd14ae7e 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::macros::{FormatArgsArg, FormatArgsExpn}; +use clippy_utils::is_diag_trait_item; +use clippy_utils::macros::{is_format_macro, FormatArgsArg, FormatArgsExpn}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::implements_trait; -use clippy_utils::{is_diag_trait_item, match_def_path, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -65,21 +65,6 @@ declare_clippy_lint! { declare_lint_pass!(FormatArgs => [FORMAT_IN_FORMAT_ARGS, TO_STRING_IN_FORMAT_ARGS]); -const FORMAT_MACRO_PATHS: &[&[&str]] = &[ - &paths::FORMAT_ARGS_MACRO, - &paths::ASSERT_EQ_MACRO, - &paths::ASSERT_MACRO, - &paths::ASSERT_NE_MACRO, - &paths::EPRINT_MACRO, - &paths::EPRINTLN_MACRO, - &paths::PRINT_MACRO, - &paths::PRINTLN_MACRO, - &paths::WRITE_MACRO, - &paths::WRITELN_MACRO, -]; - -const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[sym::format_macro, sym::std_panic_macro]; - impl<'tcx> LateLintPass<'tcx> for FormatArgs { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if_chain! { @@ -87,12 +72,7 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs { let expr_expn_data = expr.span.ctxt().outer_expn_data(); let outermost_expn_data = outermost_expn_data(expr_expn_data); if let Some(macro_def_id) = outermost_expn_data.macro_def_id; - if FORMAT_MACRO_PATHS - .iter() - .any(|path| match_def_path(cx, macro_def_id, path)) - || FORMAT_MACRO_DIAG_ITEMS - .iter() - .any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, macro_def_id)); + if is_format_macro(cx, macro_def_id); if let ExpnKind::Macro(_, name) = outermost_expn_data.kind; if let Some(args) = format_args.args(); then { diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 4721b7f2b472..c6f5c854d5c6 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -241,6 +241,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(ranges::MANUAL_RANGE_CONTAINS), LintId::of(ranges::RANGE_ZIP_WITH_LEN), LintId::of(ranges::REVERSED_EMPTY_RANGES), + LintId::of(recursive_format_impl::RECURSIVE_FORMAT_IMPL), LintId::of(redundant_clone::REDUNDANT_CLONE), LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL), LintId::of(redundant_field_names::REDUNDANT_FIELD_NAMES), @@ -267,7 +268,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), LintId::of(temporary_assignment::TEMPORARY_ASSIGNMENT), LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME), - LintId::of(to_string_in_display::TO_STRING_IN_DISPLAY), LintId::of(transmute::CROSSPOINTER_TRANSMUTE), LintId::of(transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS), LintId::of(transmute::TRANSMUTE_BYTES_TO_STR), diff --git a/clippy_lints/src/lib.register_correctness.rs b/clippy_lints/src/lib.register_correctness.rs index 4217fd3a3ea7..f8b75964066c 100644 --- a/clippy_lints/src/lib.register_correctness.rs +++ b/clippy_lints/src/lib.register_correctness.rs @@ -52,12 +52,12 @@ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), ve LintId::of(ptr::INVALID_NULL_PTR_USAGE), LintId::of(ptr::MUT_FROM_REF), LintId::of(ranges::REVERSED_EMPTY_RANGES), + LintId::of(recursive_format_impl::RECURSIVE_FORMAT_IMPL), LintId::of(regex::INVALID_REGEX), LintId::of(self_assignment::SELF_ASSIGNMENT), LintId::of(serde_api::SERDE_API_MISUSE), LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), LintId::of(swap::ALMOST_SWAPPED), - LintId::of(to_string_in_display::TO_STRING_IN_DISPLAY), LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE), LintId::of(transmute::WRONG_TRANSMUTE), LintId::of(transmuting_null::TRANSMUTING_NULL), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 2d2693832e97..bfd9edaaa08c 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -414,6 +414,7 @@ store.register_lints(&[ ranges::RANGE_PLUS_ONE, ranges::RANGE_ZIP_WITH_LEN, ranges::REVERSED_EMPTY_RANGES, + recursive_format_impl::RECURSIVE_FORMAT_IMPL, redundant_clone::REDUNDANT_CLONE, redundant_closure_call::REDUNDANT_CLOSURE_CALL, redundant_else::REDUNDANT_ELSE, @@ -458,7 +459,6 @@ store.register_lints(&[ tabs_in_doc_comments::TABS_IN_DOC_COMMENTS, temporary_assignment::TEMPORARY_ASSIGNMENT, to_digit_is_some::TO_DIGIT_IS_SOME, - to_string_in_display::TO_STRING_IN_DISPLAY, trailing_empty_array::TRAILING_EMPTY_ARRAY, trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS, trait_bounds::TYPE_REPETITION_IN_BOUNDS, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f2a7e925dd39..1f2ed366a109 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -330,6 +330,7 @@ mod ptr_eq; mod ptr_offset_with_cast; mod question_mark; mod ranges; +mod recursive_format_impl; mod redundant_clone; mod redundant_closure_call; mod redundant_else; @@ -362,7 +363,6 @@ mod swap; mod tabs_in_doc_comments; mod temporary_assignment; mod to_digit_is_some; -mod to_string_in_display; mod trailing_empty_array; mod trait_bounds; mod transmute; @@ -704,7 +704,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(modulo_arithmetic::ModuloArithmetic)); store.register_early_pass(|| Box::new(reference::DerefAddrOf)); store.register_early_pass(|| Box::new(double_parens::DoubleParens)); - store.register_late_pass(|| Box::new(to_string_in_display::ToStringInDisplay::new())); + store.register_late_pass(|| Box::new(recursive_format_impl::RecursiveFormatImpl::new())); store.register_early_pass(|| Box::new(unsafe_removed_from_name::UnsafeNameRemoval)); store.register_early_pass(|| Box::new(else_if_without_else::ElseIfWithoutElse)); store.register_early_pass(|| Box::new(int_plus_one::IntPlusOne)); @@ -935,6 +935,7 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) { ls.register_renamed("clippy::disallowed_type", "clippy::disallowed_types"); ls.register_renamed("clippy::disallowed_method", "clippy::disallowed_methods"); ls.register_renamed("clippy::ref_in_deref", "clippy::needless_borrow"); + ls.register_renamed("clippy::to_string_in_display", "clippy::recursive_format_impl"); // uplifted lints ls.register_renamed("clippy::invalid_ref", "invalid_value"); diff --git a/clippy_lints/src/recursive_format_impl.rs b/clippy_lints/src/recursive_format_impl.rs new file mode 100644 index 000000000000..e7c703976bb7 --- /dev/null +++ b/clippy_lints/src/recursive_format_impl.rs @@ -0,0 +1,190 @@ +use clippy_utils::diagnostics::span_lint; +use clippy_utils::macros::{is_format_macro, root_macro_call_first_node, FormatArgsArg, FormatArgsExpn}; +use clippy_utils::{is_diag_trait_item, path_to_local, peel_ref_operators}; +use if_chain::if_chain; +use rustc_hir::{Expr, ExprKind, Impl, Item, ItemKind, QPath}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::{sym, symbol::kw, Symbol}; + +#[derive(Clone, Copy)] +enum FormatTrait { + Debug, + Display, +} + +impl FormatTrait { + fn name(self) -> Symbol { + match self { + FormatTrait::Debug => sym::Debug, + FormatTrait::Display => sym::Display, + } + } +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for format trait implementations (e.g. `Display`) with a recursive call to itself + /// which uses `self` as a parameter. + /// This is typically done indirectly with the `write!` macro or with `to_string()`. + /// + /// ### Why is this bad? + /// This will lead to infinite recursion and a stack overflow. + /// + /// ### Example + /// + /// ```rust + /// use std::fmt; + /// + /// struct Structure(i32); + /// impl fmt::Display for Structure { + /// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + /// write!(f, "{}", self.to_string()) + /// } + /// } + /// + /// ``` + /// Use instead: + /// ```rust + /// use std::fmt; + /// + /// struct Structure(i32); + /// impl fmt::Display for Structure { + /// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + /// write!(f, "{}", self.0) + /// } + /// } + /// ``` + #[clippy::version = "1.48.0"] + pub RECURSIVE_FORMAT_IMPL, + correctness, + "Format trait method called while implementing the same Format trait" +} + +#[derive(Default)] +pub struct RecursiveFormatImpl { + // Whether we are inside Display or Debug trait impl - None for neither + format_trait_impl: Option, +} + +impl RecursiveFormatImpl { + pub fn new() -> Self { + Self { + format_trait_impl: None, + } + } +} + +impl_lint_pass!(RecursiveFormatImpl => [RECURSIVE_FORMAT_IMPL]); + +impl<'tcx> LateLintPass<'tcx> for RecursiveFormatImpl { + fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + if let Some(format_trait_impl) = is_format_trait_impl(cx, item) { + self.format_trait_impl = Some(format_trait_impl); + } + } + + fn check_item_post(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + // Assume no nested Impl of Debug and Display within eachother + if is_format_trait_impl(cx, item).is_some() { + self.format_trait_impl = None; + } + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + match self.format_trait_impl { + Some(FormatTrait::Display) => { + check_to_string_in_display(cx, expr); + check_self_in_format_args(cx, expr, FormatTrait::Display); + }, + Some(FormatTrait::Debug) => { + check_self_in_format_args(cx, expr, FormatTrait::Debug); + }, + None => {}, + } + } +} + +fn check_to_string_in_display(cx: &LateContext<'_>, expr: &Expr<'_>) { + if_chain! { + // Get the hir_id of the object we are calling the method on + if let ExprKind::MethodCall(path, _, [ref self_arg, ..], _) = expr.kind; + // Is the method to_string() ? + if path.ident.name == sym!(to_string); + // Is the method a part of the ToString trait? (i.e. not to_string() implemented + // separately) + if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); + if is_diag_trait_item(cx, expr_def_id, sym::ToString); + // Is the method is called on self + if let ExprKind::Path(QPath::Resolved(_, path)) = self_arg.kind; + if let [segment] = path.segments; + if segment.ident.name == kw::SelfLower; + then { + span_lint( + cx, + RECURSIVE_FORMAT_IMPL, + expr.span, + "using `self.to_string` in `fmt::Display` implementation will cause infinite recursion", + ); + } + } +} + +fn check_self_in_format_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, impl_trait: FormatTrait) { + // Check each arg in format calls - do we ever use Display on self (directly or via deref)? + if_chain! { + if let Some(outer_macro) = root_macro_call_first_node(cx, expr); + if let macro_def_id = outer_macro.def_id; + if let Some(format_args) = FormatArgsExpn::find_nested(cx, expr, outer_macro.expn); + if is_format_macro(cx, macro_def_id); + if let Some(args) = format_args.args(); + then { + for arg in args { + if arg.format_trait != impl_trait.name() { + continue; + } + check_format_arg_self(cx, expr, &arg, impl_trait); + } + } + } +} + +fn check_format_arg_self(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &FormatArgsArg<'_>, impl_trait: FormatTrait) { + // Handle multiple dereferencing of references e.g. &&self + // Handle dereference of &self -> self that is equivalent (i.e. via *self in fmt() impl) + // Since the argument to fmt is itself a reference: &self + let reference = peel_ref_operators(cx, arg.value); + let map = cx.tcx.hir(); + // Is the reference self? + let symbol_ident = impl_trait.name().to_ident_string(); + if path_to_local(reference).map(|x| map.name(x)) == Some(kw::SelfLower) { + span_lint( + cx, + RECURSIVE_FORMAT_IMPL, + expr.span, + &format!( + "using `self` as `{}` in `impl {}` will cause infinite recursion", + &symbol_ident, &symbol_ident + ), + ); + } +} + +fn is_format_trait_impl(cx: &LateContext<'_>, item: &Item<'_>) -> Option { + if_chain! { + // Are we at an Impl? + if let ItemKind::Impl(Impl { of_trait: Some(trait_ref), .. }) = &item.kind; + if let Some(did) = trait_ref.trait_def_id(); + if let Some(name) = cx.tcx.get_diagnostic_name(did); + then { + // Is Impl for Debug or Display? + match name { + sym::Debug => Some(FormatTrait::Debug), + sym::Display => Some(FormatTrait::Display), + _ => None, + } + } else { + None + } + } +} diff --git a/clippy_lints/src/to_string_in_display.rs b/clippy_lints/src/to_string_in_display.rs deleted file mode 100644 index f8b6bdcd3e15..000000000000 --- a/clippy_lints/src/to_string_in_display.rs +++ /dev/null @@ -1,123 +0,0 @@ -use clippy_utils::diagnostics::span_lint; -use clippy_utils::{is_diag_trait_item, match_def_path, path_to_local_id, paths}; -use if_chain::if_chain; -use rustc_hir::{Expr, ExprKind, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::symbol::sym; - -declare_clippy_lint! { - /// ### What it does - /// Checks for uses of `to_string()` in `Display` traits. - /// - /// ### Why is this bad? - /// Usually `to_string` is implemented indirectly - /// via `Display`. Hence using it while implementing `Display` would - /// lead to infinite recursion. - /// - /// ### Example - /// - /// ```rust - /// use std::fmt; - /// - /// struct Structure(i32); - /// impl fmt::Display for Structure { - /// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - /// write!(f, "{}", self.to_string()) - /// } - /// } - /// - /// ``` - /// Use instead: - /// ```rust - /// use std::fmt; - /// - /// struct Structure(i32); - /// impl fmt::Display for Structure { - /// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - /// write!(f, "{}", self.0) - /// } - /// } - /// ``` - #[clippy::version = "1.48.0"] - pub TO_STRING_IN_DISPLAY, - correctness, - "`to_string` method used while implementing `Display` trait" -} - -#[derive(Default)] -pub struct ToStringInDisplay { - in_display_impl: bool, - self_hir_id: Option, -} - -impl ToStringInDisplay { - pub fn new() -> Self { - Self { - in_display_impl: false, - self_hir_id: None, - } - } -} - -impl_lint_pass!(ToStringInDisplay => [TO_STRING_IN_DISPLAY]); - -impl LateLintPass<'_> for ToStringInDisplay { - fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { - if is_display_impl(cx, item) { - self.in_display_impl = true; - } - } - - fn check_item_post(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { - if is_display_impl(cx, item) { - self.in_display_impl = false; - self.self_hir_id = None; - } - } - - fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &ImplItem<'_>) { - if_chain! { - if self.in_display_impl; - if let ImplItemKind::Fn(.., body_id) = &impl_item.kind; - let body = cx.tcx.hir().body(*body_id); - if !body.params.is_empty(); - then { - let self_param = &body.params[0]; - self.self_hir_id = Some(self_param.pat.hir_id); - } - } - } - - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - if_chain! { - if self.in_display_impl; - if let Some(self_hir_id) = self.self_hir_id; - if let ExprKind::MethodCall(path, _, [ref self_arg, ..], _) = expr.kind; - if path.ident.name == sym!(to_string); - if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - if is_diag_trait_item(cx, expr_def_id, sym::ToString); - if path_to_local_id(self_arg, self_hir_id); - then { - span_lint( - cx, - TO_STRING_IN_DISPLAY, - expr.span, - "using `to_string` in `fmt::Display` implementation might lead to infinite recursion", - ); - } - } - } -} - -fn is_display_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool { - if_chain! { - if let ItemKind::Impl(Impl { of_trait: Some(trait_ref), .. }) = &item.kind; - if let Some(did) = trait_ref.trait_def_id(); - then { - match_def_path(cx, did, &paths::DISPLAY_TRAIT) - } else { - false - } - } -} diff --git a/clippy_utils/src/macros.rs b/clippy_utils/src/macros.rs index a3e336d701ca..b64e46f08d2f 100644 --- a/clippy_utils/src/macros.rs +++ b/clippy_utils/src/macros.rs @@ -1,6 +1,7 @@ #![allow(clippy::similar_names)] // `expr` and `expn` use crate::visitors::expr_visitor_no_bodies; +use crate::{match_def_path, paths}; use arrayvec::ArrayVec; use if_chain::if_chain; @@ -13,6 +14,31 @@ use rustc_span::hygiene::{MacroKind, SyntaxContext}; use rustc_span::{sym, ExpnData, ExpnId, ExpnKind, Span, Symbol}; use std::ops::ControlFlow; +const FORMAT_MACRO_PATHS: &[&[&str]] = &[ + &paths::FORMAT_ARGS_MACRO, + &paths::ASSERT_EQ_MACRO, + &paths::ASSERT_MACRO, + &paths::ASSERT_NE_MACRO, + &paths::EPRINT_MACRO, + &paths::EPRINTLN_MACRO, + &paths::PRINT_MACRO, + &paths::PRINTLN_MACRO, + &paths::WRITE_MACRO, + &paths::WRITELN_MACRO, +]; + +const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[sym::format_macro, sym::std_panic_macro]; + +/// Returns true if a given Macro `DefId` is a format macro (e.g. `println!`) +pub fn is_format_macro(cx: &LateContext<'_>, macro_def_id: DefId) -> bool { + FORMAT_MACRO_PATHS + .iter() + .any(|path| match_def_path(cx, macro_def_id, path)) + || FORMAT_MACRO_DIAG_ITEMS + .iter() + .any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, macro_def_id)) +} + /// A macro call, like `vec![1, 2, 3]`. /// /// Use `tcx.item_name(macro_call.def_id)` to get the macro name. diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 288c56e9fd73..b2d5320b2823 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -36,6 +36,7 @@ pub const CMP_MAX: [&str; 3] = ["core", "cmp", "max"]; pub const CMP_MIN: [&str; 3] = ["core", "cmp", "min"]; pub const COW: [&str; 3] = ["alloc", "borrow", "Cow"]; pub const CSTRING_AS_C_STR: [&str; 5] = ["std", "ffi", "c_str", "CString", "as_c_str"]; +pub const DEBUG_TRAIT: [&str; 3] = ["core", "fmt", "Debug"]; pub const DEFAULT_TRAIT_METHOD: [&str; 4] = ["core", "default", "Default", "default"]; pub const DEREF_MUT_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "DerefMut", "deref_mut"]; /// Preferably use the diagnostic item `sym::deref_method` where possible diff --git a/tests/ui/recursive_format_impl.rs b/tests/ui/recursive_format_impl.rs new file mode 100644 index 000000000000..9241bf7ed740 --- /dev/null +++ b/tests/ui/recursive_format_impl.rs @@ -0,0 +1,321 @@ +#![warn(clippy::recursive_format_impl)] +#![allow( + clippy::inherent_to_string_shadow_display, + clippy::to_string_in_format_args, + clippy::deref_addrof +)] + +use std::fmt; + +struct A; +impl A { + fn fmt(&self) { + self.to_string(); + } +} + +trait B { + fn fmt(&self) {} +} + +impl B for A { + fn fmt(&self) { + self.to_string(); + } +} + +impl fmt::Display for A { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.to_string()) + } +} + +fn fmt(a: A) { + a.to_string(); +} + +struct C; + +impl C { + // Doesn't trigger if to_string defined separately + // i.e. not using ToString trait (from Display) + fn to_string(&self) -> String { + String::from("I am C") + } +} + +impl fmt::Display for C { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.to_string()) + } +} + +enum D { + E(String), + F, +} + +impl std::fmt::Display for D { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self { + Self::E(string) => write!(f, "E {}", string.to_string()), + Self::F => write!(f, "F"), + } + } +} + +// Check for use of self as Display, in Display impl +// Triggers on direct use of self +struct G {} + +impl std::fmt::Display for G { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self) + } +} + +// Triggers on reference to self +struct H {} + +impl std::fmt::Display for H { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", &self) + } +} + +impl std::fmt::Debug for H { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", &self) + } +} + +// Triggers on multiple reference to self +struct H2 {} + +impl std::fmt::Display for H2 { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", &&&self) + } +} + +// Doesn't trigger on correct deref +struct I {} + +impl std::ops::Deref for I { + type Target = str; + + fn deref(&self) -> &Self::Target { + "test" + } +} + +impl std::fmt::Display for I { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}", &**self) + } +} + +impl std::fmt::Debug for I { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", &**self) + } +} + +// Doesn't trigger on multiple correct deref +struct I2 {} + +impl std::ops::Deref for I2 { + type Target = str; + + fn deref(&self) -> &Self::Target { + "test" + } +} + +impl std::fmt::Display for I2 { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}", **&&&**self) + } +} + +// Doesn't trigger on multiple correct deref +struct I3 {} + +impl std::ops::Deref for I3 { + type Target = str; + + fn deref(&self) -> &Self::Target { + "test" + } +} + +impl std::fmt::Display for I3 { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}", &&**&&&**self) + } +} + +// Does trigger when deref resolves to self +struct J {} + +impl std::ops::Deref for J { + type Target = str; + + fn deref(&self) -> &Self::Target { + "test" + } +} + +impl std::fmt::Display for J { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}", &*self) + } +} + +impl std::fmt::Debug for J { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", &*self) + } +} + +struct J2 {} + +impl std::ops::Deref for J2 { + type Target = str; + + fn deref(&self) -> &Self::Target { + "test" + } +} + +impl std::fmt::Display for J2 { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}", *self) + } +} + +struct J3 {} + +impl std::ops::Deref for J3 { + type Target = str; + + fn deref(&self) -> &Self::Target { + "test" + } +} + +impl std::fmt::Display for J3 { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}", **&&*self) + } +} + +struct J4 {} + +impl std::ops::Deref for J4 { + type Target = str; + + fn deref(&self) -> &Self::Target { + "test" + } +} + +impl std::fmt::Display for J4 { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}", &&**&&*self) + } +} + +// Doesn't trigger on Debug from Display +struct K {} + +impl std::fmt::Debug for K { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "test") + } +} + +impl std::fmt::Display for K { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +// Doesn't trigger on Display from Debug +struct K2 {} + +impl std::fmt::Debug for K2 { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}", self) + } +} + +impl std::fmt::Display for K2 { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "test") + } +} + +// Doesn't trigger on struct fields +struct L { + field1: u32, + field2: i32, +} + +impl std::fmt::Display for L { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{},{}", self.field1, self.field2) + } +} + +impl std::fmt::Debug for L { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?},{:?}", self.field1, self.field2) + } +} + +// Doesn't trigger on nested enum matching +enum Tree { + Leaf, + Node(Vec), +} + +impl std::fmt::Display for Tree { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Tree::Leaf => write!(f, "*"), + Tree::Node(children) => { + write!(f, "(")?; + for child in children.iter() { + write!(f, "{},", child)?; + } + write!(f, ")") + }, + } + } +} + +impl std::fmt::Debug for Tree { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Tree::Leaf => write!(f, "*"), + Tree::Node(children) => { + write!(f, "(")?; + for child in children.iter() { + write!(f, "{:?},", child)?; + } + write!(f, ")") + }, + } + } +} + +fn main() { + let a = A; + a.to_string(); + a.fmt(); + fmt(a); + + let c = C; + c.to_string(); +} diff --git a/tests/ui/recursive_format_impl.stderr b/tests/ui/recursive_format_impl.stderr new file mode 100644 index 000000000000..1a717ac92d8b --- /dev/null +++ b/tests/ui/recursive_format_impl.stderr @@ -0,0 +1,82 @@ +error: using `self.to_string` in `fmt::Display` implementation will cause infinite recursion + --> $DIR/recursive_format_impl.rs:29:25 + | +LL | write!(f, "{}", self.to_string()) + | ^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::recursive-format-impl` implied by `-D warnings` + +error: using `self` as `Display` in `impl Display` will cause infinite recursion + --> $DIR/recursive_format_impl.rs:73:9 + | +LL | write!(f, "{}", self) + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: using `self` as `Display` in `impl Display` will cause infinite recursion + --> $DIR/recursive_format_impl.rs:82:9 + | +LL | write!(f, "{}", &self) + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: using `self` as `Debug` in `impl Debug` will cause infinite recursion + --> $DIR/recursive_format_impl.rs:88:9 + | +LL | write!(f, "{:?}", &self) + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: using `self` as `Display` in `impl Display` will cause infinite recursion + --> $DIR/recursive_format_impl.rs:97:9 + | +LL | write!(f, "{}", &&&self) + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: using `self` as `Display` in `impl Display` will cause infinite recursion + --> $DIR/recursive_format_impl.rs:171:9 + | +LL | write!(f, "{}", &*self) + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: using `self` as `Debug` in `impl Debug` will cause infinite recursion + --> $DIR/recursive_format_impl.rs:177:9 + | +LL | write!(f, "{:?}", &*self) + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: using `self` as `Display` in `impl Display` will cause infinite recursion + --> $DIR/recursive_format_impl.rs:193:9 + | +LL | write!(f, "{}", *self) + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: using `self` as `Display` in `impl Display` will cause infinite recursion + --> $DIR/recursive_format_impl.rs:209:9 + | +LL | write!(f, "{}", **&&*self) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: using `self` as `Display` in `impl Display` will cause infinite recursion + --> $DIR/recursive_format_impl.rs:225:9 + | +LL | write!(f, "{}", &&**&&*self) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 10 previous errors + diff --git a/tests/ui/rename.fixed b/tests/ui/rename.fixed index 8bddec576ed1..24a0c8122919 100644 --- a/tests/ui/rename.fixed +++ b/tests/ui/rename.fixed @@ -20,6 +20,7 @@ #![allow(clippy::match_result_ok)] #![allow(clippy::disallowed_types)] #![allow(clippy::disallowed_methods)] +#![allow(clippy::recursive_format_impl)] // uplifted lints #![allow(invalid_value)] #![allow(array_into_iter)] @@ -55,6 +56,7 @@ #![warn(clippy::disallowed_types)] #![warn(clippy::disallowed_methods)] #![warn(clippy::needless_borrow)] +#![warn(clippy::recursive_format_impl)] // uplifted lints #![warn(invalid_value)] #![warn(array_into_iter)] diff --git a/tests/ui/rename.rs b/tests/ui/rename.rs index d2010d71d2c1..ea64234c680d 100644 --- a/tests/ui/rename.rs +++ b/tests/ui/rename.rs @@ -20,6 +20,7 @@ #![allow(clippy::match_result_ok)] #![allow(clippy::disallowed_types)] #![allow(clippy::disallowed_methods)] +#![allow(clippy::recursive_format_impl)] // uplifted lints #![allow(invalid_value)] #![allow(array_into_iter)] @@ -55,6 +56,7 @@ #![warn(clippy::disallowed_type)] #![warn(clippy::disallowed_method)] #![warn(clippy::ref_in_deref)] +#![warn(clippy::to_string_in_display)] // uplifted lints #![warn(clippy::invalid_ref)] #![warn(clippy::into_iter_on_array)] diff --git a/tests/ui/rename.stderr b/tests/ui/rename.stderr index 45cb8b786f5f..8b132a783847 100644 --- a/tests/ui/rename.stderr +++ b/tests/ui/rename.stderr @@ -1,5 +1,5 @@ error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` - --> $DIR/rename.rs:34:9 + --> $DIR/rename.rs:35:9 | LL | #![warn(clippy::stutter)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` @@ -7,196 +7,202 @@ LL | #![warn(clippy::stutter)] = note: `-D renamed-and-removed-lints` implied by `-D warnings` error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` - --> $DIR/rename.rs:35:9 + --> $DIR/rename.rs:36:9 | LL | #![warn(clippy::new_without_default_derive)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` - --> $DIR/rename.rs:36:9 + --> $DIR/rename.rs:37:9 | LL | #![warn(clippy::const_static_lifetime)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes` error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` - --> $DIR/rename.rs:37:9 + --> $DIR/rename.rs:38:9 | LL | #![warn(clippy::cyclomatic_complexity)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` - --> $DIR/rename.rs:38:9 + --> $DIR/rename.rs:39:9 | LL | #![warn(clippy::option_and_then_some)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` - --> $DIR/rename.rs:39:9 + --> $DIR/rename.rs:40:9 | LL | #![warn(clippy::box_vec)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions` - --> $DIR/rename.rs:40:9 + --> $DIR/rename.rs:41:9 | LL | #![warn(clippy::block_in_if_condition_expr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions` - --> $DIR/rename.rs:41:9 + --> $DIR/rename.rs:42:9 | LL | #![warn(clippy::block_in_if_condition_stmt)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:42:9 + --> $DIR/rename.rs:43:9 | LL | #![warn(clippy::option_map_unwrap_or)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:43:9 + --> $DIR/rename.rs:44:9 | LL | #![warn(clippy::option_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:44:9 + --> $DIR/rename.rs:45:9 | LL | #![warn(clippy::result_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` - --> $DIR/rename.rs:45:9 + --> $DIR/rename.rs:46:9 | LL | #![warn(clippy::option_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` - --> $DIR/rename.rs:46:9 + --> $DIR/rename.rs:47:9 | LL | #![warn(clippy::result_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used` - --> $DIR/rename.rs:47:9 + --> $DIR/rename.rs:48:9 | LL | #![warn(clippy::option_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` - --> $DIR/rename.rs:48:9 + --> $DIR/rename.rs:49:9 | LL | #![warn(clippy::result_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::for_loop_over_option` has been renamed to `clippy::for_loops_over_fallibles` - --> $DIR/rename.rs:49:9 + --> $DIR/rename.rs:50:9 | LL | #![warn(clippy::for_loop_over_option)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles` error: lint `clippy::for_loop_over_result` has been renamed to `clippy::for_loops_over_fallibles` - --> $DIR/rename.rs:50:9 + --> $DIR/rename.rs:51:9 | LL | #![warn(clippy::for_loop_over_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles` error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` - --> $DIR/rename.rs:51:9 + --> $DIR/rename.rs:52:9 | LL | #![warn(clippy::identity_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` - --> $DIR/rename.rs:52:9 + --> $DIR/rename.rs:53:9 | LL | #![warn(clippy::zero_width_space)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` - --> $DIR/rename.rs:53:9 + --> $DIR/rename.rs:54:9 | LL | #![warn(clippy::single_char_push_str)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` - --> $DIR/rename.rs:54:9 + --> $DIR/rename.rs:55:9 | LL | #![warn(clippy::if_let_some_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` - --> $DIR/rename.rs:55:9 + --> $DIR/rename.rs:56:9 | LL | #![warn(clippy::disallowed_type)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` - --> $DIR/rename.rs:56:9 + --> $DIR/rename.rs:57:9 | LL | #![warn(clippy::disallowed_method)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` - --> $DIR/rename.rs:57:9 + --> $DIR/rename.rs:58:9 | LL | #![warn(clippy::ref_in_deref)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` -error: lint `clippy::invalid_ref` has been renamed to `invalid_value` +error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` --> $DIR/rename.rs:59:9 | +LL | #![warn(clippy::to_string_in_display)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` + +error: lint `clippy::invalid_ref` has been renamed to `invalid_value` + --> $DIR/rename.rs:61:9 + | LL | #![warn(clippy::invalid_ref)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` - --> $DIR/rename.rs:60:9 + --> $DIR/rename.rs:62:9 | LL | #![warn(clippy::into_iter_on_array)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` error: lint `clippy::unused_label` has been renamed to `unused_labels` - --> $DIR/rename.rs:61:9 + --> $DIR/rename.rs:63:9 | LL | #![warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` - --> $DIR/rename.rs:62:9 + --> $DIR/rename.rs:64:9 | LL | #![warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr` - --> $DIR/rename.rs:63:9 + --> $DIR/rename.rs:65:9 | LL | #![warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr` error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` - --> $DIR/rename.rs:64:9 + --> $DIR/rename.rs:66:9 | LL | #![warn(clippy::panic_params)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` - --> $DIR/rename.rs:65:9 + --> $DIR/rename.rs:67:9 | LL | #![warn(clippy::unknown_clippy_lints)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` - --> $DIR/rename.rs:66:9 + --> $DIR/rename.rs:68:9 | LL | #![warn(clippy::invalid_atomic_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` - --> $DIR/rename.rs:67:9 + --> $DIR/rename.rs:69:9 | LL | #![warn(clippy::mem_discriminant_non_enum)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` -error: aborting due to 33 previous errors +error: aborting due to 34 previous errors diff --git a/tests/ui/to_string_in_display.rs b/tests/ui/to_string_in_display.rs deleted file mode 100644 index 3ccdcd1117b5..000000000000 --- a/tests/ui/to_string_in_display.rs +++ /dev/null @@ -1,69 +0,0 @@ -#![warn(clippy::to_string_in_display)] -#![allow(clippy::inherent_to_string_shadow_display, clippy::to_string_in_format_args)] - -use std::fmt; - -struct A; -impl A { - fn fmt(&self) { - self.to_string(); - } -} - -trait B { - fn fmt(&self) {} -} - -impl B for A { - fn fmt(&self) { - self.to_string(); - } -} - -impl fmt::Display for A { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.to_string()) - } -} - -fn fmt(a: A) { - a.to_string(); -} - -struct C; - -impl C { - fn to_string(&self) -> String { - String::from("I am C") - } -} - -impl fmt::Display for C { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.to_string()) - } -} - -enum D { - E(String), - F, -} - -impl std::fmt::Display for D { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match &self { - Self::E(string) => write!(f, "E {}", string.to_string()), - Self::F => write!(f, "F"), - } - } -} - -fn main() { - let a = A; - a.to_string(); - a.fmt(); - fmt(a); - - let c = C; - c.to_string(); -} diff --git a/tests/ui/to_string_in_display.stderr b/tests/ui/to_string_in_display.stderr deleted file mode 100644 index 5f26ef413e23..000000000000 --- a/tests/ui/to_string_in_display.stderr +++ /dev/null @@ -1,10 +0,0 @@ -error: using `to_string` in `fmt::Display` implementation might lead to infinite recursion - --> $DIR/to_string_in_display.rs:25:25 - | -LL | write!(f, "{}", self.to_string()) - | ^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::to-string-in-display` implied by `-D warnings` - -error: aborting due to previous error -