diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index d847e4c749481..044cbff4b78e5 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -3800,6 +3800,7 @@ Released 2018-09-13 [`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements [`iter_cloned_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_cloned_collect [`iter_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_count +[`iter_kv_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_kv_map [`iter_next_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_loop [`iter_next_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_slice [`iter_not_returning_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_not_returning_iterator diff --git a/src/tools/clippy/CONTRIBUTING.md b/src/tools/clippy/CONTRIBUTING.md index 28b4cfd5f0995..6c977b2cacab5 100644 --- a/src/tools/clippy/CONTRIBUTING.md +++ b/src/tools/clippy/CONTRIBUTING.md @@ -110,23 +110,28 @@ Just make sure to remove the dependencies again before finally making a pull req [IntelliJ_rust_homepage]: https://intellij-rust.github.io/ ### Rust Analyzer -As of [#6869][6869], [`rust-analyzer`][ra_homepage] can understand that Clippy uses compiler-internals -using `extern crate` when `package.metadata.rust-analyzer.rustc_private` is set to `true` in Clippy's `Cargo.toml.` -You will require a `nightly` toolchain with the `rustc-dev` component installed. -Make sure that in the `rust-analyzer` configuration, you set +For [`rust-analyzer`][ra_homepage] to work correctly make sure that in the `rust-analyzer` configuration you set + ```json { "rust-analyzer.rustc.source": "discover" } ``` -and -```json -{ "rust-analyzer.updates.channel": "nightly" } -``` + You should be able to see information on things like `Expr` or `EarlyContext` now if you hover them, also a lot more type hints. -This will work with `rust-analyzer 2021-03-15` shipped in nightly `1.52.0-nightly (107896c32 2021-03-15)` or later. + +To have `rust-analyzer` also work in the `clippy_dev` and `lintcheck` crates, add the following configuration + +```json +{ + "rust-analyzer.linkedProjects": [ + "./Cargo.toml", + "clippy_dev/Cargo.toml", + "lintcheck/Cargo.toml", + ] +} +``` [ra_homepage]: https://rust-analyzer.github.io/ -[6869]: https://github.com/rust-lang/rust-clippy/pull/6869 ## How Clippy works diff --git a/src/tools/clippy/book/src/development/adding_lints.md b/src/tools/clippy/book/src/development/adding_lints.md index da781eb970df7..b1e843bc7f4c8 100644 --- a/src/tools/clippy/book/src/development/adding_lints.md +++ b/src/tools/clippy/book/src/development/adding_lints.md @@ -90,6 +90,7 @@ We start by opening the test file created at `tests/ui/foo_functions.rs`. Update the file with some examples to get started: ```rust +#![allow(unused)] #![warn(clippy::foo_functions)] // Impl methods diff --git a/src/tools/clippy/book/src/development/common_tools_writing_lints.md b/src/tools/clippy/book/src/development/common_tools_writing_lints.md index 2bc275ceff0be..f5aa06e4bf653 100644 --- a/src/tools/clippy/book/src/development/common_tools_writing_lints.md +++ b/src/tools/clippy/book/src/development/common_tools_writing_lints.md @@ -123,7 +123,8 @@ There are three ways to do this, depending on if the target trait has a diagnostic item, lang item or neither. ```rust -use clippy_utils::{implements_trait, is_trait_method, match_trait_method, paths}; +use clippy_utils::ty::implements_trait; +use clippy_utils::is_trait_method; use rustc_span::symbol::sym; impl LateLintPass<'_> for MyStructLint { @@ -143,13 +144,6 @@ impl LateLintPass<'_> for MyStructLint { .map_or(false, |id| implements_trait(cx, ty, id, &[])) { // `expr` implements `Drop` trait } - - // 3. Using the type path with the expression - // we use `match_trait_method` function from Clippy's utils - // (This method should be avoided if possible) - if match_trait_method(cx, expr, &paths::INTO) { - // `expr` implements `Into` trait - } } } ``` @@ -233,8 +227,9 @@ functions to deal with macros: crates ```rust - #[macro_use] - extern crate a_crate_with_macros; + use rustc_middle::lint::in_external_macro; + + use a_crate_with_macros::foo; // `foo` is defined in `a_crate_with_macros` foo!("bar"); diff --git a/src/tools/clippy/clippy_dev/src/new_lint.rs b/src/tools/clippy/clippy_dev/src/new_lint.rs index 331b76484b8a5..02cb13a1d8afe 100644 --- a/src/tools/clippy/clippy_dev/src/new_lint.rs +++ b/src/tools/clippy/clippy_dev/src/new_lint.rs @@ -188,6 +188,7 @@ pub(crate) fn get_stabilization_version() -> String { fn get_test_file_contents(lint_name: &str, header_commands: Option<&str>) -> String { let mut contents = format!( indoc! {" + #![allow(unused)] #![warn(clippy::{})] fn main() {{ diff --git a/src/tools/clippy/clippy_lints/src/almost_complete_letter_range.rs b/src/tools/clippy/clippy_lints/src/almost_complete_letter_range.rs index 59a7c53540069..073e4af1318e3 100644 --- a/src/tools/clippy/clippy_lints/src/almost_complete_letter_range.rs +++ b/src/tools/clippy/clippy_lints/src/almost_complete_letter_range.rs @@ -4,6 +4,7 @@ use clippy_utils::{meets_msrv, msrvs}; use rustc_ast::ast::{Expr, ExprKind, LitKind, Pat, PatKind, RangeEnd, RangeLimits}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; @@ -79,6 +80,7 @@ fn check_range(cx: &EarlyContext<'_>, span: Span, start: &Expr, end: &Expr, sugg (LitKind::Byte(b'a') | LitKind::Char('a'), LitKind::Byte(b'z') | LitKind::Char('z')) | (LitKind::Byte(b'A') | LitKind::Char('A'), LitKind::Byte(b'Z') | LitKind::Char('Z')) ) + && !in_external_macro(cx.sess(), span) { span_lint_and_then( cx, diff --git a/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs b/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs index 7cd198ace86c0..656dc5feeb570 100644 --- a/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs +++ b/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::{find_assert_args, root_macro_call_first_node, PanicExpn}; -use clippy_utils::path_res; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::{implements_trait, is_copy, is_type_diagnostic_item}; +use clippy_utils::ty::{has_debug_impl, is_copy, is_type_diagnostic_item}; use clippy_utils::usage::local_used_after_expr; +use clippy_utils::{is_expr_final_block_expr, path_res}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{Expr, ExprKind}; @@ -58,6 +58,7 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates { return; } } + let semicolon = if is_expr_final_block_expr(cx.tcx, e) {";"} else {""}; let mut app = Applicability::MachineApplicable; match method_segment.ident.as_str() { "is_ok" if type_suitable_to_unwrap(cx, substs.type_at(1)) => { @@ -68,8 +69,9 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates { "called `assert!` with `Result::is_ok`", "replace with", format!( - "{}.unwrap()", - snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0 + "{}.unwrap(){}", + snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0, + semicolon ), app, ); @@ -82,8 +84,9 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates { "called `assert!` with `Result::is_err`", "replace with", format!( - "{}.unwrap_err()", - snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0 + "{}.unwrap_err(){}", + snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0, + semicolon ), app, ); @@ -94,13 +97,6 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates { } } -/// This checks whether a given type is known to implement Debug. -fn has_debug_impl<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { - cx.tcx - .get_diagnostic_item(sym::Debug) - .map_or(false, |debug| implements_trait(cx, ty, debug, &[])) -} - fn type_suitable_to_unwrap<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { has_debug_impl(cx, ty) && !ty.is_unit() && !ty.is_never() } diff --git a/src/tools/clippy/clippy_lints/src/bool_to_int_with_if.rs b/src/tools/clippy/clippy_lints/src/bool_to_int_with_if.rs index a4b8cbb0d82aa..51e98cda84519 100644 --- a/src/tools/clippy/clippy_lints/src/bool_to_int_with_if.rs +++ b/src/tools/clippy/clippy_lints/src/bool_to_int_with_if.rs @@ -1,9 +1,9 @@ -use rustc_ast::{ExprPrecedence, LitKind}; +use rustc_ast::LitKind; use rustc_hir::{Block, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use clippy_utils::{diagnostics::span_lint_and_then, is_else_clause, source::snippet_block_with_applicability}; +use clippy_utils::{diagnostics::span_lint_and_then, is_else_clause, sugg::Sugg}; use rustc_errors::Applicability; declare_clippy_lint! { @@ -55,27 +55,42 @@ fn check_if_else<'tcx>(ctx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx if let ExprKind::If(check, then, Some(else_)) = expr.kind && let Some(then_lit) = int_literal(then) && let Some(else_lit) = int_literal(else_) - && check_int_literal_equals_val(then_lit, 1) - && check_int_literal_equals_val(else_lit, 0) { + let inverted = if + check_int_literal_equals_val(then_lit, 1) + && check_int_literal_equals_val(else_lit, 0) { + false + } else if + check_int_literal_equals_val(then_lit, 0) + && check_int_literal_equals_val(else_lit, 1) { + true + } else { + // Expression isn't boolean, exit + return; + }; let mut applicability = Applicability::MachineApplicable; - let snippet = snippet_block_with_applicability(ctx, check.span, "..", None, &mut applicability); - let snippet_with_braces = { - let need_parens = should_have_parentheses(check); - let (left_paren, right_paren) = if need_parens {("(", ")")} else {("", "")}; - format!("{left_paren}{snippet}{right_paren}") + let snippet = { + let mut sugg = Sugg::hir_with_applicability(ctx, check, "..", &mut applicability); + if inverted { + sugg = !sugg; + } + sugg }; let ty = ctx.typeck_results().expr_ty(then_lit); // then and else must be of same type let suggestion = { let wrap_in_curly = is_else_clause(ctx.tcx, expr); - let (left_curly, right_curly) = if wrap_in_curly {("{", "}")} else {("", "")}; - format!( - "{left_curly}{ty}::from({snippet}){right_curly}" - ) + let mut s = Sugg::NonParen(format!("{ty}::from({snippet})").into()); + if wrap_in_curly { + s = s.blockify(); + } + s }; // when used in else clause if statement should be wrapped in curly braces + let into_snippet = snippet.clone().maybe_par(); + let as_snippet = snippet.as_ty(ty); + span_lint_and_then(ctx, BOOL_TO_INT_WITH_IF, expr.span, @@ -87,7 +102,7 @@ fn check_if_else<'tcx>(ctx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx suggestion, applicability, ); - diag.note(format!("`{snippet_with_braces} as {ty}` or `{snippet_with_braces}.into()` can also be valid options")); + diag.note(format!("`{as_snippet}` or `{into_snippet}.into()` can also be valid options")); }); }; } @@ -119,7 +134,3 @@ fn check_int_literal_equals_val<'tcx>(expr: &'tcx rustc_hir::Expr<'tcx>, expecte false } } - -fn should_have_parentheses<'tcx>(check: &'tcx rustc_hir::Expr<'tcx>) -> bool { - check.precedence().order() < ExprPrecedence::Cast.order() -} diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs index 656d639f0efd9..03d262d5a59c6 100644 --- a/src/tools/clippy/clippy_lints/src/booleans.rs +++ b/src/tools/clippy/clippy_lints/src/booleans.rs @@ -237,7 +237,7 @@ impl<'a, 'tcx, 'v> SuggestContext<'a, 'tcx, 'v> { } }, &Term(n) => { - let snip = snippet_opt(self.cx, self.terminals[n as usize].span)?; + let snip = snippet_opt(self.cx, self.terminals[n as usize].span.source_callsite())?; self.output.push_str(&snip); }, } diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index 45b5d79d44522..501f9ef78aebe 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -297,13 +297,10 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing { if !is_lint_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id) && position.lint_explicit_deref() => { + let ty_changed_count = usize::from(!deref_method_same_type(expr_ty, typeck.expr_ty(sub_expr))); self.state = Some(( State::DerefMethod { - ty_changed_count: if deref_method_same_type(expr_ty, typeck.expr_ty(sub_expr)) { - 0 - } else { - 1 - }, + ty_changed_count, is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)), target_mut, }, diff --git a/src/tools/clippy/clippy_lints/src/derivable_impls.rs b/src/tools/clippy/clippy_lints/src/derivable_impls.rs index ef9eeecc6a934..06ae5abeaeb9b 100644 --- a/src/tools/clippy/clippy_lints/src/derivable_impls.rs +++ b/src/tools/clippy/clippy_lints/src/derivable_impls.rs @@ -1,5 +1,6 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::{is_default_equivalent, peel_blocks}; +use rustc_errors::Applicability; use rustc_hir::{ def::{DefKind, Res}, Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, TyKind, @@ -100,15 +101,28 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls { ExprKind::Struct(_, fields, _) => fields.iter().all(|ef| is_default_equivalent(cx, ef.expr)), _ => false, }; + if should_emit { - let path_string = cx.tcx.def_path_str(adt_def.did()); - span_lint_and_help( + let struct_span = cx.tcx.def_span(adt_def.did()); + span_lint_and_then( cx, DERIVABLE_IMPLS, item.span, "this `impl` can be derived", - None, - &format!("try annotating `{}` with `#[derive(Default)]`", path_string), + |diag| { + diag.span_suggestion_hidden( + item.span, + "remove the manual implementation...", + String::new(), + Applicability::MachineApplicable + ); + diag.span_suggestion( + struct_span.shrink_to_lo(), + "...and instead derive it", + "#[derive(Default)]\n".to_string(), + Applicability::MachineApplicable + ); + } ); } } diff --git a/src/tools/clippy/clippy_lints/src/format.rs b/src/tools/clippy/clippy_lints/src/format.rs index 0c5851cdbed2a..f10d825695360 100644 --- a/src/tools/clippy/clippy_lints/src/format.rs +++ b/src/tools/clippy/clippy_lints/src/format.rs @@ -71,12 +71,12 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat { let value = arg.param.value; if_chain! { if format_args.format_string.parts == [kw::Empty]; + if arg.format.is_default(); if match cx.typeck_results().expr_ty(value).peel_refs().kind() { ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(sym::String, adt.did()), ty::Str => true, _ => false, }; - if !arg.format.has_string_formatting(); then { let is_new_string = match value.kind { ExprKind::Binary(..) => true, diff --git a/src/tools/clippy/clippy_lints/src/format_args.rs b/src/tools/clippy/clippy_lints/src/format_args.rs index 2a55c48cf7731..9e1eaf248b73c 100644 --- a/src/tools/clippy/clippy_lints/src/format_args.rs +++ b/src/tools/clippy/clippy_lints/src/format_args.rs @@ -77,7 +77,7 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs { if let ExpnKind::Macro(_, name) = outermost_expn_data.kind; then { for arg in &format_args.args { - if arg.format.has_string_formatting() { + if !arg.format.is_default() { continue; } if is_aliased(&format_args, arg.param.value.hir_id) { diff --git a/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs b/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs index 0acbd81aec343..5857d81ab1f20 100644 --- a/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs +++ b/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::snippet; -use if_chain::if_chain; -use rustc_hir::{Expr, ExprKind}; +use rustc_hir::{Expr, ExprKind, Item, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, ConstKind}; @@ -39,29 +38,28 @@ impl_lint_pass!(LargeStackArrays => [LARGE_STACK_ARRAYS]); impl<'tcx> LateLintPass<'tcx> for LargeStackArrays { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - if_chain! { - if let ExprKind::Repeat(_, _) = expr.kind; - if let ty::Array(element_type, cst) = cx.typeck_results().expr_ty(expr).kind(); - if let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind(); - if let Ok(element_count) = element_count.try_to_machine_usize(cx.tcx); - if let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes()); - if self.maximum_allowed_size < element_count * element_size; - then { - span_lint_and_help( - cx, - LARGE_STACK_ARRAYS, - expr.span, - &format!( - "allocating a local array larger than {} bytes", - self.maximum_allowed_size - ), - None, - &format!( - "consider allocating on the heap with `vec!{}.into_boxed_slice()`", - snippet(cx, expr.span, "[...]") - ), - ); - } - } + if let ExprKind::Repeat(_, _) = expr.kind + && let ty::Array(element_type, cst) = cx.typeck_results().expr_ty(expr).kind() + && let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind() + && let Ok(element_count) = element_count.try_to_machine_usize(cx.tcx) + && let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes()) + && !cx.tcx.hir().parent_iter(expr.hir_id) + .any(|(_, node)| matches!(node, Node::Item(Item { kind: ItemKind::Static(..), .. }))) + && self.maximum_allowed_size < element_count * element_size { + span_lint_and_help( + cx, + LARGE_STACK_ARRAYS, + expr.span, + &format!( + "allocating a local array larger than {} bytes", + self.maximum_allowed_size + ), + None, + &format!( + "consider allocating on the heap with `vec!{}.into_boxed_slice()`", + snippet(cx, expr.span, "[...]") + ), + ); + } } } diff --git a/src/tools/clippy/clippy_lints/src/lib.register_all.rs b/src/tools/clippy/clippy_lints/src/lib.register_all.rs index 59d1760fe6064..8718d5fa1da0b 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_all.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_all.rs @@ -171,6 +171,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(methods::ITERATOR_STEP_BY_ZERO), LintId::of(methods::ITER_CLONED_COLLECT), LintId::of(methods::ITER_COUNT), + LintId::of(methods::ITER_KV_MAP), LintId::of(methods::ITER_NEXT_SLICE), LintId::of(methods::ITER_NTH), LintId::of(methods::ITER_NTH_ZERO), @@ -351,7 +352,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(useless_conversion::USELESS_CONVERSION), LintId::of(vec::USELESS_VEC), LintId::of(vec_init_then_push::VEC_INIT_THEN_PUSH), - LintId::of(write::POSITIONAL_NAMED_FORMAT_PARAMETERS), LintId::of(write::PRINTLN_EMPTY_STRING), LintId::of(write::PRINT_LITERAL), LintId::of(write::PRINT_WITH_NEWLINE), diff --git a/src/tools/clippy/clippy_lints/src/lib.register_complexity.rs b/src/tools/clippy/clippy_lints/src/lib.register_complexity.rs index aa247352f88fb..185189a6af5b6 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_complexity.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_complexity.rs @@ -40,6 +40,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(methods::GET_LAST_WITH_LEN), LintId::of(methods::INSPECT_FOR_EACH), LintId::of(methods::ITER_COUNT), + LintId::of(methods::ITER_KV_MAP), LintId::of(methods::MANUAL_FILTER_MAP), LintId::of(methods::MANUAL_FIND_MAP), LintId::of(methods::MANUAL_SPLIT_ONCE), diff --git a/src/tools/clippy/clippy_lints/src/lib.register_lints.rs b/src/tools/clippy/clippy_lints/src/lib.register_lints.rs index 962e67220069a..02fcc8de50727 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_lints.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_lints.rs @@ -313,6 +313,7 @@ store.register_lints(&[ methods::ITERATOR_STEP_BY_ZERO, methods::ITER_CLONED_COLLECT, methods::ITER_COUNT, + methods::ITER_KV_MAP, methods::ITER_NEXT_SLICE, methods::ITER_NTH, methods::ITER_NTH_ZERO, @@ -595,7 +596,6 @@ store.register_lints(&[ vec_init_then_push::VEC_INIT_THEN_PUSH, wildcard_imports::ENUM_GLOB_USE, wildcard_imports::WILDCARD_IMPORTS, - write::POSITIONAL_NAMED_FORMAT_PARAMETERS, write::PRINTLN_EMPTY_STRING, write::PRINT_LITERAL, write::PRINT_STDERR, diff --git a/src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs b/src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs index bede91f183e79..6125d0f7a8628 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs @@ -35,5 +35,4 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec! LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), LintId::of(swap_ptr_to_ref::SWAP_PTR_TO_REF), - LintId::of(write::POSITIONAL_NAMED_FORMAT_PARAMETERS), ]) diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index ceaaf5c6d6ed6..298566cb5b625 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -40,7 +40,6 @@ extern crate rustc_lint; extern crate rustc_middle; extern crate rustc_mir_dataflow; extern crate rustc_parse; -extern crate rustc_parse_format; extern crate rustc_session; extern crate rustc_span; extern crate rustc_target; @@ -425,7 +424,6 @@ pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, sess: &Se }) }); - store.register_pre_expansion_pass(|| Box::new(write::Write::default())); store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes { msrv })); } @@ -524,7 +522,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: #[cfg(feature = "internal")] { if std::env::var("ENABLE_METADATA_COLLECTION").eq(&Ok("1".to_string())) { - store.register_late_pass(|| Box::new(utils::internal_lints::metadata_collector::MetadataCollector::new())); + store.register_late_pass(|_| Box::new(utils::internal_lints::metadata_collector::MetadataCollector::new())); return; } } @@ -879,6 +877,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ignore_publish: cargo_ignore_publish, }) }); + store.register_late_pass(|_| Box::new(write::Write::default())); store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef)); store.register_early_pass(|| Box::new(empty_structs_with_brackets::EmptyStructsWithBrackets)); store.register_late_pass(|_| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings)); diff --git a/src/tools/clippy/clippy_lints/src/methods/err_expect.rs b/src/tools/clippy/clippy_lints/src/methods/err_expect.rs index 570a1b87358dd..720d9a68c85ee 100644 --- a/src/tools/clippy/clippy_lints/src/methods/err_expect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/err_expect.rs @@ -1,6 +1,6 @@ use super::ERR_EXPECT; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::ty::implements_trait; +use clippy_utils::ty::has_debug_impl; use clippy_utils::{meets_msrv, msrvs, ty::is_type_diagnostic_item}; use rustc_errors::Applicability; use rustc_lint::LateContext; @@ -28,7 +28,7 @@ pub(super) fn check( // Tests if the T type in a `Result` is not None if let Some(data_type) = get_data_type(cx, result_type); // Tests if the T type in a `Result` implements debug - if has_debug_impl(data_type, cx); + if has_debug_impl(cx, data_type); then { span_lint_and_sugg( @@ -51,10 +51,3 @@ fn get_data_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option> { _ => None, } } - -/// Given a type, very if the Debug trait has been impl'd -fn has_debug_impl<'tcx>(ty: Ty<'tcx>, cx: &LateContext<'tcx>) -> bool { - cx.tcx - .get_diagnostic_item(sym::Debug) - .map_or(false, |debug| implements_trait(cx, ty, debug, &[])) -} diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs b/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs new file mode 100644 index 0000000000000..a7eecabd68495 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs @@ -0,0 +1,87 @@ +#![allow(unused_imports)] + +use super::ITER_KV_MAP; +use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::source::{snippet, snippet_with_applicability}; +use clippy_utils::sugg; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::visitors::is_local_used; +use rustc_hir::{BindingAnnotation, Body, BorrowKind, Expr, ExprKind, Mutability, Pat, PatKind}; +use rustc_lint::{LateContext, LintContext}; +use rustc_middle::ty; +use rustc_span::sym; +use rustc_span::Span; + +/// lint use of: +/// - `hashmap.iter().map(|(_, v)| v)` +/// - `hashmap.into_iter().map(|(_, v)| v)` +/// on `HashMaps` and `BTreeMaps` in std + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + map_type: &'tcx str, // iter / into_iter + expr: &'tcx Expr<'tcx>, // .iter().map(|(_, v_| v)) + recv: &'tcx Expr<'tcx>, // hashmap + m_arg: &'tcx Expr<'tcx>, // |(_, v)| v +) { + if_chain! { + if !expr.span.from_expansion(); + if let ExprKind::Closure(c) = m_arg.kind; + if let Body {params: [p], value: body_expr, generator_kind: _ } = cx.tcx.hir().body(c.body); + if let PatKind::Tuple([key_pat, val_pat], _) = p.pat.kind; + + let (replacement_kind, binded_ident) = match (&key_pat.kind, &val_pat.kind) { + (key, PatKind::Binding(_, _, value, _)) if pat_is_wild(cx, key, m_arg) => ("value", value), + (PatKind::Binding(_, _, key, _), value) if pat_is_wild(cx, value, m_arg) => ("key", key), + _ => return, + }; + + let ty = cx.typeck_results().expr_ty(recv); + if is_type_diagnostic_item(cx, ty, sym::HashMap) || is_type_diagnostic_item(cx, ty, sym::BTreeMap); + + then { + let mut applicability = rustc_errors::Applicability::MachineApplicable; + let recv_snippet = snippet_with_applicability(cx, recv.span, "map", &mut applicability); + let into_prefix = if map_type == "into_iter" {"into_"} else {""}; + + if_chain! { + if let ExprKind::Path(rustc_hir::QPath::Resolved(_, path)) = body_expr.kind; + if let [local_ident] = path.segments; + if local_ident.ident.as_str() == binded_ident.as_str(); + + then { + span_lint_and_sugg( + cx, + ITER_KV_MAP, + expr.span, + &format!("iterating on a map's {}s", replacement_kind), + "try", + format!("{}.{}{}s()", recv_snippet, into_prefix, replacement_kind), + applicability, + ); + } else { + span_lint_and_sugg( + cx, + ITER_KV_MAP, + expr.span, + &format!("iterating on a map's {}s", replacement_kind), + "try", + format!("{}.{}{}s().map(|{}| {})", recv_snippet, into_prefix, replacement_kind, binded_ident, + snippet_with_applicability(cx, body_expr.span, "/* body */", &mut applicability)), + applicability, + ); + } + } + } + } +} + +/// Returns `true` if the pattern is a `PatWild`, or is an ident prefixed with `_` +/// that is not locally used. +fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: &'tcx Expr<'_>) -> bool { + match *pat { + PatKind::Wild => true, + PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => !is_local_used(cx, body, id), + _ => false, + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index 41942b20ea163..cdde4c54d6378 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -35,6 +35,7 @@ mod into_iter_on_ref; mod is_digit_ascii_radix; mod iter_cloned_collect; mod iter_count; +mod iter_kv_map; mod iter_next_slice; mod iter_nth; mod iter_nth_zero; @@ -3036,6 +3037,37 @@ declare_clippy_lint! { "use of `File::read_to_end` or `File::read_to_string`" } +declare_clippy_lint! { + /// ### What it does + /// + /// Checks for iterating a map (`HashMap` or `BTreeMap`) and + /// ignoring either the keys or values. + /// + /// ### Why is this bad? + /// + /// Readability. There are `keys` and `values` methods that + /// can be used to express that we only need the keys or the values. + /// + /// ### Example + /// + /// ``` + /// # use std::collections::HashMap; + /// let map: HashMap = HashMap::new(); + /// let values = map.iter().map(|(_, value)| value).collect::>(); + /// ``` + /// + /// Use instead: + /// ``` + /// # use std::collections::HashMap; + /// let map: HashMap = HashMap::new(); + /// let values = map.values().collect::>(); + /// ``` + #[clippy::version = "1.65.0"] + pub ITER_KV_MAP, + complexity, + "iterating on map using `iter` when `keys` or `values` would do" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Option, @@ -3159,6 +3191,7 @@ impl_lint_pass!(Methods => [ UNNECESSARY_SORT_BY, VEC_RESIZE_TO_ZERO, VERBOSE_FILE_READS, + ITER_KV_MAP, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -3498,6 +3531,9 @@ impl Methods { (name @ ("map" | "map_err"), [m_arg]) => { if name == "map" { map_clone::check(cx, expr, recv, m_arg, self.msrv); + if let Some((map_name @ ("iter" | "into_iter"), recv2, _, _)) = method_call(recv) { + iter_kv_map::check(cx, map_name, expr, recv2, m_arg); + } } else { map_err_ignore::check(cx, expr, m_arg); } diff --git a/src/tools/clippy/clippy_lints/src/methods/ok_expect.rs b/src/tools/clippy/clippy_lints/src/methods/ok_expect.rs index d64a9f320d90e..646fc4a7bcf37 100644 --- a/src/tools/clippy/clippy_lints/src/methods/ok_expect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/ok_expect.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; +use clippy_utils::ty::{has_debug_impl, is_type_diagnostic_item}; use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::LateContext; @@ -15,7 +15,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); let result_type = cx.typeck_results().expr_ty(recv); if let Some(error_type) = get_error_type(cx, result_type); - if has_debug_impl(error_type, cx); + if has_debug_impl(cx, error_type); then { span_lint_and_help( @@ -37,10 +37,3 @@ fn get_error_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option> { _ => None, } } - -/// This checks whether a given type is known to implement Debug. -fn has_debug_impl<'tcx>(ty: Ty<'tcx>, cx: &LateContext<'tcx>) -> bool { - cx.tcx - .get_diagnostic_item(sym::Debug) - .map_or(false, |debug| implements_trait(cx, ty, debug, &[])) -} diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs index 8d3cede70f70d..79d784c342cac 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -7,7 +7,7 @@ use clippy_utils::visitors::find_all_ret_expressions; use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, return_ty}; use clippy_utils::{meets_msrv, msrvs}; use rustc_errors::Applicability; -use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind, ItemKind, Node}; +use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind, ItemKind, LangItem, Node}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::mir::Mutability; @@ -268,7 +268,7 @@ fn check_other_call_arg<'tcx>( // We can't add an `&` when the trait is `Deref` because `Target = &T` won't match // `Target = T`. if n_refs > 0 || is_copy(cx, receiver_ty) || trait_predicate.def_id() != deref_trait_id; - let n_refs = max(n_refs, if is_copy(cx, receiver_ty) { 0 } else { 1 }); + let n_refs = max(n_refs, usize::from(!is_copy(cx, receiver_ty))); if let Some(receiver_snippet) = snippet_opt(cx, receiver.span); then { span_lint_and_sugg( @@ -379,6 +379,10 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< Node::Expr(parent_expr) => { if let Some((callee_def_id, call_substs, recv, call_args)) = get_callee_substs_and_args(cx, parent_expr) { + if cx.tcx.lang_items().require(LangItem::IntoFutureIntoFuture) == Ok(callee_def_id) { + return false; + } + let fn_sig = cx.tcx.fn_sig(callee_def_id).skip_binder(); if let Some(arg_index) = recv.into_iter().chain(call_args).position(|arg| arg.hir_id == expr.hir_id) && let Some(param_ty) = fn_sig.inputs().get(arg_index) diff --git a/src/tools/clippy/clippy_lints/src/module_style.rs b/src/tools/clippy/clippy_lints/src/module_style.rs index 0a393657267b0..22071ab3044f4 100644 --- a/src/tools/clippy/clippy_lints/src/module_style.rs +++ b/src/tools/clippy/clippy_lints/src/module_style.rs @@ -2,7 +2,7 @@ use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::{FileName, RealFileName, SourceFile, Span, SyntaxContext}; +use rustc_span::{FileName, SourceFile, Span, SyntaxContext}; use std::ffi::OsStr; use std::path::{Component, Path}; @@ -79,7 +79,7 @@ impl EarlyLintPass for ModStyle { let files = cx.sess().source_map().files(); - let RealFileName::LocalPath(trim_to_src) = &cx.sess().opts.working_dir else { return }; + let Some(trim_to_src) = cx.sess().opts.working_dir.local_path() else { return }; // `folder_segments` is all unique folder path segments `path/to/foo.rs` gives // `[path, to]` but not foo @@ -90,7 +90,7 @@ impl EarlyLintPass for ModStyle { // `{ foo => path/to/foo.rs, .. } let mut file_map = FxHashMap::default(); for file in files.iter() { - if let FileName::Real(RealFileName::LocalPath(lp)) = &file.name { + if let FileName::Real(name) = &file.name && let Some(lp) = name.local_path() { let path = if lp.is_relative() { lp } else if let Ok(relative) = lp.strip_prefix(trim_to_src) { diff --git a/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs b/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs index 4722c031006be..f86dfb6b8df84 100644 --- a/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs +++ b/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs @@ -184,6 +184,10 @@ fn macro_braces(conf: FxHashSet) -> FxHashMap>(); diff --git a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs index 83b69fbb31164..d24c57c0a4b8a 100644 --- a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -42,27 +42,30 @@ impl ArithmeticSideEffects { } } - /// Checks assign operators (+=, -=, *=, /=) of integers in a non-constant environment that - /// won't overflow. - fn has_valid_assign_op(op: &Spanned, rhs: &hir::Expr<'_>, rhs_refs: Ty<'_>) -> bool { - if !Self::is_literal_integer(rhs, rhs_refs) { - return false; + /// Assuming that `expr` is a literal integer, checks operators (+=, -=, *, /) in a + /// non-constant environment that won't overflow. + fn has_valid_op(op: &Spanned, expr: &hir::Expr<'_>) -> bool { + if let hir::BinOpKind::Add | hir::BinOpKind::Sub = op.node + && let hir::ExprKind::Lit(ref lit) = expr.kind + && let ast::LitKind::Int(0, _) = lit.node + { + return true; + } + if let hir::BinOpKind::Div | hir::BinOpKind::Rem = op.node + && let hir::ExprKind::Lit(ref lit) = expr.kind + && !matches!(lit.node, ast::LitKind::Int(0, _)) + { + return true; } - if let hir::BinOpKind::Div | hir::BinOpKind::Mul = op.node - && let hir::ExprKind::Lit(ref lit) = rhs.kind - && let ast::LitKind::Int(1, _) = lit.node + if let hir::BinOpKind::Mul = op.node + && let hir::ExprKind::Lit(ref lit) = expr.kind + && let ast::LitKind::Int(0 | 1, _) = lit.node { return true; } false } - /// Checks "raw" binary operators (+, -, *, /) of integers in a non-constant environment - /// already handled by the CTFE. - fn has_valid_bin_op(lhs: &hir::Expr<'_>, lhs_refs: Ty<'_>, rhs: &hir::Expr<'_>, rhs_refs: Ty<'_>) -> bool { - Self::is_literal_integer(lhs, lhs_refs) && Self::is_literal_integer(rhs, rhs_refs) - } - /// Checks if the given `expr` has any of the inner `allowed` elements. fn is_allowed_ty(&self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { self.allowed.contains( @@ -83,7 +86,8 @@ impl ArithmeticSideEffects { } fn issue_lint(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) { - span_lint(cx, ARITHMETIC_SIDE_EFFECTS, expr.span, "arithmetic detected"); + let msg = "arithmetic operation that can potentially result in unexpected side-effects"; + span_lint(cx, ARITHMETIC_SIDE_EFFECTS, expr.span, msg); self.expr_span = Some(expr.span); } @@ -115,13 +119,18 @@ impl ArithmeticSideEffects { if self.is_allowed_ty(cx, lhs) || self.is_allowed_ty(cx, rhs) { return; } - let lhs_refs = cx.typeck_results().expr_ty(lhs).peel_refs(); - let rhs_refs = cx.typeck_results().expr_ty(rhs).peel_refs(); - let has_valid_assign_op = Self::has_valid_assign_op(op, rhs, rhs_refs); - if has_valid_assign_op || Self::has_valid_bin_op(lhs, lhs_refs, rhs, rhs_refs) { - return; + let has_valid_op = match ( + Self::is_literal_integer(lhs, cx.typeck_results().expr_ty(lhs).peel_refs()), + Self::is_literal_integer(rhs, cx.typeck_results().expr_ty(rhs).peel_refs()), + ) { + (true, true) => true, + (true, false) => Self::has_valid_op(op, lhs), + (false, true) => Self::has_valid_op(op, rhs), + (false, false) => false, + }; + if !has_valid_op { + self.issue_lint(cx, expr); } - self.issue_lint(cx, expr); } } diff --git a/src/tools/clippy/clippy_lints/src/renamed_lints.rs b/src/tools/clippy/clippy_lints/src/renamed_lints.rs index 6bea6dc0773d5..d320eea1c377d 100644 --- a/src/tools/clippy/clippy_lints/src/renamed_lints.rs +++ b/src/tools/clippy/clippy_lints/src/renamed_lints.rs @@ -36,6 +36,7 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[ ("clippy::invalid_ref", "invalid_value"), ("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums"), ("clippy::panic_params", "non_fmt_panics"), + ("clippy::positional_named_format_parameters", "named_arguments_used_positionally"), ("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr"), ("clippy::unknown_clippy_lints", "unknown_lints"), ("clippy::unused_label", "unused_labels"), diff --git a/src/tools/clippy/clippy_lints/src/unused_peekable.rs b/src/tools/clippy/clippy_lints/src/unused_peekable.rs index cc86564359459..f1cebf0f9923d 100644 --- a/src/tools/clippy/clippy_lints/src/unused_peekable.rs +++ b/src/tools/clippy/clippy_lints/src/unused_peekable.rs @@ -6,6 +6,7 @@ use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::lang_items::LangItem; use rustc_hir::{Block, Expr, ExprKind, HirId, Local, Node, PatKind, PathSegment, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::nested_filter::OnlyBodies; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -109,8 +110,14 @@ impl<'a, 'tcx> PeekableVisitor<'a, 'tcx> { } } -impl<'tcx> Visitor<'_> for PeekableVisitor<'_, 'tcx> { - fn visit_expr(&mut self, ex: &'_ Expr<'_>) { +impl<'tcx> Visitor<'tcx> for PeekableVisitor<'_, 'tcx> { + type NestedFilter = OnlyBodies; + + fn nested_visit_map(&mut self) -> Self::Map { + self.cx.tcx.hir() + } + + fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { if self.found_peek_call { return; } @@ -136,12 +143,11 @@ impl<'tcx> Visitor<'_> for PeekableVisitor<'_, 'tcx> { return; } - if args.iter().any(|arg| { - matches!(arg.kind, ExprKind::Path(_)) && arg_is_mut_peekable(self.cx, arg) - }) { + if args.iter().any(|arg| arg_is_mut_peekable(self.cx, arg)) { self.found_peek_call = true; - return; } + + return; }, // Catch anything taking a Peekable mutably ExprKind::MethodCall( @@ -190,21 +196,21 @@ impl<'tcx> Visitor<'_> for PeekableVisitor<'_, 'tcx> { Node::Local(Local { init: Some(init), .. }) => { if arg_is_mut_peekable(self.cx, init) { self.found_peek_call = true; - return; } - break; + return; }, - Node::Stmt(stmt) => match stmt.kind { - StmtKind::Expr(_) | StmtKind::Semi(_) => {}, - _ => { - self.found_peek_call = true; - return; - }, + Node::Stmt(stmt) => { + match stmt.kind { + StmtKind::Local(_) | StmtKind::Item(_) => self.found_peek_call = true, + StmtKind::Expr(_) | StmtKind::Semi(_) => {}, + } + + return; }, Node::Block(_) | Node::ExprField(_) => {}, _ => { - break; + return; }, } } diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs index 486ea5e5ccfa9..44ab9bca79596 100644 --- a/src/tools/clippy/clippy_lints/src/use_self.rs +++ b/src/tools/clippy/clippy_lints/src/use_self.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::same_type_and_consts; -use clippy_utils::{meets_msrv, msrvs}; +use clippy_utils::{is_from_proc_macro, meets_msrv, msrvs}; use if_chain::if_chain; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -87,7 +87,7 @@ impl_lint_pass!(UseSelf => [USE_SELF]); const SEGMENTS_MSG: &str = "segments should be composed of at least 1 element"; impl<'tcx> LateLintPass<'tcx> for UseSelf { - fn check_item(&mut self, _cx: &LateContext<'_>, item: &Item<'_>) { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &Item<'tcx>) { if matches!(item.kind, ItemKind::OpaqueTy(_)) { // skip over `ItemKind::OpaqueTy` in order to lint `foo() -> impl <..>` return; @@ -103,6 +103,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { if parameters.as_ref().map_or(true, |params| { !params.parenthesized && !params.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_))) }); + if !is_from_proc_macro(cx, item); // expensive, should be last check then { StackItem::Check { impl_id: item.def_id, @@ -213,9 +214,6 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { hir_ty_to_ty(cx.tcx, hir_ty) }; if same_type_and_consts(ty, cx.tcx.type_of(impl_id)); - let hir = cx.tcx.hir(); - // prevents false positive on `#[derive(serde::Deserialize)]` - if !hir.span(hir.get_parent_node(hir_ty.hir_id)).in_derive_expansion(); then { span_lint(cx, hir_ty.span); } diff --git a/src/tools/clippy/clippy_lints/src/utils/conf.rs b/src/tools/clippy/clippy_lints/src/utils/conf.rs index a8500beb25747..2be3fa99c811c 100644 --- a/src/tools/clippy/clippy_lints/src/utils/conf.rs +++ b/src/tools/clippy/clippy_lints/src/utils/conf.rs @@ -476,7 +476,7 @@ pub fn format_error(error: Box) -> String { let mut msg = String::from(prefix); for row in 0..rows { - write!(msg, "\n").unwrap(); + writeln!(msg).unwrap(); for (column, column_width) in column_widths.iter().copied().enumerate() { let index = column * rows + row; let field = fields.get(index).copied().unwrap_or_default(); diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs index 640a09a7a9123..06e7d70170171 100644 --- a/src/tools/clippy/clippy_lints/src/write.rs +++ b/src/tools/clippy/clippy_lints/src/write.rs @@ -1,20 +1,12 @@ -use std::borrow::Cow; -use std::iter; -use std::ops::{Deref, Range}; - -use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability}; -use rustc_ast::ast::{Expr, ExprKind, Impl, Item, ItemKind, MacCall, Path, StrLit, StrStyle}; -use rustc_ast::ptr::P; -use rustc_ast::token::{self, LitKind}; -use rustc_ast::tokenstream::TokenStream; -use rustc_errors::{Applicability, DiagnosticBuilder}; -use rustc_lexer::unescape::{self, EscapeError}; -use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; -use rustc_parse::parser; +use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; +use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn, MacroCall}; +use clippy_utils::source::snippet_opt; +use rustc_ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, HirIdMap, Impl, Item, ItemKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::symbol::{kw, Symbol}; -use rustc_span::{sym, BytePos, InnerSpan, Span, DUMMY_SP}; +use rustc_span::{sym, BytePos, Span}; declare_clippy_lint! { /// ### What it does @@ -74,13 +66,7 @@ declare_clippy_lint! { /// application and might forget to remove those prints afterward. /// /// ### Known problems - /// * Only catches `print!` and `println!` calls. - /// * The lint level is unaffected by crate attributes. The level can still - /// be set for functions, modules and other items. To change the level for - /// the entire crate, please use command line flags. More information and a - /// configuration example can be found in [clippy#6610]. - /// - /// [clippy#6610]: https://github.com/rust-lang/rust-clippy/issues/6610#issuecomment-977120558 + /// Only catches `print!` and `println!` calls. /// /// ### Example /// ```rust @@ -102,13 +88,7 @@ declare_clippy_lint! { /// application and might forget to remove those prints afterward. /// /// ### Known problems - /// * Only catches `eprint!` and `eprintln!` calls. - /// * The lint level is unaffected by crate attributes. The level can still - /// be set for functions, modules and other items. To change the level for - /// the entire crate, please use command line flags. More information and a - /// configuration example can be found in [clippy#6610]. - /// - /// [clippy#6610]: https://github.com/rust-lang/rust-clippy/issues/6610#issuecomment-977120558 + /// Only catches `eprint!` and `eprintln!` calls. /// /// ### Example /// ```rust @@ -149,10 +129,6 @@ declare_clippy_lint! { /// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary /// (i.e., just put the literal in the format string) /// - /// ### Known problems - /// Will also warn with macro calls as arguments that expand to literals - /// -- e.g., `println!("{}", env!("FOO"))`. - /// /// ### Example /// ```rust /// println!("{}", "foo"); @@ -234,10 +210,6 @@ declare_clippy_lint! { /// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary /// (i.e., just put the literal in the format string) /// - /// ### Known problems - /// Will also warn with macro calls as arguments that expand to literals - /// -- e.g., `writeln!(buf, "{}", env!("FOO"))`. - /// /// ### Example /// ```rust /// # use std::fmt::Write; @@ -257,28 +229,6 @@ declare_clippy_lint! { "writing a literal with a format string" } -declare_clippy_lint! { - /// ### What it does - /// This lint warns when a named parameter in a format string is used as a positional one. - /// - /// ### Why is this bad? - /// It may be confused for an assignment and obfuscates which parameter is being used. - /// - /// ### Example - /// ```rust - /// println!("{}", x = 10); - /// ``` - /// - /// Use instead: - /// ```rust - /// println!("{x}", x = 10); - /// ``` - #[clippy::version = "1.63.0"] - pub POSITIONAL_NAMED_FORMAT_PARAMETERS, - suspicious, - "named parameter in a format string is used positionally" -} - #[derive(Default)] pub struct Write { in_debug_impl: bool, @@ -294,537 +244,308 @@ impl_lint_pass!(Write => [ WRITE_WITH_NEWLINE, WRITELN_EMPTY_STRING, WRITE_LITERAL, - POSITIONAL_NAMED_FORMAT_PARAMETERS, ]); -impl EarlyLintPass for Write { - fn check_item(&mut self, _: &EarlyContext<'_>, item: &Item) { - if let ItemKind::Impl(box Impl { - of_trait: Some(trait_ref), - .. - }) = &item.kind - { - let trait_name = trait_ref - .path - .segments - .iter() - .last() - .expect("path has at least one segment") - .ident - .name; - if trait_name == sym::Debug { - self.in_debug_impl = true; - } +impl<'tcx> LateLintPass<'tcx> for Write { + fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + if is_debug_impl(cx, item) { + self.in_debug_impl = true; } } - fn check_item_post(&mut self, _: &EarlyContext<'_>, _: &Item) { - self.in_debug_impl = false; + fn check_item_post(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + if is_debug_impl(cx, item) { + self.in_debug_impl = false; + } } - fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &MacCall) { - fn is_build_script(cx: &EarlyContext<'_>) -> bool { - // Cargo sets the crate name for build scripts to `build_script_build` - cx.sess() - .opts - .crate_name - .as_ref() - .map_or(false, |crate_name| crate_name == "build_script_build") - } + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return }; + let Some(diag_name) = cx.tcx.get_diagnostic_name(macro_call.def_id) else { return }; + let Some(name) = diag_name.as_str().strip_suffix("_macro") else { return }; - if mac.path == sym!(print) { - if !is_build_script(cx) { - span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`"); - } - self.lint_print_with_newline(cx, mac); - } else if mac.path == sym!(println) { - if !is_build_script(cx) { - span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`"); - } - self.lint_println_empty_string(cx, mac); - } else if mac.path == sym!(eprint) { - span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprint!`"); - self.lint_print_with_newline(cx, mac); - } else if mac.path == sym!(eprintln) { - span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprintln!`"); - self.lint_println_empty_string(cx, mac); - } else if mac.path == sym!(write) { - if let (Some(fmt_str), dest) = self.check_tts(cx, mac.args.inner_tokens(), true) { - if check_newlines(&fmt_str) { - let (nl_span, only_nl) = newline_span(&fmt_str); - let nl_span = match (dest, only_nl) { - // Special case of `write!(buf, "\n")`: Mark everything from the end of - // `buf` for removal so no trailing comma [`writeln!(buf, )`] remains. - (Some(dest_expr), true) => nl_span.with_lo(dest_expr.span.hi()), - _ => nl_span, - }; - span_lint_and_then( - cx, - WRITE_WITH_NEWLINE, - mac.span(), - "using `write!()` with a format string that ends in a single newline", - |err| { - err.multipart_suggestion( - "use `writeln!()` instead", - vec![(mac.path.span, String::from("writeln")), (nl_span, String::new())], - Applicability::MachineApplicable, - ); - }, - ); - } - } - } else if mac.path == sym!(writeln) { - if let (Some(fmt_str), expr) = self.check_tts(cx, mac.args.inner_tokens(), true) { - if fmt_str.symbol == kw::Empty { - let mut applicability = Applicability::MachineApplicable; - let suggestion = if let Some(e) = expr { - snippet_with_applicability(cx, e.span, "v", &mut applicability) - } else { - applicability = Applicability::HasPlaceholders; - Cow::Borrowed("v") - }; - - span_lint_and_sugg( - cx, - WRITELN_EMPTY_STRING, - mac.span(), - format!("using `writeln!({}, \"\")`", suggestion).as_str(), - "replace it with", - format!("writeln!({})", suggestion), - applicability, - ); + let is_build_script = cx + .sess() + .opts + .crate_name + .as_ref() + .map_or(false, |crate_name| crate_name == "build_script_build"); + + match diag_name { + sym::print_macro | sym::println_macro => { + if !is_build_script { + span_lint(cx, PRINT_STDOUT, macro_call.span, &format!("use of `{name}!`")); } - } + }, + sym::eprint_macro | sym::eprintln_macro => { + span_lint(cx, PRINT_STDERR, macro_call.span, &format!("use of `{name}!`")); + }, + sym::write_macro | sym::writeln_macro => {}, + _ => return, } - } -} -/// Given a format string that ends in a newline and its span, calculates the span of the -/// newline, or the format string itself if the format string consists solely of a newline. -/// Return this and a boolean indicating whether it only consisted of a newline. -fn newline_span(fmtstr: &StrLit) -> (Span, bool) { - let sp = fmtstr.span; - let contents = fmtstr.symbol.as_str(); + let Some(format_args) = FormatArgsExpn::find_nested(cx, expr, macro_call.expn) else { return }; - if contents == r"\n" { - return (sp, true); - } - - let newline_sp_hi = sp.hi() - - match fmtstr.style { - StrStyle::Cooked => BytePos(1), - StrStyle::Raw(hashes) => BytePos((1 + hashes).into()), - }; + // ignore `writeln!(w)` and `write!(v, some_macro!())` + if format_args.format_string.span.from_expansion() { + return; + } - let newline_sp_len = if contents.ends_with('\n') { - BytePos(1) - } else if contents.ends_with(r"\n") { - BytePos(2) - } else { - panic!("expected format string to contain a newline"); - }; + match diag_name { + sym::print_macro | sym::eprint_macro | sym::write_macro => { + check_newline(cx, &format_args, ¯o_call, name); + }, + sym::println_macro | sym::eprintln_macro | sym::writeln_macro => { + check_empty_string(cx, &format_args, ¯o_call, name); + }, + _ => {}, + } - (sp.with_lo(newline_sp_hi - newline_sp_len).with_hi(newline_sp_hi), false) -} + check_literal(cx, &format_args, name); -/// Stores a list of replacement spans for each argument, but only if all the replacements used an -/// empty format string. -#[derive(Default)] -struct SimpleFormatArgs { - unnamed: Vec>, - complex_unnamed: Vec>, - named: Vec<(Symbol, Vec)>, + if !self.in_debug_impl { + for arg in &format_args.args { + if arg.format.r#trait == sym::Debug { + span_lint(cx, USE_DEBUG, arg.span, "use of `Debug`-based formatting"); + } + } + } + } } -impl SimpleFormatArgs { - fn get_unnamed(&self) -> impl Iterator { - self.unnamed.iter().map(|x| match x.as_slice() { - // Ignore the dummy span added from out of order format arguments. - [DUMMY_SP] => &[], - x => x, - }) +fn is_debug_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool { + if let ItemKind::Impl(Impl { of_trait: Some(trait_ref), .. }) = &item.kind + && let Some(trait_id) = trait_ref.trait_def_id() + { + cx.tcx.is_diagnostic_item(sym::Debug, trait_id) + } else { + false } +} - fn get_complex_unnamed(&self) -> impl Iterator { - self.complex_unnamed.iter().map(Vec::as_slice) - } +fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_call: &MacroCall, name: &str) { + let format_string_parts = &format_args.format_string.parts; + let mut format_string_span = format_args.format_string.span; - fn get_named(&self, n: &Path) -> &[Span] { - self.named.iter().find(|x| *n == x.0).map_or(&[], |x| x.1.as_slice()) - } + let Some(last) = format_string_parts.last() else { return }; - fn push(&mut self, arg: rustc_parse_format::Argument<'_>, span: Span) { - use rustc_parse_format::{ - AlignUnknown, ArgumentImplicitlyIs, ArgumentIs, ArgumentNamed, CountImplied, FormatSpec, - }; + let count_vertical_whitespace = || { + format_string_parts + .iter() + .flat_map(|part| part.as_str().chars()) + .filter(|ch| matches!(ch, '\r' | '\n')) + .count() + }; - const SIMPLE: FormatSpec<'_> = FormatSpec { - fill: None, - align: AlignUnknown, - flags: 0, - precision: CountImplied, - precision_span: None, - width: CountImplied, - width_span: None, - ty: "", - ty_span: None, - }; + if last.as_str().ends_with('\n') + // ignore format strings with other internal vertical whitespace + && count_vertical_whitespace() == 1 - match arg.position { - ArgumentIs(n) | ArgumentImplicitlyIs(n) => { - if self.unnamed.len() <= n { - // Use a dummy span to mark all unseen arguments. - self.unnamed.resize_with(n, || vec![DUMMY_SP]); - if arg.format == SIMPLE { - self.unnamed.push(vec![span]); - } else { - self.unnamed.push(Vec::new()); - } - } else { - let args = &mut self.unnamed[n]; - match (args.as_mut_slice(), arg.format == SIMPLE) { - // A non-empty format string has been seen already. - ([], _) => (), - // Replace the dummy span, if it exists. - ([dummy @ DUMMY_SP], true) => *dummy = span, - ([_, ..], true) => args.push(span), - ([_, ..], false) => *args = Vec::new(), - } - } - }, - ArgumentNamed(n) => { - let n = Symbol::intern(n); - if let Some(x) = self.named.iter_mut().find(|x| x.0 == n) { - match x.1.as_slice() { - // A non-empty format string has been seen already. - [] => (), - [_, ..] if arg.format == SIMPLE => x.1.push(span), - [_, ..] => x.1 = Vec::new(), - } - } else if arg.format == SIMPLE { - self.named.push((n, vec![span])); - } else { - self.named.push((n, Vec::new())); - } - }, - }; - } + // ignore trailing arguments: `print!("Issue\n{}", 1265);` + && format_string_parts.len() > format_args.args.len() + { + let lint = if name == "write" { + format_string_span = expand_past_previous_comma(cx, format_string_span); - fn push_to_complex(&mut self, span: Span, position: usize) { - if self.complex_unnamed.len() <= position { - self.complex_unnamed.resize_with(position, Vec::new); - self.complex_unnamed.push(vec![span]); + WRITE_WITH_NEWLINE } else { - let args: &mut Vec = &mut self.complex_unnamed[position]; - args.push(span); - } - } - - fn push_complex( - &mut self, - cx: &EarlyContext<'_>, - arg: rustc_parse_format::Argument<'_>, - str_lit_span: Span, - fmt_span: Span, - ) { - use rustc_parse_format::{ArgumentImplicitlyIs, ArgumentIs, CountIsParam, CountIsStar}; - - let snippet = snippet_opt(cx, fmt_span); - - let end = snippet - .as_ref() - .and_then(|s| s.find(':')) - .or_else(|| fmt_span.hi().0.checked_sub(fmt_span.lo().0 + 1).map(|u| u as usize)); - - if let (ArgumentIs(n) | ArgumentImplicitlyIs(n), Some(end)) = (arg.position, end) { - let span = fmt_span.from_inner(InnerSpan::new(1, end)); - self.push_to_complex(span, n); + PRINT_WITH_NEWLINE }; - if let (CountIsParam(n) | CountIsStar(n), Some(span)) = (arg.format.precision, arg.format.precision_span) { - // We need to do this hack as precision spans should be converted from .* to .foo$ - let hack = if snippet.as_ref().and_then(|s| s.find('*')).is_some() { - 0 - } else { - 1 - }; + span_lint_and_then( + cx, + lint, + macro_call.span, + &format!("using `{name}!()` with a format string that ends in a single newline"), + |diag| { + let name_span = cx.sess().source_map().span_until_char(macro_call.span, '!'); + let Some(format_snippet) = snippet_opt(cx, format_string_span) else { return }; + + if format_string_parts.len() == 1 && last.as_str() == "\n" { + // print!("\n"), write!(f, "\n") + + diag.multipart_suggestion( + &format!("use `{name}ln!` instead"), + vec![(name_span, format!("{name}ln")), (format_string_span, String::new())], + Applicability::MachineApplicable, + ); + } else if format_snippet.ends_with("\\n\"") { + // print!("...\n"), write!(f, "...\n") - let span = str_lit_span.from_inner(InnerSpan { - start: span.start + 1, - end: span.end - hack, - }); - self.push_to_complex(span, n); - }; + let hi = format_string_span.hi(); + let newline_span = format_string_span.with_lo(hi - BytePos(3)).with_hi(hi - BytePos(1)); - if let (CountIsParam(n), Some(span)) = (arg.format.width, arg.format.width_span) { - let span = str_lit_span.from_inner(InnerSpan { - start: span.start, - end: span.end - 1, - }); - self.push_to_complex(span, n); - }; + diag.multipart_suggestion( + &format!("use `{name}ln!` instead"), + vec![(name_span, format!("{name}ln")), (newline_span, String::new())], + Applicability::MachineApplicable, + ); + } + }, + ); } } -impl Write { - /// Parses a format string into a collection of spans for each argument. This only keeps track - /// of empty format arguments. Will also lint usages of debug format strings outside of debug - /// impls. - fn parse_fmt_string(&self, cx: &EarlyContext<'_>, str_lit: &StrLit) -> Option { - use rustc_parse_format::{ParseMode, Parser, Piece}; - - let str_sym = str_lit.symbol_unescaped.as_str(); - let style = match str_lit.style { - StrStyle::Cooked => None, - StrStyle::Raw(n) => Some(n as usize), - }; - - let mut parser = Parser::new(str_sym, style, snippet_opt(cx, str_lit.span), false, ParseMode::Format); - let mut args = SimpleFormatArgs::default(); +fn check_empty_string(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_call: &MacroCall, name: &str) { + if let [part] = &format_args.format_string.parts[..] + && let mut span = format_args.format_string.span + && part.as_str() == "\n" + { + let lint = if name == "writeln" { + span = expand_past_previous_comma(cx, span); - while let Some(arg) = parser.next() { - let arg = match arg { - Piece::String(_) => continue, - Piece::NextArgument(arg) => arg, - }; - let span = parser - .arg_places - .last() - .map_or(DUMMY_SP, |&x| str_lit.span.from_inner(InnerSpan::new(x.start, x.end))); - - if !self.in_debug_impl && arg.format.ty == "?" { - // FIXME: modify rustc's fmt string parser to give us the current span - span_lint(cx, USE_DEBUG, span, "use of `Debug`-based formatting"); - } - args.push(arg, span); - args.push_complex(cx, arg, str_lit.span, span); - } - - parser.errors.is_empty().then_some(args) - } - - /// Checks the arguments of `print[ln]!` and `write[ln]!` calls. It will return a tuple of two - /// `Option`s. The first `Option` of the tuple is the macro's format string. It includes - /// the contents of the string, whether it's a raw string, and the span of the literal in the - /// source. The second `Option` in the tuple is, in the `write[ln]!` case, the expression the - /// `format_str` should be written to. - /// - /// Example: - /// - /// Calling this function on - /// ```rust - /// # use std::fmt::Write; - /// # let mut buf = String::new(); - /// # let something = "something"; - /// writeln!(buf, "string to write: {}", something); - /// ``` - /// will return - /// ```rust,ignore - /// (Some("string to write: {}"), Some(buf)) - /// ``` - fn check_tts<'a>(&self, cx: &EarlyContext<'a>, tts: TokenStream, is_write: bool) -> (Option, Option) { - let mut parser = parser::Parser::new(&cx.sess().parse_sess, tts, false, None); - let expr = if is_write { - match parser - .parse_expr() - .map(rustc_ast::ptr::P::into_inner) - .map_err(DiagnosticBuilder::cancel) - { - // write!(e, ...) - Ok(p) if parser.eat(&token::Comma) => Some(p), - // write!(e) or error - e => return (None, e.ok()), - } + WRITELN_EMPTY_STRING } else { - None + PRINTLN_EMPTY_STRING }; - let fmtstr = match parser.parse_str_lit() { - Ok(fmtstr) => fmtstr, - Err(_) => return (None, expr), - }; + span_lint_and_then( + cx, + lint, + macro_call.span, + &format!("empty string literal in `{name}!`"), + |diag| { + diag.span_suggestion( + span, + "remove the empty string", + String::new(), + Applicability::MachineApplicable, + ); + }, + ); + } +} - let args = match self.parse_fmt_string(cx, &fmtstr) { - Some(args) => args, - None => return (Some(fmtstr), expr), - }; +fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, name: &str) { + let mut counts = HirIdMap::::default(); + for param in format_args.params() { + *counts.entry(param.value.hir_id).or_default() += 1; + } - let lint = if is_write { WRITE_LITERAL } else { PRINT_LITERAL }; - let mut unnamed_args = args.get_unnamed(); - let mut complex_unnamed_args = args.get_complex_unnamed(); - loop { - if !parser.eat(&token::Comma) { - return (Some(fmtstr), expr); - } + for arg in &format_args.args { + let value = arg.param.value; - let comma_span = parser.prev_token.span; - let token_expr = if let Ok(expr) = parser.parse_expr().map_err(DiagnosticBuilder::cancel) { - expr - } else { - return (Some(fmtstr), None); - }; - let complex_unnamed_arg = complex_unnamed_args.next(); - - let (fmt_spans, lit) = match &token_expr.kind { - ExprKind::Lit(lit) => (unnamed_args.next().unwrap_or(&[]), lit), - ExprKind::Assign(lhs, rhs, _) => { - if let Some(span) = complex_unnamed_arg { - for x in span { - Self::report_positional_named_param(cx, *x, lhs, rhs); - } - } - match (&lhs.kind, &rhs.kind) { - (ExprKind::Path(_, p), ExprKind::Lit(lit)) => (args.get_named(p), lit), - _ => continue, + if counts[&value.hir_id] == 1 + && arg.format.is_default() + && let ExprKind::Lit(lit) = &value.kind + && !value.span.from_expansion() + && let Some(value_string) = snippet_opt(cx, value.span) + { + let (replacement, replace_raw) = match lit.node { + LitKind::Str(..) => extract_str_literal(&value_string), + LitKind::Char(ch) => ( + match ch { + '"' => "\\\"", + '\'' => "'", + _ => &value_string[1..value_string.len() - 1], } - }, - _ => { - unnamed_args.next(); - continue; - }, + .to_string(), + false, + ), + LitKind::Bool(b) => (b.to_string(), false), + _ => continue, + }; + + let lint = if name.starts_with("write") { + WRITE_LITERAL + } else { + PRINT_LITERAL }; - let replacement: String = match lit.token_lit.kind { - LitKind::StrRaw(_) | LitKind::ByteStrRaw(_) if matches!(fmtstr.style, StrStyle::Raw(_)) => { - lit.token_lit.symbol.as_str().replace('{', "{{").replace('}', "}}") + let format_string_is_raw = format_args.format_string.style.is_some(); + let replacement = match (format_string_is_raw, replace_raw) { + (false, false) => Some(replacement), + (false, true) => Some(replacement.replace('"', "\\\"").replace('\\', "\\\\")), + (true, false) => match conservative_unescape(&replacement) { + Ok(unescaped) => Some(unescaped), + Err(UnescapeErr::Lint) => None, + Err(UnescapeErr::Ignore) => continue, }, - LitKind::Str | LitKind::ByteStr if matches!(fmtstr.style, StrStyle::Cooked) => { - lit.token_lit.symbol.as_str().replace('{', "{{").replace('}', "}}") + (true, true) => { + if replacement.contains(['#', '"']) { + None + } else { + Some(replacement) + } }, - LitKind::StrRaw(_) - | LitKind::Str - | LitKind::ByteStrRaw(_) - | LitKind::ByteStr - | LitKind::Integer - | LitKind::Float - | LitKind::Err => continue, - LitKind::Byte | LitKind::Char => match lit.token_lit.symbol.as_str() { - "\"" if matches!(fmtstr.style, StrStyle::Cooked) => "\\\"", - "\"" if matches!(fmtstr.style, StrStyle::Raw(0)) => continue, - "\\\\" if matches!(fmtstr.style, StrStyle::Raw(_)) => "\\", - "\\'" => "'", - "{" => "{{", - "}" => "}}", - x if matches!(fmtstr.style, StrStyle::Raw(_)) && x.starts_with('\\') => continue, - x => x, - } - .into(), - LitKind::Bool => lit.token_lit.symbol.as_str().deref().into(), }; - if !fmt_spans.is_empty() { - span_lint_and_then( - cx, - lint, - token_expr.span, - "literal with an empty format string", - |diag| { + span_lint_and_then( + cx, + lint, + value.span, + "literal with an empty format string", + |diag| { + if let Some(replacement) = replacement { + // `format!("{}", "a")`, `format!("{named}", named = "b") + // ~~~~~ ~~~~~~~~~~~~~ + let value_span = expand_past_previous_comma(cx, value.span); + + let replacement = replacement.replace('{', "{{").replace('}', "}}"); diag.multipart_suggestion( "try this", - iter::once((comma_span.to(token_expr.span), String::new())) - .chain(fmt_spans.iter().copied().zip(iter::repeat(replacement))) - .collect(), + vec![(arg.span, replacement), (value_span, String::new())], Applicability::MachineApplicable, ); - }, - ); - } - } - } - - fn report_positional_named_param(cx: &EarlyContext<'_>, span: Span, lhs: &P, _rhs: &P) { - if let ExprKind::Path(_, _p) = &lhs.kind { - let mut applicability = Applicability::MachineApplicable; - let name = snippet_with_applicability(cx, lhs.span, "name", &mut applicability); - // We need to do this hack as precision spans should be converted from .* to .foo$ - let hack = snippet(cx, span, "").contains('*'); - - span_lint_and_sugg( - cx, - POSITIONAL_NAMED_FORMAT_PARAMETERS, - span, - &format!("named parameter {} is used as a positional parameter", name), - "replace it with", - if hack { - format!("{}$", name) - } else { - format!("{}", name) + } }, - applicability, ); - }; - } - - fn lint_println_empty_string(&self, cx: &EarlyContext<'_>, mac: &MacCall) { - if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { - if fmt_str.symbol == kw::Empty { - let name = mac.path.segments[0].ident.name; - span_lint_and_sugg( - cx, - PRINTLN_EMPTY_STRING, - mac.span(), - &format!("using `{}!(\"\")`", name), - "replace it with", - format!("{}!()", name), - Applicability::MachineApplicable, - ); - } - } - } - - fn lint_print_with_newline(&self, cx: &EarlyContext<'_>, mac: &MacCall) { - if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { - if check_newlines(&fmt_str) { - let name = mac.path.segments[0].ident.name; - let suggested = format!("{}ln", name); - span_lint_and_then( - cx, - PRINT_WITH_NEWLINE, - mac.span(), - &format!("using `{}!()` with a format string that ends in a single newline", name), - |err| { - err.multipart_suggestion( - &format!("use `{}!` instead", suggested), - vec![(mac.path.span, suggested), (newline_span(&fmt_str).0, String::new())], - Applicability::MachineApplicable, - ); - }, - ); - } } } } -/// Checks if the format string contains a single newline that terminates it. +/// Removes the raw marker, `#`s and quotes from a str, and returns if the literal is raw /// -/// Literal and escaped newlines are both checked (only literal for raw strings). -fn check_newlines(fmtstr: &StrLit) -> bool { - let mut has_internal_newline = false; - let mut last_was_cr = false; - let mut should_lint = false; - - let contents = fmtstr.symbol.as_str(); - - let mut cb = |r: Range, c: Result| { - let c = match c { - Ok(c) => c, - Err(e) if !e.is_fatal() => return, - Err(e) => panic!("{:?}", e), - }; - - if r.end == contents.len() && c == '\n' && !last_was_cr && !has_internal_newline { - should_lint = true; - } else { - last_was_cr = c == '\r'; - if c == '\n' { - has_internal_newline = true; - } - } +/// `r#"a"#` -> (`a`, true) +/// +/// `"b"` -> (`b`, false) +fn extract_str_literal(literal: &str) -> (String, bool) { + let (literal, raw) = match literal.strip_prefix('r') { + Some(stripped) => (stripped.trim_matches('#'), true), + None => (literal, false), }; - match fmtstr.style { - StrStyle::Cooked => unescape::unescape_literal(contents, unescape::Mode::Str, &mut cb), - StrStyle::Raw(_) => unescape::unescape_literal(contents, unescape::Mode::RawStr, &mut cb), + (literal[1..literal.len() - 1].to_string(), raw) +} + +enum UnescapeErr { + /// Should still be linted, can be manually resolved by author, e.g. + /// + /// ```ignore + /// print!(r"{}", '"'); + /// ``` + Lint, + /// Should not be linted, e.g. + /// + /// ```ignore + /// print!(r"{}", '\r'); + /// ``` + Ignore, +} + +/// Unescape a normal string into a raw string +fn conservative_unescape(literal: &str) -> Result { + let mut unescaped = String::with_capacity(literal.len()); + let mut chars = literal.chars(); + let mut err = false; + + while let Some(ch) = chars.next() { + match ch { + '#' => err = true, + '\\' => match chars.next() { + Some('\\') => unescaped.push('\\'), + Some('"') => err = true, + _ => return Err(UnescapeErr::Ignore), + }, + _ => unescaped.push(ch), + } } - should_lint + if err { Err(UnescapeErr::Lint) } else { Ok(unescaped) } +} + +// Expand from `writeln!(o, "")` to `writeln!(o, "")` +// ^^ ^^^^ +fn expand_past_previous_comma(cx: &LateContext<'_>, span: Span) -> Span { + let extended = cx.sess().source_map().span_extend_to_prev_char(span, ',', true); + extended.with_lo(extended.lo() - BytePos(1)) } diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index 96c55111fbaff..1b8a9c05559a9 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -501,8 +501,8 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { BinOpKind::Mul => l.checked_mul(r).map(zext), BinOpKind::Div if r != 0 => l.checked_div(r).map(zext), BinOpKind::Rem if r != 0 => l.checked_rem(r).map(zext), - BinOpKind::Shr => l.checked_shr(r.try_into().expect("invalid shift")).map(zext), - BinOpKind::Shl => l.checked_shl(r.try_into().expect("invalid shift")).map(zext), + BinOpKind::Shr => l.checked_shr(r.try_into().ok()?).map(zext), + BinOpKind::Shl => l.checked_shl(r.try_into().ok()?).map(zext), BinOpKind::BitXor => Some(zext(l ^ r)), BinOpKind::BitOr => Some(zext(l | r)), BinOpKind::BitAnd => Some(zext(l & r)), @@ -521,8 +521,8 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { BinOpKind::Mul => l.checked_mul(r).map(Constant::Int), BinOpKind::Div => l.checked_div(r).map(Constant::Int), BinOpKind::Rem => l.checked_rem(r).map(Constant::Int), - BinOpKind::Shr => l.checked_shr(r.try_into().expect("shift too large")).map(Constant::Int), - BinOpKind::Shl => l.checked_shl(r.try_into().expect("shift too large")).map(Constant::Int), + BinOpKind::Shr => l.checked_shr(r.try_into().ok()?).map(Constant::Int), + BinOpKind::Shl => l.checked_shl(r.try_into().ok()?).map(Constant::Int), BinOpKind::BitXor => Some(Constant::Int(l ^ r)), BinOpKind::BitOr => Some(Constant::Int(l | r)), BinOpKind::BitAnd => Some(Constant::Int(l & r)), diff --git a/src/tools/clippy/clippy_utils/src/macros.rs b/src/tools/clippy/clippy_utils/src/macros.rs index bd89ff977f877..a1808c0972009 100644 --- a/src/tools/clippy/clippy_utils/src/macros.rs +++ b/src/tools/clippy/clippy_utils/src/macros.rs @@ -7,8 +7,8 @@ use crate::visitors::expr_visitor_no_bodies; use arrayvec::ArrayVec; use itertools::{izip, Either, Itertools}; use rustc_ast::ast::LitKind; -use rustc_hir::intravisit::Visitor; -use rustc_hir::{self as hir, Expr, ExprKind, HirId, Node, QPath}; +use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::{self as hir, Expr, ExprField, ExprKind, HirId, Node, QPath}; use rustc_lexer::unescape::unescape_literal; use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind}; use rustc_lint::LateContext; @@ -485,64 +485,49 @@ struct ParamPosition { precision: Option, } -/// Parses the `fmt` arg of `Arguments::new_v1_formatted(pieces, args, fmt, _)` -fn parse_rt_fmt<'tcx>(fmt_arg: &'tcx Expr<'tcx>) -> Option + 'tcx> { - fn parse_count(expr: &Expr<'_>) -> Option { - // ::core::fmt::rt::v1::Count::Param(1usize), - if let ExprKind::Call(ctor, [val]) = expr.kind - && let ExprKind::Path(QPath::Resolved(_, path)) = ctor.kind - && path.segments.last()?.ident.name == sym::Param - && let ExprKind::Lit(lit) = &val.kind - && let LitKind::Int(pos, _) = lit.node - { - Some(pos as usize) - } else { - None +impl<'tcx> Visitor<'tcx> for ParamPosition { + fn visit_expr_field(&mut self, field: &'tcx ExprField<'tcx>) { + fn parse_count(expr: &Expr<'_>) -> Option { + // ::core::fmt::rt::v1::Count::Param(1usize), + if let ExprKind::Call(ctor, [val]) = expr.kind + && let ExprKind::Path(QPath::Resolved(_, path)) = ctor.kind + && path.segments.last()?.ident.name == sym::Param + && let ExprKind::Lit(lit) = &val.kind + && let LitKind::Int(pos, _) = lit.node + { + Some(pos as usize) + } else { + None + } + } + + match field.ident.name { + sym::position => { + if let ExprKind::Lit(lit) = &field.expr.kind + && let LitKind::Int(pos, _) = lit.node + { + self.value = pos as usize; + } + }, + sym::precision => { + self.precision = parse_count(field.expr); + }, + sym::width => { + self.width = parse_count(field.expr); + }, + _ => walk_expr(self, field.expr), } } +} +/// Parses the `fmt` arg of `Arguments::new_v1_formatted(pieces, args, fmt, _)` +fn parse_rt_fmt<'tcx>(fmt_arg: &'tcx Expr<'tcx>) -> Option + 'tcx> { if let ExprKind::AddrOf(.., array) = fmt_arg.kind && let ExprKind::Array(specs) = array.kind { Some(specs.iter().map(|spec| { let mut position = ParamPosition::default(); - - // ::core::fmt::rt::v1::Argument { - // position: 0usize, - // format: ::core::fmt::rt::v1::FormatSpec { - // .. - // precision: ::core::fmt::rt::v1::Count::Implied, - // width: ::core::fmt::rt::v1::Count::Implied, - // }, - // } - - // TODO: this can be made much nicer next sync with `Visitor::visit_expr_field` - if let ExprKind::Struct(_, fields, _) = spec.kind { - for field in fields { - match (field.ident.name, &field.expr.kind) { - (sym::position, ExprKind::Lit(lit)) => { - if let LitKind::Int(pos, _) = lit.node { - position.value = pos as usize; - } - }, - (sym::format, &ExprKind::Struct(_, spec_fields, _)) => { - for spec_field in spec_fields { - match spec_field.ident.name { - sym::precision => { - position.precision = parse_count(spec_field.expr); - }, - sym::width => { - position.width = parse_count(spec_field.expr); - }, - _ => {}, - } - } - }, - _ => {}, - } - } - } - + position.visit_expr(spec); position })) } else { @@ -711,9 +696,14 @@ impl<'tcx> FormatSpec<'tcx> { }) } - /// Returns true if this format spec would change the contents of a string when formatted - pub fn has_string_formatting(&self) -> bool { - self.r#trait != sym::Display || !self.width.is_implied() || !self.precision.is_implied() + /// Returns true if this format spec is unchanged from the default. e.g. returns true for `{}`, + /// `{foo}` and `{2}`, but false for `{:?}`, `{foo:5}` and `{3:.5}` + pub fn is_default(&self) -> bool { + self.r#trait == sym::Display + && self.width.is_implied() + && self.precision.is_implied() + && self.align == Alignment::AlignUnknown + && self.flags == 0 } } diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs index cca71bbf76e28..f08275a4ac76b 100644 --- a/src/tools/clippy/clippy_utils/src/sugg.rs +++ b/src/tools/clippy/clippy_utils/src/sugg.rs @@ -22,7 +22,7 @@ use std::fmt::{Display, Write as _}; use std::ops::{Add, Neg, Not, Sub}; /// A helper type to build suggestion correctly handling parentheses. -#[derive(Clone, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub enum Sugg<'a> { /// An expression that never needs parentheses such as `1337` or `[0; 42]`. NonParen(Cow<'a, str>), @@ -155,8 +155,8 @@ impl<'a> Sugg<'a> { | hir::ExprKind::Ret(..) | hir::ExprKind::Struct(..) | hir::ExprKind::Tup(..) - | hir::ExprKind::DropTemps(_) | hir::ExprKind::Err => Sugg::NonParen(get_snippet(expr.span)), + hir::ExprKind::DropTemps(inner) => Self::hir_from_snippet(inner, get_snippet), hir::ExprKind::Assign(lhs, rhs, _) => { Sugg::BinOp(AssocOp::Assign, get_snippet(lhs.span), get_snippet(rhs.span)) }, @@ -177,11 +177,11 @@ impl<'a> Sugg<'a> { pub fn ast(cx: &EarlyContext<'_>, expr: &ast::Expr, default: &'a str) -> Self { use rustc_ast::ast::RangeLimits; - let get_whole_snippet = || { - if expr.span.from_expansion() { - snippet_with_macro_callsite(cx, expr.span, default) + let snippet_without_expansion = |cx, span: Span, default| { + if span.from_expansion() { + snippet_with_macro_callsite(cx, span, default) } else { - snippet(cx, expr.span, default) + snippet(cx, span, default) } }; @@ -192,7 +192,7 @@ impl<'a> Sugg<'a> { | ast::ExprKind::If(..) | ast::ExprKind::Let(..) | ast::ExprKind::Unary(..) - | ast::ExprKind::Match(..) => Sugg::MaybeParen(get_whole_snippet()), + | ast::ExprKind::Match(..) => Sugg::MaybeParen(snippet_without_expansion(cx, expr.span, default)), ast::ExprKind::Async(..) | ast::ExprKind::Block(..) | ast::ExprKind::Break(..) @@ -221,41 +221,45 @@ impl<'a> Sugg<'a> { | ast::ExprKind::Array(..) | ast::ExprKind::While(..) | ast::ExprKind::Await(..) - | ast::ExprKind::Err => Sugg::NonParen(get_whole_snippet()), + | ast::ExprKind::Err => Sugg::NonParen(snippet_without_expansion(cx, expr.span, default)), ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::HalfOpen) => Sugg::BinOp( AssocOp::DotDot, - lhs.as_ref().map_or("".into(), |lhs| snippet(cx, lhs.span, default)), - rhs.as_ref().map_or("".into(), |rhs| snippet(cx, rhs.span, default)), + lhs.as_ref() + .map_or("".into(), |lhs| snippet_without_expansion(cx, lhs.span, default)), + rhs.as_ref() + .map_or("".into(), |rhs| snippet_without_expansion(cx, rhs.span, default)), ), ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::Closed) => Sugg::BinOp( AssocOp::DotDotEq, - lhs.as_ref().map_or("".into(), |lhs| snippet(cx, lhs.span, default)), - rhs.as_ref().map_or("".into(), |rhs| snippet(cx, rhs.span, default)), + lhs.as_ref() + .map_or("".into(), |lhs| snippet_without_expansion(cx, lhs.span, default)), + rhs.as_ref() + .map_or("".into(), |rhs| snippet_without_expansion(cx, rhs.span, default)), ), ast::ExprKind::Assign(ref lhs, ref rhs, _) => Sugg::BinOp( AssocOp::Assign, - snippet(cx, lhs.span, default), - snippet(cx, rhs.span, default), + snippet_without_expansion(cx, lhs.span, default), + snippet_without_expansion(cx, rhs.span, default), ), ast::ExprKind::AssignOp(op, ref lhs, ref rhs) => Sugg::BinOp( astbinop2assignop(op), - snippet(cx, lhs.span, default), - snippet(cx, rhs.span, default), + snippet_without_expansion(cx, lhs.span, default), + snippet_without_expansion(cx, rhs.span, default), ), ast::ExprKind::Binary(op, ref lhs, ref rhs) => Sugg::BinOp( AssocOp::from_ast_binop(op.node), - snippet(cx, lhs.span, default), - snippet(cx, rhs.span, default), + snippet_without_expansion(cx, lhs.span, default), + snippet_without_expansion(cx, rhs.span, default), ), ast::ExprKind::Cast(ref lhs, ref ty) => Sugg::BinOp( AssocOp::As, - snippet(cx, lhs.span, default), - snippet(cx, ty.span, default), + snippet_without_expansion(cx, lhs.span, default), + snippet_without_expansion(cx, ty.span, default), ), ast::ExprKind::Type(ref lhs, ref ty) => Sugg::BinOp( AssocOp::Colon, - snippet(cx, lhs.span, default), - snippet(cx, ty.span, default), + snippet_without_expansion(cx, lhs.span, default), + snippet_without_expansion(cx, ty.span, default), ), } } diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs index 926ecf9659321..56343880320db 100644 --- a/src/tools/clippy/clippy_utils/src/ty.rs +++ b/src/tools/clippy/clippy_utils/src/ty.rs @@ -31,6 +31,13 @@ pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { ty.is_copy_modulo_regions(cx.tcx.at(DUMMY_SP), cx.param_env) } +/// This checks whether a given type is known to implement Debug. +pub fn has_debug_impl<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + cx.tcx + .get_diagnostic_item(sym::Debug) + .map_or(false, |debug| implements_trait(cx, ty, debug, &[])) +} + /// Checks whether a type can be partially moved. pub fn can_partially_move_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { if has_drop(cx, ty) || is_copy(cx, ty) { diff --git a/src/tools/clippy/src/docs.rs b/src/tools/clippy/src/docs.rs index f3a5048e7fa82..6c89b4dde372d 100644 --- a/src/tools/clippy/src/docs.rs +++ b/src/tools/clippy/src/docs.rs @@ -221,6 +221,7 @@ docs! { "items_after_statements", "iter_cloned_collect", "iter_count", + "iter_kv_map", "iter_next_loop", "iter_next_slice", "iter_not_returning_iterator", @@ -391,7 +392,6 @@ docs! { "partialeq_to_none", "path_buf_push_overwrite", "pattern_type_mismatch", - "positional_named_format_parameters", "possible_missing_comma", "precedence", "print_in_format_impl", diff --git a/src/tools/clippy/src/docs/iter_kv_map.txt b/src/tools/clippy/src/docs/iter_kv_map.txt new file mode 100644 index 0000000000000..a063c8195ef57 --- /dev/null +++ b/src/tools/clippy/src/docs/iter_kv_map.txt @@ -0,0 +1,22 @@ +### What it does + +Checks for iterating a map (`HashMap` or `BTreeMap`) and +ignoring either the keys or values. + +### Why is this bad? + +Readability. There are `keys` and `values` methods that +can be used to express that we only need the keys or the values. + +### Example + +``` +let map: HashMap = HashMap::new(); +let values = map.iter().map(|(_, value)| value).collect::>(); +``` + +Use instead: +``` +let map: HashMap = HashMap::new(); +let values = map.values().collect::>(); +``` \ No newline at end of file diff --git a/src/tools/clippy/src/docs/positional_named_format_parameters.txt b/src/tools/clippy/src/docs/positional_named_format_parameters.txt deleted file mode 100644 index e391d2406677d..0000000000000 --- a/src/tools/clippy/src/docs/positional_named_format_parameters.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -This lint warns when a named parameter in a format string is used as a positional one. - -### Why is this bad? -It may be confused for an assignment and obfuscates which parameter is being used. - -### Example -``` -println!("{}", x = 10); -``` - -Use instead: -``` -println!("{x}", x = 10); -``` \ No newline at end of file diff --git a/src/tools/clippy/src/docs/print_literal.txt b/src/tools/clippy/src/docs/print_literal.txt index 160073414f9af..a6252a68780bb 100644 --- a/src/tools/clippy/src/docs/print_literal.txt +++ b/src/tools/clippy/src/docs/print_literal.txt @@ -6,10 +6,6 @@ Using literals as `println!` args is inefficient (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary (i.e., just put the literal in the format string) -### Known problems -Will also warn with macro calls as arguments that expand to literals --- e.g., `println!("{}", env!("FOO"))`. - ### Example ``` println!("{}", "foo"); diff --git a/src/tools/clippy/src/docs/print_stderr.txt b/src/tools/clippy/src/docs/print_stderr.txt index fc14511cd6a65..9c6edeeef125d 100644 --- a/src/tools/clippy/src/docs/print_stderr.txt +++ b/src/tools/clippy/src/docs/print_stderr.txt @@ -7,13 +7,7 @@ People often print on *stderr* while debugging an application and might forget to remove those prints afterward. ### Known problems -* Only catches `eprint!` and `eprintln!` calls. -* The lint level is unaffected by crate attributes. The level can still - be set for functions, modules and other items. To change the level for - the entire crate, please use command line flags. More information and a - configuration example can be found in [clippy#6610]. - -[clippy#6610]: https://github.com/rust-lang/rust-clippy/issues/6610#issuecomment-977120558 +Only catches `eprint!` and `eprintln!` calls. ### Example ``` diff --git a/src/tools/clippy/src/docs/print_stdout.txt b/src/tools/clippy/src/docs/print_stdout.txt index 6c9a4b98e1e66..d2cbd811d1b2a 100644 --- a/src/tools/clippy/src/docs/print_stdout.txt +++ b/src/tools/clippy/src/docs/print_stdout.txt @@ -7,13 +7,7 @@ People often print on *stdout* while debugging an application and might forget to remove those prints afterward. ### Known problems -* Only catches `print!` and `println!` calls. -* The lint level is unaffected by crate attributes. The level can still - be set for functions, modules and other items. To change the level for - the entire crate, please use command line flags. More information and a - configuration example can be found in [clippy#6610]. - -[clippy#6610]: https://github.com/rust-lang/rust-clippy/issues/6610#issuecomment-977120558 +Only catches `print!` and `println!` calls. ### Example ``` diff --git a/src/tools/clippy/src/docs/write_literal.txt b/src/tools/clippy/src/docs/write_literal.txt index 9c41a48f9f731..a7a884d087115 100644 --- a/src/tools/clippy/src/docs/write_literal.txt +++ b/src/tools/clippy/src/docs/write_literal.txt @@ -6,10 +6,6 @@ Using literals as `writeln!` args is inefficient (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary (i.e., just put the literal in the format string) -### Known problems -Will also warn with macro calls as arguments that expand to literals --- e.g., `writeln!(buf, "{}", env!("FOO"))`. - ### Example ``` writeln!(buf, "{}", "foo"); diff --git a/src/tools/clippy/tests/ui-cargo/module_style/fail_mod_remap/Cargo.toml b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod_remap/Cargo.toml new file mode 100644 index 0000000000000..a822fad388f56 --- /dev/null +++ b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod_remap/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "fail-mod-remap" +version = "0.1.0" +edition = "2018" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/src/tools/clippy/tests/ui-cargo/module_style/fail_mod_remap/src/bad.rs b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod_remap/src/bad.rs new file mode 100644 index 0000000000000..509aad18622b5 --- /dev/null +++ b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod_remap/src/bad.rs @@ -0,0 +1 @@ +pub mod inner; diff --git a/src/tools/clippy/tests/ui-cargo/module_style/fail_mod_remap/src/bad/inner.rs b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod_remap/src/bad/inner.rs new file mode 100644 index 0000000000000..8b137891791fe --- /dev/null +++ b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod_remap/src/bad/inner.rs @@ -0,0 +1 @@ + diff --git a/src/tools/clippy/tests/ui-cargo/module_style/fail_mod_remap/src/main.rs b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod_remap/src/main.rs new file mode 100644 index 0000000000000..ba4c8c873dd55 --- /dev/null +++ b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod_remap/src/main.rs @@ -0,0 +1,7 @@ +// compile-flags: --remap-path-prefix {{src-base}}=/remapped + +#![warn(clippy::self_named_module_files)] + +mod bad; + +fn main() {} diff --git a/src/tools/clippy/tests/ui-cargo/module_style/fail_mod_remap/src/main.stderr b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod_remap/src/main.stderr new file mode 100644 index 0000000000000..46991ff662e53 --- /dev/null +++ b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod_remap/src/main.stderr @@ -0,0 +1,11 @@ +error: `mod.rs` files are required, found `bad.rs` + --> /remapped/module_style/fail_mod_remap/src/bad.rs:1:1 + | +LL | pub mod inner; + | ^ + | + = note: `-D clippy::self-named-module-files` implied by `-D warnings` + = help: move `bad.rs` to `bad/mod.rs` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs b/src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs index 5b4adc868dff1..ed8161acc0efb 100644 --- a/src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs +++ b/src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs @@ -42,6 +42,7 @@ macro_rules! printlnfoo { fn main() { let _ = vec! {1, 2, 3}; let _ = format!["ugh {} stop being such a good compiler", "hello"]; + let _ = matches!{{}, ()}; let _ = quote!(let x = 1;); let _ = quote::quote!(match match match); let _ = test!(); // trigger when macro def is inside our own crate diff --git a/src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr b/src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr index 039b23b1bdb2f..d80ad49f3086d 100644 --- a/src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr +++ b/src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr @@ -23,26 +23,38 @@ help: consider writing `format!("ugh () stop being such a good compiler", "hello LL | let _ = format!["ugh {} stop being such a good compiler", "hello"]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: use of irregular braces for `quote!` macro +error: use of irregular braces for `matches!` macro + --> $DIR/conf_nonstandard_macro_braces.rs:45:13 + | +LL | let _ = matches!{{}, ()}; + | ^^^^^^^^^^^^^^^^ + | +help: consider writing `matches!((), ())` --> $DIR/conf_nonstandard_macro_braces.rs:45:13 | +LL | let _ = matches!{{}, ()}; + | ^^^^^^^^^^^^^^^^ + +error: use of irregular braces for `quote!` macro + --> $DIR/conf_nonstandard_macro_braces.rs:46:13 + | LL | let _ = quote!(let x = 1;); | ^^^^^^^^^^^^^^^^^^ | help: consider writing `quote! {let x = 1;}` - --> $DIR/conf_nonstandard_macro_braces.rs:45:13 + --> $DIR/conf_nonstandard_macro_braces.rs:46:13 | LL | let _ = quote!(let x = 1;); | ^^^^^^^^^^^^^^^^^^ error: use of irregular braces for `quote::quote!` macro - --> $DIR/conf_nonstandard_macro_braces.rs:46:13 + --> $DIR/conf_nonstandard_macro_braces.rs:47:13 | LL | let _ = quote::quote!(match match match); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: consider writing `quote::quote! {match match match}` - --> $DIR/conf_nonstandard_macro_braces.rs:46:13 + --> $DIR/conf_nonstandard_macro_braces.rs:47:13 | LL | let _ = quote::quote!(match match match); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -67,28 +79,28 @@ LL | let _ = test!(); // trigger when macro def is inside our own crate = note: this error originates in the macro `test` (in Nightly builds, run with -Z macro-backtrace for more info) error: use of irregular braces for `type_pos!` macro - --> $DIR/conf_nonstandard_macro_braces.rs:55:12 + --> $DIR/conf_nonstandard_macro_braces.rs:56:12 | LL | let _: type_pos!(usize) = vec![]; | ^^^^^^^^^^^^^^^^ | help: consider writing `type_pos![usize]` - --> $DIR/conf_nonstandard_macro_braces.rs:55:12 + --> $DIR/conf_nonstandard_macro_braces.rs:56:12 | LL | let _: type_pos!(usize) = vec![]; | ^^^^^^^^^^^^^^^^ error: use of irregular braces for `eprint!` macro - --> $DIR/conf_nonstandard_macro_braces.rs:57:5 + --> $DIR/conf_nonstandard_macro_braces.rs:58:5 | LL | eprint!("test if user config overrides defaults"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: consider writing `eprint!["test if user config overrides defaults"]` - --> $DIR/conf_nonstandard_macro_braces.rs:57:5 + --> $DIR/conf_nonstandard_macro_braces.rs:58:5 | LL | eprint!("test if user config overrides defaults"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 7 previous errors +error: aborting due to 8 previous errors diff --git a/src/tools/clippy/tests/ui/almost_complete_letter_range.fixed b/src/tools/clippy/tests/ui/almost_complete_letter_range.fixed index e69b40f35f4c6..079b7c000dce1 100644 --- a/src/tools/clippy/tests/ui/almost_complete_letter_range.fixed +++ b/src/tools/clippy/tests/ui/almost_complete_letter_range.fixed @@ -1,5 +1,6 @@ // run-rustfix // edition:2018 +// aux-build:macro_rules.rs #![feature(custom_inner_attributes)] #![feature(exclusive_range_pattern)] @@ -8,12 +9,21 @@ #![allow(ellipsis_inclusive_range_patterns)] #![allow(clippy::needless_parens_on_range_literals)] +#[macro_use] +extern crate macro_rules; + macro_rules! a { () => { 'a' }; } +macro_rules! b { + () => { + let _ = 'a'..='z'; + }; +} + fn main() { #[rustfmt::skip] { @@ -47,6 +57,9 @@ fn main() { 'B'..'Z' => 4, _ => 5, }; + + almost_complete_letter_range!(); + b!(); } fn _under_msrv() { diff --git a/src/tools/clippy/tests/ui/almost_complete_letter_range.rs b/src/tools/clippy/tests/ui/almost_complete_letter_range.rs index f2240981d45fa..a66900a976efa 100644 --- a/src/tools/clippy/tests/ui/almost_complete_letter_range.rs +++ b/src/tools/clippy/tests/ui/almost_complete_letter_range.rs @@ -1,5 +1,6 @@ // run-rustfix // edition:2018 +// aux-build:macro_rules.rs #![feature(custom_inner_attributes)] #![feature(exclusive_range_pattern)] @@ -8,12 +9,21 @@ #![allow(ellipsis_inclusive_range_patterns)] #![allow(clippy::needless_parens_on_range_literals)] +#[macro_use] +extern crate macro_rules; + macro_rules! a { () => { 'a' }; } +macro_rules! b { + () => { + let _ = 'a'..'z'; + }; +} + fn main() { #[rustfmt::skip] { @@ -47,6 +57,9 @@ fn main() { 'B'..'Z' => 4, _ => 5, }; + + almost_complete_letter_range!(); + b!(); } fn _under_msrv() { diff --git a/src/tools/clippy/tests/ui/almost_complete_letter_range.stderr b/src/tools/clippy/tests/ui/almost_complete_letter_range.stderr index 5b5dc40ee54d0..3de44c72c1b9d 100644 --- a/src/tools/clippy/tests/ui/almost_complete_letter_range.stderr +++ b/src/tools/clippy/tests/ui/almost_complete_letter_range.stderr @@ -1,5 +1,5 @@ error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:20:17 + --> $DIR/almost_complete_letter_range.rs:30:17 | LL | let _ = ('a') ..'z'; | ^^^^^^--^^^ @@ -9,7 +9,7 @@ LL | let _ = ('a') ..'z'; = note: `-D clippy::almost-complete-letter-range` implied by `-D warnings` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:21:17 + --> $DIR/almost_complete_letter_range.rs:31:17 | LL | let _ = 'A' .. ('Z'); | ^^^^--^^^^^^ @@ -17,7 +17,7 @@ LL | let _ = 'A' .. ('Z'); | help: use an inclusive range: `..=` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:27:13 + --> $DIR/almost_complete_letter_range.rs:37:13 | LL | let _ = (b'a')..(b'z'); | ^^^^^^--^^^^^^ @@ -25,7 +25,7 @@ LL | let _ = (b'a')..(b'z'); | help: use an inclusive range: `..=` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:28:13 + --> $DIR/almost_complete_letter_range.rs:38:13 | LL | let _ = b'A'..b'Z'; | ^^^^--^^^^ @@ -33,7 +33,7 @@ LL | let _ = b'A'..b'Z'; | help: use an inclusive range: `..=` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:33:13 + --> $DIR/almost_complete_letter_range.rs:43:13 | LL | let _ = a!()..'z'; | ^^^^--^^^ @@ -41,7 +41,7 @@ LL | let _ = a!()..'z'; | help: use an inclusive range: `..=` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:36:9 + --> $DIR/almost_complete_letter_range.rs:46:9 | LL | b'a'..b'z' if true => 1, | ^^^^--^^^^ @@ -49,7 +49,7 @@ LL | b'a'..b'z' if true => 1, | help: use an inclusive range: `..=` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:37:9 + --> $DIR/almost_complete_letter_range.rs:47:9 | LL | b'A'..b'Z' if true => 2, | ^^^^--^^^^ @@ -57,7 +57,7 @@ LL | b'A'..b'Z' if true => 2, | help: use an inclusive range: `..=` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:44:9 + --> $DIR/almost_complete_letter_range.rs:54:9 | LL | 'a'..'z' if true => 1, | ^^^--^^^ @@ -65,7 +65,7 @@ LL | 'a'..'z' if true => 1, | help: use an inclusive range: `..=` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:45:9 + --> $DIR/almost_complete_letter_range.rs:55:9 | LL | 'A'..'Z' if true => 2, | ^^^--^^^ @@ -73,7 +73,20 @@ LL | 'A'..'Z' if true => 2, | help: use an inclusive range: `..=` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:55:9 + --> $DIR/almost_complete_letter_range.rs:23:17 + | +LL | let _ = 'a'..'z'; + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` +... +LL | b!(); + | ---- in this macro invocation + | + = note: this error originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: almost complete ascii letter range + --> $DIR/almost_complete_letter_range.rs:68:9 | LL | 'a'..'z' => 1, | ^^^--^^^ @@ -81,7 +94,7 @@ LL | 'a'..'z' => 1, | help: use an inclusive range: `...` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:62:13 + --> $DIR/almost_complete_letter_range.rs:75:13 | LL | let _ = 'a'..'z'; | ^^^--^^^ @@ -89,12 +102,12 @@ LL | let _ = 'a'..'z'; | help: use an inclusive range: `..=` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:64:9 + --> $DIR/almost_complete_letter_range.rs:77:9 | LL | 'a'..'z' => 1, | ^^^--^^^ | | | help: use an inclusive range: `..=` -error: aborting due to 12 previous errors +error: aborting due to 13 previous errors diff --git a/src/tools/clippy/tests/ui/arithmetic_side_effects.rs b/src/tools/clippy/tests/ui/arithmetic_side_effects.rs index f5390c7464250..dd24f5aa592b3 100644 --- a/src/tools/clippy/tests/ui/arithmetic_side_effects.rs +++ b/src/tools/clippy/tests/ui/arithmetic_side_effects.rs @@ -1,9 +1,49 @@ -#![allow(clippy::assign_op_pattern, clippy::unnecessary_owned_empty_strings)] +#![allow( + clippy::assign_op_pattern, + clippy::erasing_op, + clippy::identity_op, + clippy::unnecessary_owned_empty_strings, + arithmetic_overflow, + unconditional_panic +)] #![feature(inline_const, saturating_int_impl)] #![warn(clippy::arithmetic_side_effects)] use core::num::{Saturating, Wrapping}; +pub fn association_with_structures_should_not_trigger_the_lint() { + enum Foo { + Bar = -2, + } + + impl Trait for Foo { + const ASSOC: i32 = { + let _: [i32; 1 + 1]; + fn foo() {} + 1 + 1 + }; + } + + struct Baz([i32; 1 + 1]); + + trait Trait { + const ASSOC: i32 = 1 + 1; + } + + type Alias = [i32; 1 + 1]; + + union Qux { + field: [i32; 1 + 1], + } + + let _: [i32; 1 + 1] = [0, 0]; + + let _: [i32; 1 + 1] = { + let a: [i32; 1 + 1] = [0, 0]; + a + }; +} + pub fn hard_coded_allowed() { let _ = 1f32 + 1f32; let _ = 1f64 + 1f64; @@ -26,7 +66,7 @@ pub fn hard_coded_allowed() { } #[rustfmt::skip] -pub fn non_overflowing_ops() { +pub fn const_ops_should_not_trigger_the_lint() { const _: i32 = { let mut n = 1; n += 1; n }; let _ = const { let mut n = 1; n += 1; n }; @@ -37,21 +77,63 @@ pub fn non_overflowing_ops() { let _ = const { let mut n = 1; n = 1 + n; n }; const _: i32 = 1 + 1; - let _ = 1 + 1; let _ = const { 1 + 1 }; - let mut _a = 1; - _a *= 1; - _a /= 1; + const _: i32 = { let mut n = -1; n = -(-1); n = -n; n }; + let _ = const { let mut n = -1; n = -(-1); n = -n; n }; } -#[rustfmt::skip] -pub fn overflowing_ops() { - let mut _a = 1; _a += 1; +pub fn non_overflowing_runtime_ops_or_ops_already_handled_by_the_compiler() { + let mut _n = i32::MAX; + + // Assign + _n += 0; + _n -= 0; + _n /= 99; + _n %= 99; + _n *= 0; + _n *= 1; + + // Binary + _n = _n + 0; + _n = 0 + _n; + _n = _n - 0; + _n = 0 - _n; + _n = _n / 99; + _n = _n % 99; + _n = _n * 0; + _n = 0 * _n; + _n = _n * 1; + _n = 1 * _n; + _n = 23 + 85; + + // Unary + _n = -1; + _n = -(-1); +} + +pub fn overflowing_runtime_ops() { + let mut _n = i32::MAX; + + // Assign + _n += 1; + _n -= 1; + _n /= 0; + _n %= 0; + _n *= 2; - let mut _b = 1; _b = _b + 1; + // Binary + _n = _n + 1; + _n = 1 + _n; + _n = _n - 1; + _n = 1 - _n; + _n = _n / 0; + _n = _n % 0; + _n = _n * 2; + _n = 2 * _n; - let mut _c = 1; _c = 1 + _c; + // Unary + _n = -_n; } fn main() {} diff --git a/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr b/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr index 6c4c8bdec0f05..a2a856efbffc0 100644 --- a/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr +++ b/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr @@ -1,22 +1,88 @@ -error: arithmetic detected - --> $DIR/arithmetic_side_effects.rs:50:21 +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:119:5 | -LL | let mut _a = 1; _a += 1; - | ^^^^^^^ +LL | _n += 1; + | ^^^^^^^ | = note: `-D clippy::arithmetic-side-effects` implied by `-D warnings` -error: arithmetic detected - --> $DIR/arithmetic_side_effects.rs:52:26 +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:120:5 | -LL | let mut _b = 1; _b = _b + 1; - | ^^^^^^ +LL | _n -= 1; + | ^^^^^^^ -error: arithmetic detected - --> $DIR/arithmetic_side_effects.rs:54:26 +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:121:5 | -LL | let mut _c = 1; _c = 1 + _c; - | ^^^^^^ +LL | _n /= 0; + | ^^^^^^^ -error: aborting due to 3 previous errors +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:122:5 + | +LL | _n %= 0; + | ^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:123:5 + | +LL | _n *= 2; + | ^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:126:10 + | +LL | _n = _n + 1; + | ^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:127:10 + | +LL | _n = 1 + _n; + | ^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:128:10 + | +LL | _n = _n - 1; + | ^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:129:10 + | +LL | _n = 1 - _n; + | ^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:130:10 + | +LL | _n = _n / 0; + | ^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:131:10 + | +LL | _n = _n % 0; + | ^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:132:10 + | +LL | _n = _n * 2; + | ^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:133:10 + | +LL | _n = 2 * _n; + | ^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:136:10 + | +LL | _n = -_n; + | ^^^ + +error: aborting due to 14 previous errors diff --git a/src/tools/clippy/tests/ui/assertions_on_result_states.fixed b/src/tools/clippy/tests/ui/assertions_on_result_states.fixed index 795f435f24cd4..2bb755290c508 100644 --- a/src/tools/clippy/tests/ui/assertions_on_result_states.fixed +++ b/src/tools/clippy/tests/ui/assertions_on_result_states.fixed @@ -75,3 +75,9 @@ fn main() { let r: Result = Err(Foo); assert!(r.is_err()); } + +#[allow(dead_code)] +fn issue9450() { + let res: Result = Ok(1); + res.unwrap_err(); +} diff --git a/src/tools/clippy/tests/ui/assertions_on_result_states.rs b/src/tools/clippy/tests/ui/assertions_on_result_states.rs index 1101aec1e1b34..d8a9bd2f1c455 100644 --- a/src/tools/clippy/tests/ui/assertions_on_result_states.rs +++ b/src/tools/clippy/tests/ui/assertions_on_result_states.rs @@ -75,3 +75,9 @@ fn main() { let r: Result = Err(Foo); assert!(r.is_err()); } + +#[allow(dead_code)] +fn issue9450() { + let res: Result = Ok(1); + assert!(res.is_err()) +} diff --git a/src/tools/clippy/tests/ui/assertions_on_result_states.stderr b/src/tools/clippy/tests/ui/assertions_on_result_states.stderr index 97a5f3dfca4a6..298d63c9c34fb 100644 --- a/src/tools/clippy/tests/ui/assertions_on_result_states.stderr +++ b/src/tools/clippy/tests/ui/assertions_on_result_states.stderr @@ -36,5 +36,11 @@ error: called `assert!` with `Result::is_err` LL | assert!(r.is_err()); | ^^^^^^^^^^^^^^^^^^^ help: replace with: `r.unwrap_err()` -error: aborting due to 6 previous errors +error: called `assert!` with `Result::is_err` + --> $DIR/assertions_on_result_states.rs:82:5 + | +LL | assert!(res.is_err()) + | ^^^^^^^^^^^^^^^^^^^^^ help: replace with: `res.unwrap_err();` + +error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs b/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs index 83a0af6b87aca..ef3ca9aea380c 100644 --- a/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs +++ b/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs @@ -140,3 +140,10 @@ macro_rules! manual_rem_euclid { macro_rules! equatable_if_let { ($a:ident) => {{ if let 2 = $a {} }}; } + +#[macro_export] +macro_rules! almost_complete_letter_range { + () => { + let _ = 'a'..'z'; + }; +} diff --git a/src/tools/clippy/tests/ui/bool_to_int_with_if.fixed b/src/tools/clippy/tests/ui/bool_to_int_with_if.fixed index 9c1098dc4c17a..2c8339cdd7f8a 100644 --- a/src/tools/clippy/tests/ui/bool_to_int_with_if.fixed +++ b/src/tools/clippy/tests/ui/bool_to_int_with_if.fixed @@ -14,6 +14,7 @@ fn main() { // precedence i32::from(a); i32::from(!a); + i32::from(!a); i32::from(a || b); i32::from(cond(a, b)); i32::from(x + y < 4); @@ -21,7 +22,12 @@ fn main() { // if else if if a { 123 - } else {i32::from(b)}; + } else { i32::from(b) }; + + // if else if inverted + if a { + 123 + } else { i32::from(!b) }; // Shouldn't lint diff --git a/src/tools/clippy/tests/ui/bool_to_int_with_if.rs b/src/tools/clippy/tests/ui/bool_to_int_with_if.rs index 0c967dac6e2dd..5d9496f01775f 100644 --- a/src/tools/clippy/tests/ui/bool_to_int_with_if.rs +++ b/src/tools/clippy/tests/ui/bool_to_int_with_if.rs @@ -17,6 +17,11 @@ fn main() { } else { 0 }; + if a { + 0 + } else { + 1 + }; if !a { 1 } else { @@ -47,6 +52,15 @@ fn main() { 0 }; + // if else if inverted + if a { + 123 + } else if b { + 0 + } else { + 1 + }; + // Shouldn't lint if a { diff --git a/src/tools/clippy/tests/ui/bool_to_int_with_if.stderr b/src/tools/clippy/tests/ui/bool_to_int_with_if.stderr index 8647a9cffbede..e695440f6682f 100644 --- a/src/tools/clippy/tests/ui/bool_to_int_with_if.stderr +++ b/src/tools/clippy/tests/ui/bool_to_int_with_if.stderr @@ -14,6 +14,18 @@ LL | | }; error: boolean to int conversion using if --> $DIR/bool_to_int_with_if.rs:20:5 | +LL | / if a { +LL | | 0 +LL | | } else { +LL | | 1 +LL | | }; + | |_____^ help: replace with from: `i32::from(!a)` + | + = note: `!a as i32` or `(!a).into()` can also be valid options + +error: boolean to int conversion using if + --> $DIR/bool_to_int_with_if.rs:25:5 + | LL | / if !a { LL | | 1 LL | | } else { @@ -21,10 +33,10 @@ LL | | 0 LL | | }; | |_____^ help: replace with from: `i32::from(!a)` | - = note: `!a as i32` or `!a.into()` can also be valid options + = note: `!a as i32` or `(!a).into()` can also be valid options error: boolean to int conversion using if - --> $DIR/bool_to_int_with_if.rs:25:5 + --> $DIR/bool_to_int_with_if.rs:30:5 | LL | / if a || b { LL | | 1 @@ -36,7 +48,7 @@ LL | | }; = note: `(a || b) as i32` or `(a || b).into()` can also be valid options error: boolean to int conversion using if - --> $DIR/bool_to_int_with_if.rs:30:5 + --> $DIR/bool_to_int_with_if.rs:35:5 | LL | / if cond(a, b) { LL | | 1 @@ -48,7 +60,7 @@ LL | | }; = note: `cond(a, b) as i32` or `cond(a, b).into()` can also be valid options error: boolean to int conversion using if - --> $DIR/bool_to_int_with_if.rs:35:5 + --> $DIR/bool_to_int_with_if.rs:40:5 | LL | / if x + y < 4 { LL | | 1 @@ -60,7 +72,7 @@ LL | | }; = note: `(x + y < 4) as i32` or `(x + y < 4).into()` can also be valid options error: boolean to int conversion using if - --> $DIR/bool_to_int_with_if.rs:44:12 + --> $DIR/bool_to_int_with_if.rs:49:12 | LL | } else if b { | ____________^ @@ -68,17 +80,30 @@ LL | | 1 LL | | } else { LL | | 0 LL | | }; - | |_____^ help: replace with from: `{i32::from(b)}` + | |_____^ help: replace with from: `{ i32::from(b) }` | = note: `b as i32` or `b.into()` can also be valid options error: boolean to int conversion using if - --> $DIR/bool_to_int_with_if.rs:102:5 + --> $DIR/bool_to_int_with_if.rs:58:12 + | +LL | } else if b { + | ____________^ +LL | | 0 +LL | | } else { +LL | | 1 +LL | | }; + | |_____^ help: replace with from: `{ i32::from(!b) }` + | + = note: `!b as i32` or `(!b).into()` can also be valid options + +error: boolean to int conversion using if + --> $DIR/bool_to_int_with_if.rs:116:5 | LL | if a { 1 } else { 0 } | ^^^^^^^^^^^^^^^^^^^^^ help: replace with from: `u8::from(a)` | = note: `a as u8` or `a.into()` can also be valid options -error: aborting due to 7 previous errors +error: aborting due to 9 previous errors diff --git a/src/tools/clippy/tests/ui/collapsible_if.fixed b/src/tools/clippy/tests/ui/collapsible_if.fixed index 5b0e4a473c4ad..6bb7682bae953 100644 --- a/src/tools/clippy/tests/ui/collapsible_if.fixed +++ b/src/tools/clippy/tests/ui/collapsible_if.fixed @@ -139,6 +139,9 @@ fn main() { // Fix #5962 if matches!(true, true) && matches!(true, true) {} + // Issue #9375 + if matches!(true, true) && truth() && matches!(true, true) {} + if true { #[cfg(not(teehee))] if true { diff --git a/src/tools/clippy/tests/ui/collapsible_if.rs b/src/tools/clippy/tests/ui/collapsible_if.rs index cd231a5d7abb0..e216a9ee54c90 100644 --- a/src/tools/clippy/tests/ui/collapsible_if.rs +++ b/src/tools/clippy/tests/ui/collapsible_if.rs @@ -155,6 +155,11 @@ fn main() { if matches!(true, true) {} } + // Issue #9375 + if matches!(true, true) && truth() { + if matches!(true, true) {} + } + if true { #[cfg(not(teehee))] if true { diff --git a/src/tools/clippy/tests/ui/collapsible_if.stderr b/src/tools/clippy/tests/ui/collapsible_if.stderr index 6749612388fe3..6327444df21d7 100644 --- a/src/tools/clippy/tests/ui/collapsible_if.stderr +++ b/src/tools/clippy/tests/ui/collapsible_if.stderr @@ -126,5 +126,13 @@ LL | | if matches!(true, true) {} LL | | } | |_____^ help: collapse nested if block: `if matches!(true, true) && matches!(true, true) {}` -error: aborting due to 8 previous errors +error: this `if` statement can be collapsed + --> $DIR/collapsible_if.rs:159:5 + | +LL | / if matches!(true, true) && truth() { +LL | | if matches!(true, true) {} +LL | | } + | |_____^ help: collapse nested if block: `if matches!(true, true) && truth() && matches!(true, true) {}` + +error: aborting due to 9 previous errors diff --git a/src/tools/clippy/tests/ui/crashes/ice-9463.rs b/src/tools/clippy/tests/ui/crashes/ice-9463.rs new file mode 100644 index 0000000000000..41ef930d3233d --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-9463.rs @@ -0,0 +1,5 @@ +#![deny(arithmetic_overflow, const_err)] +fn main() { + let _x = -1_i32 >> -1; + let _y = 1u32 >> 10000000000000u32; +} diff --git a/src/tools/clippy/tests/ui/crashes/ice-9463.stderr b/src/tools/clippy/tests/ui/crashes/ice-9463.stderr new file mode 100644 index 0000000000000..7daa08aeb6c94 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-9463.stderr @@ -0,0 +1,29 @@ +error: this arithmetic operation will overflow + --> $DIR/ice-9463.rs:3:14 + | +LL | let _x = -1_i32 >> -1; + | ^^^^^^^^^^^^ attempt to shift right by `-1_i32`, which would overflow + | +note: the lint level is defined here + --> $DIR/ice-9463.rs:1:9 + | +LL | #![deny(arithmetic_overflow, const_err)] + | ^^^^^^^^^^^^^^^^^^^ + +error: this arithmetic operation will overflow + --> $DIR/ice-9463.rs:4:14 + | +LL | let _y = 1u32 >> 10000000000000u32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ attempt to shift right by `1316134912_u32`, which would overflow + +error: literal out of range for `u32` + --> $DIR/ice-9463.rs:4:22 + | +LL | let _y = 1u32 >> 10000000000000u32; + | ^^^^^^^^^^^^^^^^^ + | + = note: `#[deny(overflowing_literals)]` on by default + = note: the literal `10000000000000u32` does not fit into the type `u32` whose range is `0..=4294967295` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/derivable_impls.fixed b/src/tools/clippy/tests/ui/derivable_impls.fixed new file mode 100644 index 0000000000000..7dcdfb0937e89 --- /dev/null +++ b/src/tools/clippy/tests/ui/derivable_impls.fixed @@ -0,0 +1,213 @@ +// run-rustfix + +#![allow(dead_code)] + +use std::collections::HashMap; + +#[derive(Default)] +struct FooDefault<'a> { + a: bool, + b: i32, + c: u64, + d: Vec, + e: FooND1, + f: FooND2, + g: HashMap, + h: (i32, Vec), + i: [Vec; 3], + j: [i32; 5], + k: Option, + l: &'a [i32], +} + + + +#[derive(Default)] +struct TupleDefault(bool, i32, u64); + + + +struct FooND1 { + a: bool, +} + +impl std::default::Default for FooND1 { + fn default() -> Self { + Self { a: true } + } +} + +struct FooND2 { + a: i32, +} + +impl std::default::Default for FooND2 { + fn default() -> Self { + Self { a: 5 } + } +} + +struct FooNDNew { + a: bool, +} + +impl FooNDNew { + fn new() -> Self { + Self { a: true } + } +} + +impl Default for FooNDNew { + fn default() -> Self { + Self::new() + } +} + +struct FooNDVec(Vec); + +impl Default for FooNDVec { + fn default() -> Self { + Self(vec![5, 12]) + } +} + +#[derive(Default)] +struct StrDefault<'a>(&'a str); + + + +#[derive(Default)] +struct AlreadyDerived(i32, bool); + +macro_rules! mac { + () => { + 0 + }; + ($e:expr) => { + struct X(u32); + impl Default for X { + fn default() -> Self { + Self($e) + } + } + }; +} + +mac!(0); + +#[derive(Default)] +struct Y(u32); + + +struct RustIssue26925 { + a: Option, +} + +// We should watch out for cases where a manual impl is needed because a +// derive adds different type bounds (https://github.com/rust-lang/rust/issues/26925). +// For example, a struct with Option does not require T: Default, but a derive adds +// that type bound anyways. So until #26925 get fixed we should disable lint +// for the following case +impl Default for RustIssue26925 { + fn default() -> Self { + Self { a: None } + } +} + +struct SpecializedImpl { + a: A, + b: B, +} + +impl Default for SpecializedImpl { + fn default() -> Self { + Self { + a: T::default(), + b: T::default(), + } + } +} + +#[derive(Default)] +struct WithoutSelfCurly { + a: bool, +} + + + +#[derive(Default)] +struct WithoutSelfParan(bool); + + + +// https://github.com/rust-lang/rust-clippy/issues/7655 + +pub struct SpecializedImpl2 { + v: Vec, +} + +impl Default for SpecializedImpl2 { + fn default() -> Self { + Self { v: Vec::new() } + } +} + +// https://github.com/rust-lang/rust-clippy/issues/7654 + +pub struct Color { + pub r: u8, + pub g: u8, + pub b: u8, +} + +/// `#000000` +impl Default for Color { + fn default() -> Self { + Color { r: 0, g: 0, b: 0 } + } +} + +pub struct Color2 { + pub r: u8, + pub g: u8, + pub b: u8, +} + +impl Default for Color2 { + /// `#000000` + fn default() -> Self { + Self { r: 0, g: 0, b: 0 } + } +} + +#[derive(Default)] +pub struct RepeatDefault1 { + a: [i8; 32], +} + + + +pub struct RepeatDefault2 { + a: [i8; 33], +} + +impl Default for RepeatDefault2 { + fn default() -> Self { + RepeatDefault2 { a: [0; 33] } + } +} + +// https://github.com/rust-lang/rust-clippy/issues/7753 + +pub enum IntOrString { + Int(i32), + String(String), +} + +impl Default for IntOrString { + fn default() -> Self { + IntOrString::Int(0) + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/derivable_impls.rs b/src/tools/clippy/tests/ui/derivable_impls.rs index a6412004726d3..625cbcdde230a 100644 --- a/src/tools/clippy/tests/ui/derivable_impls.rs +++ b/src/tools/clippy/tests/ui/derivable_impls.rs @@ -1,3 +1,7 @@ +// run-rustfix + +#![allow(dead_code)] + use std::collections::HashMap; struct FooDefault<'a> { diff --git a/src/tools/clippy/tests/ui/derivable_impls.stderr b/src/tools/clippy/tests/ui/derivable_impls.stderr index 49fb471a21962..c1db5a58b1f51 100644 --- a/src/tools/clippy/tests/ui/derivable_impls.stderr +++ b/src/tools/clippy/tests/ui/derivable_impls.stderr @@ -1,5 +1,5 @@ error: this `impl` can be derived - --> $DIR/derivable_impls.rs:18:1 + --> $DIR/derivable_impls.rs:22:1 | LL | / impl std::default::Default for FooDefault<'_> { LL | | fn default() -> Self { @@ -11,10 +11,14 @@ LL | | } | |_^ | = note: `-D clippy::derivable-impls` implied by `-D warnings` - = help: try annotating `FooDefault` with `#[derive(Default)]` + = help: remove the manual implementation... +help: ...and instead derive it + | +LL | #[derive(Default)] + | error: this `impl` can be derived - --> $DIR/derivable_impls.rs:39:1 + --> $DIR/derivable_impls.rs:43:1 | LL | / impl std::default::Default for TupleDefault { LL | | fn default() -> Self { @@ -23,10 +27,14 @@ LL | | } LL | | } | |_^ | - = help: try annotating `TupleDefault` with `#[derive(Default)]` + = help: remove the manual implementation... +help: ...and instead derive it + | +LL | #[derive(Default)] + | error: this `impl` can be derived - --> $DIR/derivable_impls.rs:91:1 + --> $DIR/derivable_impls.rs:95:1 | LL | / impl Default for StrDefault<'_> { LL | | fn default() -> Self { @@ -35,10 +43,14 @@ LL | | } LL | | } | |_^ | - = help: try annotating `StrDefault` with `#[derive(Default)]` + = help: remove the manual implementation... +help: ...and instead derive it + | +LL | #[derive(Default)] + | error: this `impl` can be derived - --> $DIR/derivable_impls.rs:117:1 + --> $DIR/derivable_impls.rs:121:1 | LL | / impl Default for Y { LL | | fn default() -> Self { @@ -47,10 +59,14 @@ LL | | } LL | | } | |_^ | - = help: try annotating `Y` with `#[derive(Default)]` + = help: remove the manual implementation... +help: ...and instead derive it + | +LL | #[derive(Default)] + | error: this `impl` can be derived - --> $DIR/derivable_impls.rs:156:1 + --> $DIR/derivable_impls.rs:160:1 | LL | / impl Default for WithoutSelfCurly { LL | | fn default() -> Self { @@ -59,10 +75,14 @@ LL | | } LL | | } | |_^ | - = help: try annotating `WithoutSelfCurly` with `#[derive(Default)]` + = help: remove the manual implementation... +help: ...and instead derive it + | +LL | #[derive(Default)] + | error: this `impl` can be derived - --> $DIR/derivable_impls.rs:164:1 + --> $DIR/derivable_impls.rs:168:1 | LL | / impl Default for WithoutSelfParan { LL | | fn default() -> Self { @@ -71,10 +91,14 @@ LL | | } LL | | } | |_^ | - = help: try annotating `WithoutSelfParan` with `#[derive(Default)]` + = help: remove the manual implementation... +help: ...and instead derive it + | +LL | #[derive(Default)] + | error: this `impl` can be derived - --> $DIR/derivable_impls.rs:214:1 + --> $DIR/derivable_impls.rs:218:1 | LL | / impl Default for RepeatDefault1 { LL | | fn default() -> Self { @@ -83,7 +107,11 @@ LL | | } LL | | } | |_^ | - = help: try annotating `RepeatDefault1` with `#[derive(Default)]` + = help: remove the manual implementation... +help: ...and instead derive it + | +LL | #[derive(Default)] + | error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/eprint_with_newline.rs b/src/tools/clippy/tests/ui/eprint_with_newline.rs index 8df32649ad948..de5e121be8774 100644 --- a/src/tools/clippy/tests/ui/eprint_with_newline.rs +++ b/src/tools/clippy/tests/ui/eprint_with_newline.rs @@ -45,5 +45,13 @@ fn main() { eprint!("\r\n"); eprint!("foo\r\n"); eprint!("\\r\n"); //~ ERROR - eprint!("foo\rbar\n") // ~ ERROR + eprint!("foo\rbar\n"); + + // Ignore expanded format strings + macro_rules! newline { + () => { + "\n" + }; + } + eprint!(newline!()); } diff --git a/src/tools/clippy/tests/ui/eprint_with_newline.stderr b/src/tools/clippy/tests/ui/eprint_with_newline.stderr index f137787bff0c7..0eefb9f0ca978 100644 --- a/src/tools/clippy/tests/ui/eprint_with_newline.stderr +++ b/src/tools/clippy/tests/ui/eprint_with_newline.stderr @@ -83,7 +83,7 @@ LL | | ); help: use `eprintln!` instead | LL ~ eprintln!( -LL ~ "" +LL ~ | error: using `eprint!()` with a format string that ends in a single newline @@ -98,7 +98,7 @@ LL | | ); help: use `eprintln!` instead | LL ~ eprintln!( -LL ~ r"" +LL ~ | error: using `eprint!()` with a format string that ends in a single newline @@ -113,17 +113,5 @@ LL - eprint!("/r/n"); //~ ERROR LL + eprintln!("/r"); //~ ERROR | -error: using `eprint!()` with a format string that ends in a single newline - --> $DIR/eprint_with_newline.rs:48:5 - | -LL | eprint!("foo/rbar/n") // ~ ERROR - | ^^^^^^^^^^^^^^^^^^^^^ - | -help: use `eprintln!` instead - | -LL - eprint!("foo/rbar/n") // ~ ERROR -LL + eprintln!("foo/rbar") // ~ ERROR - | - -error: aborting due to 10 previous errors +error: aborting due to 9 previous errors diff --git a/src/tools/clippy/tests/ui/explicit_write.fixed b/src/tools/clippy/tests/ui/explicit_write.fixed index 74d0e5290282a..35283725619ae 100644 --- a/src/tools/clippy/tests/ui/explicit_write.fixed +++ b/src/tools/clippy/tests/ui/explicit_write.fixed @@ -36,6 +36,8 @@ fn main() { eprintln!("with {} {}", 2, value); eprintln!("with {value}"); eprintln!("macro arg {}", one!()); + let width = 2; + eprintln!("{:w$}", value, w = width); } // these should not warn, different destination { diff --git a/src/tools/clippy/tests/ui/explicit_write.rs b/src/tools/clippy/tests/ui/explicit_write.rs index e7a698d3e012d..be864a55b663f 100644 --- a/src/tools/clippy/tests/ui/explicit_write.rs +++ b/src/tools/clippy/tests/ui/explicit_write.rs @@ -36,6 +36,8 @@ fn main() { writeln!(std::io::stderr(), "with {} {}", 2, value).unwrap(); writeln!(std::io::stderr(), "with {value}").unwrap(); writeln!(std::io::stderr(), "macro arg {}", one!()).unwrap(); + let width = 2; + writeln!(std::io::stderr(), "{:w$}", value, w = width).unwrap(); } // these should not warn, different destination { diff --git a/src/tools/clippy/tests/ui/explicit_write.stderr b/src/tools/clippy/tests/ui/explicit_write.stderr index 29ae0cdece249..ff05f4343d77d 100644 --- a/src/tools/clippy/tests/ui/explicit_write.stderr +++ b/src/tools/clippy/tests/ui/explicit_write.stderr @@ -72,5 +72,11 @@ error: use of `writeln!(stderr(), ...).unwrap()` LL | writeln!(std::io::stderr(), "macro arg {}", one!()).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("macro arg {}", one!())` -error: aborting due to 12 previous errors +error: use of `writeln!(stderr(), ...).unwrap()` + --> $DIR/explicit_write.rs:40:9 + | +LL | writeln!(std::io::stderr(), "{:w$}", value, w = width).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("{:w$}", value, w = width)` + +error: aborting due to 13 previous errors diff --git a/src/tools/clippy/tests/ui/format.fixed b/src/tools/clippy/tests/ui/format.fixed index b56d6aec508d3..e0c5f692740a1 100644 --- a/src/tools/clippy/tests/ui/format.fixed +++ b/src/tools/clippy/tests/ui/format.fixed @@ -28,8 +28,6 @@ fn main() { format!("{:?}", "foo"); // Don't warn about `Debug`. format!("{:8}", "foo"); format!("{:width$}", "foo", width = 8); - "foo".to_string(); // Warn when the format makes no difference. - "foo".to_string(); // Warn when the format makes no difference. format!("foo {}", "bar"); format!("{} bar", "foo"); @@ -38,8 +36,6 @@ fn main() { format!("{:?}", arg); // Don't warn about debug. format!("{:8}", arg); format!("{:width$}", arg, width = 8); - arg.to_string(); // Warn when the format makes no difference. - arg.to_string(); // Warn when the format makes no difference. format!("foo {}", arg); format!("{} bar", arg); diff --git a/src/tools/clippy/tests/ui/format.rs b/src/tools/clippy/tests/ui/format.rs index 4c1a3a840ed96..ff83cd64bf09c 100644 --- a/src/tools/clippy/tests/ui/format.rs +++ b/src/tools/clippy/tests/ui/format.rs @@ -30,8 +30,6 @@ fn main() { format!("{:?}", "foo"); // Don't warn about `Debug`. format!("{:8}", "foo"); format!("{:width$}", "foo", width = 8); - format!("{:+}", "foo"); // Warn when the format makes no difference. - format!("{:<}", "foo"); // Warn when the format makes no difference. format!("foo {}", "bar"); format!("{} bar", "foo"); @@ -40,8 +38,6 @@ fn main() { format!("{:?}", arg); // Don't warn about debug. format!("{:8}", arg); format!("{:width$}", arg, width = 8); - format!("{:+}", arg); // Warn when the format makes no difference. - format!("{:<}", arg); // Warn when the format makes no difference. format!("foo {}", arg); format!("{} bar", arg); diff --git a/src/tools/clippy/tests/ui/format.stderr b/src/tools/clippy/tests/ui/format.stderr index 6c35caeb034d0..0ef0ac655d394 100644 --- a/src/tools/clippy/tests/ui/format.stderr +++ b/src/tools/clippy/tests/ui/format.stderr @@ -46,82 +46,58 @@ LL | format!("{}", "foo"); | ^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()` error: useless use of `format!` - --> $DIR/format.rs:33:5 - | -LL | format!("{:+}", "foo"); // Warn when the format makes no difference. - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()` - -error: useless use of `format!` - --> $DIR/format.rs:34:5 - | -LL | format!("{:<}", "foo"); // Warn when the format makes no difference. - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()` - -error: useless use of `format!` - --> $DIR/format.rs:39:5 + --> $DIR/format.rs:37:5 | LL | format!("{}", arg); | ^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string()` error: useless use of `format!` - --> $DIR/format.rs:43:5 - | -LL | format!("{:+}", arg); // Warn when the format makes no difference. - | ^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string()` - -error: useless use of `format!` - --> $DIR/format.rs:44:5 - | -LL | format!("{:<}", arg); // Warn when the format makes no difference. - | ^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string()` - -error: useless use of `format!` - --> $DIR/format.rs:71:5 + --> $DIR/format.rs:67:5 | LL | format!("{}", 42.to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `42.to_string()` error: useless use of `format!` - --> $DIR/format.rs:73:5 + --> $DIR/format.rs:69:5 | LL | format!("{}", x.display().to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.display().to_string()` error: useless use of `format!` - --> $DIR/format.rs:77:18 + --> $DIR/format.rs:73:18 | LL | let _ = Some(format!("{}", a + "bar")); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `a + "bar"` error: useless use of `format!` - --> $DIR/format.rs:81:22 + --> $DIR/format.rs:77:22 | LL | let _s: String = format!("{}", &*v.join("/n")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `(&*v.join("/n")).to_string()` error: useless use of `format!` - --> $DIR/format.rs:87:13 + --> $DIR/format.rs:83:13 | LL | let _ = format!("{x}"); | ^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()` error: useless use of `format!` - --> $DIR/format.rs:89:13 + --> $DIR/format.rs:85:13 | LL | let _ = format!("{y}", y = x); | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()` error: useless use of `format!` - --> $DIR/format.rs:93:13 + --> $DIR/format.rs:89:13 | LL | let _ = format!("{abc}"); | ^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `abc.to_string()` error: useless use of `format!` - --> $DIR/format.rs:95:13 + --> $DIR/format.rs:91:13 | LL | let _ = format!("{xx}"); | ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `xx.to_string()` -error: aborting due to 19 previous errors +error: aborting due to 15 previous errors diff --git a/src/tools/clippy/tests/ui/iter_kv_map.fixed b/src/tools/clippy/tests/ui/iter_kv_map.fixed new file mode 100644 index 0000000000000..83fee04080fa7 --- /dev/null +++ b/src/tools/clippy/tests/ui/iter_kv_map.fixed @@ -0,0 +1,64 @@ +// run-rustfix + +#![warn(clippy::iter_kv_map)] +#![allow(clippy::redundant_clone)] +#![allow(clippy::suspicious_map)] +#![allow(clippy::map_identity)] + +use std::collections::{BTreeMap, HashMap}; + +fn main() { + let get_key = |(key, _val)| key; + + let map: HashMap = HashMap::new(); + + let _ = map.keys().collect::>(); + let _ = map.values().collect::>(); + let _ = map.values().map(|v| v + 2).collect::>(); + + let _ = map.clone().into_keys().collect::>(); + let _ = map.clone().into_keys().map(|key| key + 2).collect::>(); + + let _ = map.clone().into_values().collect::>(); + let _ = map.clone().into_values().map(|val| val + 2).collect::>(); + + let _ = map.clone().values().collect::>(); + let _ = map.keys().filter(|x| *x % 2 == 0).count(); + + // Don't lint + let _ = map.iter().filter(|(_, val)| *val % 2 == 0).map(|(key, _)| key).count(); + let _ = map.iter().map(get_key).collect::>(); + + // Linting the following could be an improvement to the lint + // map.iter().filter_map(|(_, val)| (val % 2 == 0).then(val * 17)).count(); + + // Lint + let _ = map.keys().map(|key| key * 9).count(); + let _ = map.values().map(|value| value * 17).count(); + + let map: BTreeMap = BTreeMap::new(); + + let _ = map.keys().collect::>(); + let _ = map.values().collect::>(); + let _ = map.values().map(|v| v + 2).collect::>(); + + let _ = map.clone().into_keys().collect::>(); + let _ = map.clone().into_keys().map(|key| key + 2).collect::>(); + + let _ = map.clone().into_values().collect::>(); + let _ = map.clone().into_values().map(|val| val + 2).collect::>(); + + let _ = map.clone().values().collect::>(); + let _ = map.keys().filter(|x| *x % 2 == 0).count(); + + // Don't lint + let _ = map.iter().filter(|(_, val)| *val % 2 == 0).map(|(key, _)| key).count(); + let _ = map.iter().map(get_key).collect::>(); + + // Linting the following could be an improvement to the lint + // map.iter().filter_map(|(_, val)| (val % 2 == 0).then(val * 17)).count(); + + // Lint + let _ = map.keys().map(|key| key * 9).count(); + let _ = map.values().map(|value| value * 17).count(); +} diff --git a/src/tools/clippy/tests/ui/iter_kv_map.rs b/src/tools/clippy/tests/ui/iter_kv_map.rs new file mode 100644 index 0000000000000..7a1f1fb0198c7 --- /dev/null +++ b/src/tools/clippy/tests/ui/iter_kv_map.rs @@ -0,0 +1,64 @@ +// run-rustfix + +#![warn(clippy::iter_kv_map)] +#![allow(clippy::redundant_clone)] +#![allow(clippy::suspicious_map)] +#![allow(clippy::map_identity)] + +use std::collections::{BTreeMap, HashMap}; + +fn main() { + let get_key = |(key, _val)| key; + + let map: HashMap = HashMap::new(); + + let _ = map.iter().map(|(key, _)| key).collect::>(); + let _ = map.iter().map(|(_, value)| value).collect::>(); + let _ = map.iter().map(|(_, v)| v + 2).collect::>(); + + let _ = map.clone().into_iter().map(|(key, _)| key).collect::>(); + let _ = map.clone().into_iter().map(|(key, _)| key + 2).collect::>(); + + let _ = map.clone().into_iter().map(|(_, val)| val).collect::>(); + let _ = map.clone().into_iter().map(|(_, val)| val + 2).collect::>(); + + let _ = map.clone().iter().map(|(_, val)| val).collect::>(); + let _ = map.iter().map(|(key, _)| key).filter(|x| *x % 2 == 0).count(); + + // Don't lint + let _ = map.iter().filter(|(_, val)| *val % 2 == 0).map(|(key, _)| key).count(); + let _ = map.iter().map(get_key).collect::>(); + + // Linting the following could be an improvement to the lint + // map.iter().filter_map(|(_, val)| (val % 2 == 0).then(val * 17)).count(); + + // Lint + let _ = map.iter().map(|(key, _value)| key * 9).count(); + let _ = map.iter().map(|(_key, value)| value * 17).count(); + + let map: BTreeMap = BTreeMap::new(); + + let _ = map.iter().map(|(key, _)| key).collect::>(); + let _ = map.iter().map(|(_, value)| value).collect::>(); + let _ = map.iter().map(|(_, v)| v + 2).collect::>(); + + let _ = map.clone().into_iter().map(|(key, _)| key).collect::>(); + let _ = map.clone().into_iter().map(|(key, _)| key + 2).collect::>(); + + let _ = map.clone().into_iter().map(|(_, val)| val).collect::>(); + let _ = map.clone().into_iter().map(|(_, val)| val + 2).collect::>(); + + let _ = map.clone().iter().map(|(_, val)| val).collect::>(); + let _ = map.iter().map(|(key, _)| key).filter(|x| *x % 2 == 0).count(); + + // Don't lint + let _ = map.iter().filter(|(_, val)| *val % 2 == 0).map(|(key, _)| key).count(); + let _ = map.iter().map(get_key).collect::>(); + + // Linting the following could be an improvement to the lint + // map.iter().filter_map(|(_, val)| (val % 2 == 0).then(val * 17)).count(); + + // Lint + let _ = map.iter().map(|(key, _value)| key * 9).count(); + let _ = map.iter().map(|(_key, value)| value * 17).count(); +} diff --git a/src/tools/clippy/tests/ui/iter_kv_map.stderr b/src/tools/clippy/tests/ui/iter_kv_map.stderr new file mode 100644 index 0000000000000..9b9b04c97d81e --- /dev/null +++ b/src/tools/clippy/tests/ui/iter_kv_map.stderr @@ -0,0 +1,136 @@ +error: iterating on a map's keys + --> $DIR/iter_kv_map.rs:15:13 + | +LL | let _ = map.iter().map(|(key, _)| key).collect::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()` + | + = note: `-D clippy::iter-kv-map` implied by `-D warnings` + +error: iterating on a map's values + --> $DIR/iter_kv_map.rs:16:13 + | +LL | let _ = map.iter().map(|(_, value)| value).collect::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values()` + +error: iterating on a map's values + --> $DIR/iter_kv_map.rs:17:13 + | +LL | let _ = map.iter().map(|(_, v)| v + 2).collect::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|v| v + 2)` + +error: iterating on a map's keys + --> $DIR/iter_kv_map.rs:19:13 + | +LL | let _ = map.clone().into_iter().map(|(key, _)| key).collect::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys()` + +error: iterating on a map's keys + --> $DIR/iter_kv_map.rs:20:13 + | +LL | let _ = map.clone().into_iter().map(|(key, _)| key + 2).collect::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys().map(|key| key + 2)` + +error: iterating on a map's values + --> $DIR/iter_kv_map.rs:22:13 + | +LL | let _ = map.clone().into_iter().map(|(_, val)| val).collect::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()` + +error: iterating on a map's values + --> $DIR/iter_kv_map.rs:23:13 + | +LL | let _ = map.clone().into_iter().map(|(_, val)| val + 2).collect::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|val| val + 2)` + +error: iterating on a map's values + --> $DIR/iter_kv_map.rs:25:13 + | +LL | let _ = map.clone().iter().map(|(_, val)| val).collect::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().values()` + +error: iterating on a map's keys + --> $DIR/iter_kv_map.rs:26:13 + | +LL | let _ = map.iter().map(|(key, _)| key).filter(|x| *x % 2 == 0).count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()` + +error: iterating on a map's keys + --> $DIR/iter_kv_map.rs:36:13 + | +LL | let _ = map.iter().map(|(key, _value)| key * 9).count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys().map(|key| key * 9)` + +error: iterating on a map's values + --> $DIR/iter_kv_map.rs:37:13 + | +LL | let _ = map.iter().map(|(_key, value)| value * 17).count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|value| value * 17)` + +error: iterating on a map's keys + --> $DIR/iter_kv_map.rs:41:13 + | +LL | let _ = map.iter().map(|(key, _)| key).collect::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()` + +error: iterating on a map's values + --> $DIR/iter_kv_map.rs:42:13 + | +LL | let _ = map.iter().map(|(_, value)| value).collect::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values()` + +error: iterating on a map's values + --> $DIR/iter_kv_map.rs:43:13 + | +LL | let _ = map.iter().map(|(_, v)| v + 2).collect::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|v| v + 2)` + +error: iterating on a map's keys + --> $DIR/iter_kv_map.rs:45:13 + | +LL | let _ = map.clone().into_iter().map(|(key, _)| key).collect::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys()` + +error: iterating on a map's keys + --> $DIR/iter_kv_map.rs:46:13 + | +LL | let _ = map.clone().into_iter().map(|(key, _)| key + 2).collect::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys().map(|key| key + 2)` + +error: iterating on a map's values + --> $DIR/iter_kv_map.rs:48:13 + | +LL | let _ = map.clone().into_iter().map(|(_, val)| val).collect::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()` + +error: iterating on a map's values + --> $DIR/iter_kv_map.rs:49:13 + | +LL | let _ = map.clone().into_iter().map(|(_, val)| val + 2).collect::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|val| val + 2)` + +error: iterating on a map's values + --> $DIR/iter_kv_map.rs:51:13 + | +LL | let _ = map.clone().iter().map(|(_, val)| val).collect::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().values()` + +error: iterating on a map's keys + --> $DIR/iter_kv_map.rs:52:13 + | +LL | let _ = map.iter().map(|(key, _)| key).filter(|x| *x % 2 == 0).count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()` + +error: iterating on a map's keys + --> $DIR/iter_kv_map.rs:62:13 + | +LL | let _ = map.iter().map(|(key, _value)| key * 9).count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys().map(|key| key * 9)` + +error: iterating on a map's values + --> $DIR/iter_kv_map.rs:63:13 + | +LL | let _ = map.iter().map(|(_key, value)| value * 17).count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|value| value * 17)` + +error: aborting due to 22 previous errors + diff --git a/src/tools/clippy/tests/ui/large_enum_variant.rs b/src/tools/clippy/tests/ui/large_enum_variant.rs index 717009e4c4cca..3b96f09d7b1d6 100644 --- a/src/tools/clippy/tests/ui/large_enum_variant.rs +++ b/src/tools/clippy/tests/ui/large_enum_variant.rs @@ -101,12 +101,12 @@ struct Struct2 { #[derive(Copy, Clone)] enum CopyableLargeEnum { A(bool), - B([u128; 4000]), + B([u64; 8000]), } enum ManuallyCopyLargeEnum { A(bool), - B([u128; 4000]), + B([u64; 8000]), } impl Clone for ManuallyCopyLargeEnum { diff --git a/src/tools/clippy/tests/ui/large_enum_variant.stderr b/src/tools/clippy/tests/ui/large_enum_variant.stderr index c6ed97487c0e3..709972b4a6e49 100644 --- a/src/tools/clippy/tests/ui/large_enum_variant.stderr +++ b/src/tools/clippy/tests/ui/large_enum_variant.stderr @@ -167,8 +167,8 @@ error: large size difference between variants LL | / enum CopyableLargeEnum { LL | | A(bool), | | ------- the second-largest variant contains at least 1 bytes -LL | | B([u128; 4000]), - | | --------------- the largest variant contains at least 64000 bytes +LL | | B([u64; 8000]), + | | -------------- the largest variant contains at least 64000 bytes LL | | } | |_^ the entire enum is at least 64008 bytes | @@ -180,8 +180,8 @@ LL | enum CopyableLargeEnum { help: consider boxing the large fields to reduce the total size of the enum --> $DIR/large_enum_variant.rs:104:5 | -LL | B([u128; 4000]), - | ^^^^^^^^^^^^^^^ +LL | B([u64; 8000]), + | ^^^^^^^^^^^^^^ error: large size difference between variants --> $DIR/large_enum_variant.rs:107:1 @@ -189,8 +189,8 @@ error: large size difference between variants LL | / enum ManuallyCopyLargeEnum { LL | | A(bool), | | ------- the second-largest variant contains at least 1 bytes -LL | | B([u128; 4000]), - | | --------------- the largest variant contains at least 64000 bytes +LL | | B([u64; 8000]), + | | -------------- the largest variant contains at least 64000 bytes LL | | } | |_^ the entire enum is at least 64008 bytes | @@ -202,8 +202,8 @@ LL | enum ManuallyCopyLargeEnum { help: consider boxing the large fields to reduce the total size of the enum --> $DIR/large_enum_variant.rs:109:5 | -LL | B([u128; 4000]), - | ^^^^^^^^^^^^^^^ +LL | B([u64; 8000]), + | ^^^^^^^^^^^^^^ error: large size difference between variants --> $DIR/large_enum_variant.rs:120:1 diff --git a/src/tools/clippy/tests/ui/large_stack_arrays.rs b/src/tools/clippy/tests/ui/large_stack_arrays.rs index d9161bfcf1543..6790765f803e0 100644 --- a/src/tools/clippy/tests/ui/large_stack_arrays.rs +++ b/src/tools/clippy/tests/ui/large_stack_arrays.rs @@ -12,6 +12,12 @@ enum E { T(u32), } +pub static DOESNOTLINT: [u8; 512_001] = [0; 512_001]; +pub static DOESNOTLINT2: [u8; 512_001] = { + let x = 0; + [x; 512_001] +}; + fn main() { let bad = ( [0u32; 20_000_000], diff --git a/src/tools/clippy/tests/ui/large_stack_arrays.stderr b/src/tools/clippy/tests/ui/large_stack_arrays.stderr index 58c0a77c1c841..0d91b65b42840 100644 --- a/src/tools/clippy/tests/ui/large_stack_arrays.stderr +++ b/src/tools/clippy/tests/ui/large_stack_arrays.stderr @@ -1,5 +1,5 @@ error: allocating a local array larger than 512000 bytes - --> $DIR/large_stack_arrays.rs:17:9 + --> $DIR/large_stack_arrays.rs:23:9 | LL | [0u32; 20_000_000], | ^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | [0u32; 20_000_000], = help: consider allocating on the heap with `vec![0u32; 20_000_000].into_boxed_slice()` error: allocating a local array larger than 512000 bytes - --> $DIR/large_stack_arrays.rs:18:9 + --> $DIR/large_stack_arrays.rs:24:9 | LL | [S { data: [0; 32] }; 5000], | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | [S { data: [0; 32] }; 5000], = help: consider allocating on the heap with `vec![S { data: [0; 32] }; 5000].into_boxed_slice()` error: allocating a local array larger than 512000 bytes - --> $DIR/large_stack_arrays.rs:19:9 + --> $DIR/large_stack_arrays.rs:25:9 | LL | [Some(""); 20_000_000], | ^^^^^^^^^^^^^^^^^^^^^^ @@ -24,7 +24,7 @@ LL | [Some(""); 20_000_000], = help: consider allocating on the heap with `vec![Some(""); 20_000_000].into_boxed_slice()` error: allocating a local array larger than 512000 bytes - --> $DIR/large_stack_arrays.rs:20:9 + --> $DIR/large_stack_arrays.rs:26:9 | LL | [E::T(0); 5000], | ^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/len_without_is_empty.rs b/src/tools/clippy/tests/ui/len_without_is_empty.rs index 1e938e72b5777..78397c2af346b 100644 --- a/src/tools/clippy/tests/ui/len_without_is_empty.rs +++ b/src/tools/clippy/tests/ui/len_without_is_empty.rs @@ -274,7 +274,7 @@ impl AsyncLen { } pub async fn len(&self) -> usize { - if self.async_task().await { 0 } else { 1 } + usize::from(!self.async_task().await) } pub async fn is_empty(&self) -> bool { diff --git a/src/tools/clippy/tests/ui/nonminimal_bool.rs b/src/tools/clippy/tests/ui/nonminimal_bool.rs index 24ae62bb05884..e9b4367ca653c 100644 --- a/src/tools/clippy/tests/ui/nonminimal_bool.rs +++ b/src/tools/clippy/tests/ui/nonminimal_bool.rs @@ -57,3 +57,9 @@ fn check_expect() { #[expect(clippy::nonminimal_bool)] let _ = !!a; } + +fn issue9428() { + if matches!(true, true) && true { + println!("foo"); + } +} diff --git a/src/tools/clippy/tests/ui/nonminimal_bool.stderr b/src/tools/clippy/tests/ui/nonminimal_bool.stderr index fc6a5ce1dc2ee..91b5805aa97ab 100644 --- a/src/tools/clippy/tests/ui/nonminimal_bool.stderr +++ b/src/tools/clippy/tests/ui/nonminimal_bool.stderr @@ -107,5 +107,11 @@ LL | let _ = !(a == b || c == d); LL | let _ = a != b && c != d; | ~~~~~~~~~~~~~~~~ -error: aborting due to 12 previous errors +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool.rs:62:8 + | +LL | if matches!(true, true) && true { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(true, true)` + +error: aborting due to 13 previous errors diff --git a/src/tools/clippy/tests/ui/positional_named_format_parameters.fixed b/src/tools/clippy/tests/ui/positional_named_format_parameters.fixed deleted file mode 100644 index 4170e10982045..0000000000000 --- a/src/tools/clippy/tests/ui/positional_named_format_parameters.fixed +++ /dev/null @@ -1,56 +0,0 @@ -// run-rustfix -#![allow(unused_must_use)] -#![allow(named_arguments_used_positionally)] // Unstable at time of writing. -#![warn(clippy::positional_named_format_parameters)] - -use std::io::Write; - -fn main() { - let mut v = Vec::new(); - let hello = "Hello"; - - println!("{hello:.foo$}", foo = 2); - writeln!(v, "{hello:.foo$}", foo = 2); - - // Warnings - println!("{zero} {one:?}", zero = 0, one = 1); - println!("This is a test {zero} {one:?}", zero = 0, one = 1); - println!("Hello {one} is {two:.zero$}", zero = 5, one = hello, two = 0.01); - println!("Hello {one:zero$}!", zero = 5, one = 1); - println!("Hello {zero:one$}!", zero = 4, one = 1); - println!("Hello {zero:0one$}!", zero = 4, one = 1); - println!("Hello is {one:.zero$}", zero = 5, one = 0.01); - println!("Hello is {one:<6.zero$}", zero = 5, one = 0.01); - println!("{zero}, `{two:>8.one$}` has 3", zero = hello, one = 3, two = hello); - println!("Hello {one} is {two:.zero$}", zero = 5, one = hello, two = 0.01); - println!("Hello {world} {world}!", world = 5); - - writeln!(v, "{zero} {one:?}", zero = 0, one = 1); - writeln!(v, "This is a test {zero} {one:?}", zero = 0, one = 1); - writeln!(v, "Hello {one} is {two:.zero$}", zero = 5, one = hello, two = 0.01); - writeln!(v, "Hello {one:zero$}!", zero = 4, one = 1); - writeln!(v, "Hello {zero:one$}!", zero = 4, one = 1); - writeln!(v, "Hello {zero:0one$}!", zero = 4, one = 1); - writeln!(v, "Hello is {one:.zero$}", zero = 3, one = 0.01); - writeln!(v, "Hello is {one:<6.zero$}", zero = 2, one = 0.01); - writeln!(v, "{zero}, `{two:>8.one$}` has 3", zero = hello, one = 3, two = hello); - writeln!(v, "Hello {one} is {two:.zero$}", zero = 1, one = hello, two = 0.01); - writeln!(v, "Hello {world} {world}!", world = 0); - - // Tests from other files - println!("{w:w$}", w = 1); - println!("{p:.p$}", p = 1); - println!("{v}", v = 1); - println!("{v:v$}", v = 1); - println!("{v:v$}", v = 1); - println!("{v:v$.v$}", v = 1); - println!("{v:v$.v$}", v = 1); - println!("{v:v$.v$}", v = 1); - println!("{v:v$.v$}", v = 1); - println!("{v:v$.v$}", v = 1); - println!("{v:v$.v$}", v = 1); - println!("{v:v$.v$}", v = 1); - println!("{w:w$}", w = 1); - println!("{p:.p$}", p = 1); - println!("{:p$.w$}", 1, w = 1, p = 1); -} diff --git a/src/tools/clippy/tests/ui/positional_named_format_parameters.rs b/src/tools/clippy/tests/ui/positional_named_format_parameters.rs deleted file mode 100644 index 553d8494ecc04..0000000000000 --- a/src/tools/clippy/tests/ui/positional_named_format_parameters.rs +++ /dev/null @@ -1,56 +0,0 @@ -// run-rustfix -#![allow(unused_must_use)] -#![allow(named_arguments_used_positionally)] // Unstable at time of writing. -#![warn(clippy::positional_named_format_parameters)] - -use std::io::Write; - -fn main() { - let mut v = Vec::new(); - let hello = "Hello"; - - println!("{hello:.foo$}", foo = 2); - writeln!(v, "{hello:.foo$}", foo = 2); - - // Warnings - println!("{} {1:?}", zero = 0, one = 1); - println!("This is a test { } {000001:?}", zero = 0, one = 1); - println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01); - println!("Hello {1:0$}!", zero = 5, one = 1); - println!("Hello {0:1$}!", zero = 4, one = 1); - println!("Hello {0:01$}!", zero = 4, one = 1); - println!("Hello is {1:.*}", zero = 5, one = 0.01); - println!("Hello is {:<6.*}", zero = 5, one = 0.01); - println!("{}, `{two:>8.*}` has 3", zero = hello, one = 3, two = hello); - println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01); - println!("Hello {world} {}!", world = 5); - - writeln!(v, "{} {1:?}", zero = 0, one = 1); - writeln!(v, "This is a test { } {000001:?}", zero = 0, one = 1); - writeln!(v, "Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01); - writeln!(v, "Hello {1:0$}!", zero = 4, one = 1); - writeln!(v, "Hello {0:1$}!", zero = 4, one = 1); - writeln!(v, "Hello {0:01$}!", zero = 4, one = 1); - writeln!(v, "Hello is {1:.*}", zero = 3, one = 0.01); - writeln!(v, "Hello is {:<6.*}", zero = 2, one = 0.01); - writeln!(v, "{}, `{two:>8.*}` has 3", zero = hello, one = 3, two = hello); - writeln!(v, "Hello {1} is {2:.0$}", zero = 1, one = hello, two = 0.01); - writeln!(v, "Hello {world} {}!", world = 0); - - // Tests from other files - println!("{:w$}", w = 1); - println!("{:.p$}", p = 1); - println!("{}", v = 1); - println!("{:0$}", v = 1); - println!("{0:0$}", v = 1); - println!("{:0$.0$}", v = 1); - println!("{0:0$.0$}", v = 1); - println!("{0:0$.v$}", v = 1); - println!("{0:v$.0$}", v = 1); - println!("{v:0$.0$}", v = 1); - println!("{v:v$.0$}", v = 1); - println!("{v:0$.v$}", v = 1); - println!("{:w$}", w = 1); - println!("{:.p$}", p = 1); - println!("{:p$.w$}", 1, w = 1, p = 1); -} diff --git a/src/tools/clippy/tests/ui/positional_named_format_parameters.stderr b/src/tools/clippy/tests/ui/positional_named_format_parameters.stderr deleted file mode 100644 index 48ddb6d67ad24..0000000000000 --- a/src/tools/clippy/tests/ui/positional_named_format_parameters.stderr +++ /dev/null @@ -1,418 +0,0 @@ -error: named parameter zero is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:16:16 - | -LL | println!("{} {1:?}", zero = 0, one = 1); - | ^ help: replace it with: `zero` - | - = note: `-D clippy::positional-named-format-parameters` implied by `-D warnings` - -error: named parameter one is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:16:19 - | -LL | println!("{} {1:?}", zero = 0, one = 1); - | ^ help: replace it with: `one` - -error: named parameter zero is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:17:31 - | -LL | println!("This is a test { } {000001:?}", zero = 0, one = 1); - | ^ help: replace it with: `zero` - -error: named parameter one is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:17:35 - | -LL | println!("This is a test { } {000001:?}", zero = 0, one = 1); - | ^^^^^^ help: replace it with: `one` - -error: named parameter zero is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:18:32 - | -LL | println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01); - | ^ help: replace it with: `zero` - -error: named parameter one is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:18:22 - | -LL | println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01); - | ^ help: replace it with: `one` - -error: named parameter two is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:18:29 - | -LL | println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01); - | ^ help: replace it with: `two` - -error: named parameter zero is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:19:24 - | -LL | println!("Hello {1:0$}!", zero = 5, one = 1); - | ^ help: replace it with: `zero` - -error: named parameter one is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:19:22 - | -LL | println!("Hello {1:0$}!", zero = 5, one = 1); - | ^ help: replace it with: `one` - -error: named parameter zero is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:20:22 - | -LL | println!("Hello {0:1$}!", zero = 4, one = 1); - | ^ help: replace it with: `zero` - -error: named parameter one is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:20:24 - | -LL | println!("Hello {0:1$}!", zero = 4, one = 1); - | ^ help: replace it with: `one` - -error: named parameter zero is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:21:22 - | -LL | println!("Hello {0:01$}!", zero = 4, one = 1); - | ^ help: replace it with: `zero` - -error: named parameter one is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:21:25 - | -LL | println!("Hello {0:01$}!", zero = 4, one = 1); - | ^ help: replace it with: `one` - -error: named parameter zero is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:22:28 - | -LL | println!("Hello is {1:.*}", zero = 5, one = 0.01); - | ^ help: replace it with: `zero$` - -error: named parameter one is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:22:25 - | -LL | println!("Hello is {1:.*}", zero = 5, one = 0.01); - | ^ help: replace it with: `one` - -error: named parameter zero is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:23:29 - | -LL | println!("Hello is {:<6.*}", zero = 5, one = 0.01); - | ^ help: replace it with: `zero$` - -error: named parameter one is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:23:25 - | -LL | println!("Hello is {:<6.*}", zero = 5, one = 0.01); - | ^ help: replace it with: `one` - -error: named parameter zero is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:24:16 - | -LL | println!("{}, `{two:>8.*}` has 3", zero = hello, one = 3, two = hello); - | ^ help: replace it with: `zero` - -error: named parameter one is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:24:28 - | -LL | println!("{}, `{two:>8.*}` has 3", zero = hello, one = 3, two = hello); - | ^ help: replace it with: `one$` - -error: named parameter zero is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:25:32 - | -LL | println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01); - | ^ help: replace it with: `zero` - -error: named parameter one is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:25:22 - | -LL | println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01); - | ^ help: replace it with: `one` - -error: named parameter two is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:25:29 - | -LL | println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01); - | ^ help: replace it with: `two` - -error: named parameter world is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:26:30 - | -LL | println!("Hello {world} {}!", world = 5); - | ^ help: replace it with: `world` - -error: named parameter zero is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:28:19 - | -LL | writeln!(v, "{} {1:?}", zero = 0, one = 1); - | ^ help: replace it with: `zero` - -error: named parameter one is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:28:22 - | -LL | writeln!(v, "{} {1:?}", zero = 0, one = 1); - | ^ help: replace it with: `one` - -error: named parameter zero is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:29:34 - | -LL | writeln!(v, "This is a test { } {000001:?}", zero = 0, one = 1); - | ^ help: replace it with: `zero` - -error: named parameter one is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:29:38 - | -LL | writeln!(v, "This is a test { } {000001:?}", zero = 0, one = 1); - | ^^^^^^ help: replace it with: `one` - -error: named parameter zero is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:30:35 - | -LL | writeln!(v, "Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01); - | ^ help: replace it with: `zero` - -error: named parameter one is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:30:25 - | -LL | writeln!(v, "Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01); - | ^ help: replace it with: `one` - -error: named parameter two is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:30:32 - | -LL | writeln!(v, "Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01); - | ^ help: replace it with: `two` - -error: named parameter zero is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:31:27 - | -LL | writeln!(v, "Hello {1:0$}!", zero = 4, one = 1); - | ^ help: replace it with: `zero` - -error: named parameter one is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:31:25 - | -LL | writeln!(v, "Hello {1:0$}!", zero = 4, one = 1); - | ^ help: replace it with: `one` - -error: named parameter zero is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:32:25 - | -LL | writeln!(v, "Hello {0:1$}!", zero = 4, one = 1); - | ^ help: replace it with: `zero` - -error: named parameter one is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:32:27 - | -LL | writeln!(v, "Hello {0:1$}!", zero = 4, one = 1); - | ^ help: replace it with: `one` - -error: named parameter zero is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:33:25 - | -LL | writeln!(v, "Hello {0:01$}!", zero = 4, one = 1); - | ^ help: replace it with: `zero` - -error: named parameter one is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:33:28 - | -LL | writeln!(v, "Hello {0:01$}!", zero = 4, one = 1); - | ^ help: replace it with: `one` - -error: named parameter zero is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:34:31 - | -LL | writeln!(v, "Hello is {1:.*}", zero = 3, one = 0.01); - | ^ help: replace it with: `zero$` - -error: named parameter one is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:34:28 - | -LL | writeln!(v, "Hello is {1:.*}", zero = 3, one = 0.01); - | ^ help: replace it with: `one` - -error: named parameter zero is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:35:32 - | -LL | writeln!(v, "Hello is {:<6.*}", zero = 2, one = 0.01); - | ^ help: replace it with: `zero$` - -error: named parameter one is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:35:28 - | -LL | writeln!(v, "Hello is {:<6.*}", zero = 2, one = 0.01); - | ^ help: replace it with: `one` - -error: named parameter zero is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:36:19 - | -LL | writeln!(v, "{}, `{two:>8.*}` has 3", zero = hello, one = 3, two = hello); - | ^ help: replace it with: `zero` - -error: named parameter one is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:36:31 - | -LL | writeln!(v, "{}, `{two:>8.*}` has 3", zero = hello, one = 3, two = hello); - | ^ help: replace it with: `one$` - -error: named parameter zero is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:37:35 - | -LL | writeln!(v, "Hello {1} is {2:.0$}", zero = 1, one = hello, two = 0.01); - | ^ help: replace it with: `zero` - -error: named parameter one is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:37:25 - | -LL | writeln!(v, "Hello {1} is {2:.0$}", zero = 1, one = hello, two = 0.01); - | ^ help: replace it with: `one` - -error: named parameter two is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:37:32 - | -LL | writeln!(v, "Hello {1} is {2:.0$}", zero = 1, one = hello, two = 0.01); - | ^ help: replace it with: `two` - -error: named parameter world is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:38:33 - | -LL | writeln!(v, "Hello {world} {}!", world = 0); - | ^ help: replace it with: `world` - -error: named parameter w is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:41:16 - | -LL | println!("{:w$}", w = 1); - | ^ help: replace it with: `w` - -error: named parameter p is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:42:16 - | -LL | println!("{:.p$}", p = 1); - | ^ help: replace it with: `p` - -error: named parameter v is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:43:16 - | -LL | println!("{}", v = 1); - | ^ help: replace it with: `v` - -error: named parameter v is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:44:16 - | -LL | println!("{:0$}", v = 1); - | ^ help: replace it with: `v` - -error: named parameter v is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:44:17 - | -LL | println!("{:0$}", v = 1); - | ^ help: replace it with: `v` - -error: named parameter v is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:45:16 - | -LL | println!("{0:0$}", v = 1); - | ^ help: replace it with: `v` - -error: named parameter v is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:45:18 - | -LL | println!("{0:0$}", v = 1); - | ^ help: replace it with: `v` - -error: named parameter v is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:46:16 - | -LL | println!("{:0$.0$}", v = 1); - | ^ help: replace it with: `v` - -error: named parameter v is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:46:20 - | -LL | println!("{:0$.0$}", v = 1); - | ^ help: replace it with: `v` - -error: named parameter v is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:46:17 - | -LL | println!("{:0$.0$}", v = 1); - | ^ help: replace it with: `v` - -error: named parameter v is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:47:16 - | -LL | println!("{0:0$.0$}", v = 1); - | ^ help: replace it with: `v` - -error: named parameter v is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:47:21 - | -LL | println!("{0:0$.0$}", v = 1); - | ^ help: replace it with: `v` - -error: named parameter v is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:47:18 - | -LL | println!("{0:0$.0$}", v = 1); - | ^ help: replace it with: `v` - -error: named parameter v is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:48:16 - | -LL | println!("{0:0$.v$}", v = 1); - | ^ help: replace it with: `v` - -error: named parameter v is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:48:18 - | -LL | println!("{0:0$.v$}", v = 1); - | ^ help: replace it with: `v` - -error: named parameter v is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:49:16 - | -LL | println!("{0:v$.0$}", v = 1); - | ^ help: replace it with: `v` - -error: named parameter v is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:49:21 - | -LL | println!("{0:v$.0$}", v = 1); - | ^ help: replace it with: `v` - -error: named parameter v is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:50:21 - | -LL | println!("{v:0$.0$}", v = 1); - | ^ help: replace it with: `v` - -error: named parameter v is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:50:18 - | -LL | println!("{v:0$.0$}", v = 1); - | ^ help: replace it with: `v` - -error: named parameter v is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:51:21 - | -LL | println!("{v:v$.0$}", v = 1); - | ^ help: replace it with: `v` - -error: named parameter v is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:52:18 - | -LL | println!("{v:0$.v$}", v = 1); - | ^ help: replace it with: `v` - -error: named parameter w is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:53:16 - | -LL | println!("{:w$}", w = 1); - | ^ help: replace it with: `w` - -error: named parameter p is used as a positional parameter - --> $DIR/positional_named_format_parameters.rs:54:16 - | -LL | println!("{:.p$}", p = 1); - | ^ help: replace it with: `p` - -error: aborting due to 69 previous errors - diff --git a/src/tools/clippy/tests/ui/print_literal.rs b/src/tools/clippy/tests/ui/print_literal.rs index 8665a3bb28ae1..3f6639c14585a 100644 --- a/src/tools/clippy/tests/ui/print_literal.rs +++ b/src/tools/clippy/tests/ui/print_literal.rs @@ -20,11 +20,13 @@ fn main() { println!("{} of {:b} people know binary, the other half doesn't", 1, 2); println!("10 / 4 is {}", 2.5); println!("2 + 1 = {}", 3); + println!("From expansion {}", stringify!(not a string literal)); // these should throw warnings print!("Hello {}", "world"); println!("Hello {} {}", world, "world"); println!("Hello {}", "world"); + println!("{} {:.4}", "a literal", 5); // positional args don't change the fact // that we're using a literal -- this should diff --git a/src/tools/clippy/tests/ui/print_literal.stderr b/src/tools/clippy/tests/ui/print_literal.stderr index 72aae0756033a..23e6dbc3e341f 100644 --- a/src/tools/clippy/tests/ui/print_literal.stderr +++ b/src/tools/clippy/tests/ui/print_literal.stderr @@ -1,5 +1,5 @@ error: literal with an empty format string - --> $DIR/print_literal.rs:25:24 + --> $DIR/print_literal.rs:26:24 | LL | print!("Hello {}", "world"); | ^^^^^^^ @@ -12,7 +12,7 @@ LL + print!("Hello world"); | error: literal with an empty format string - --> $DIR/print_literal.rs:26:36 + --> $DIR/print_literal.rs:27:36 | LL | println!("Hello {} {}", world, "world"); | ^^^^^^^ @@ -24,7 +24,7 @@ LL + println!("Hello {} world", world); | error: literal with an empty format string - --> $DIR/print_literal.rs:27:26 + --> $DIR/print_literal.rs:28:26 | LL | println!("Hello {}", "world"); | ^^^^^^^ @@ -36,7 +36,19 @@ LL + println!("Hello world"); | error: literal with an empty format string - --> $DIR/print_literal.rs:32:25 + --> $DIR/print_literal.rs:29:26 + | +LL | println!("{} {:.4}", "a literal", 5); + | ^^^^^^^^^^^ + | +help: try this + | +LL - println!("{} {:.4}", "a literal", 5); +LL + println!("a literal {:.4}", 5); + | + +error: literal with an empty format string + --> $DIR/print_literal.rs:34:25 | LL | println!("{0} {1}", "hello", "world"); | ^^^^^^^ @@ -48,7 +60,7 @@ LL + println!("hello {1}", "world"); | error: literal with an empty format string - --> $DIR/print_literal.rs:32:34 + --> $DIR/print_literal.rs:34:34 | LL | println!("{0} {1}", "hello", "world"); | ^^^^^^^ @@ -60,34 +72,34 @@ LL + println!("{0} world", "hello"); | error: literal with an empty format string - --> $DIR/print_literal.rs:33:25 + --> $DIR/print_literal.rs:35:34 | LL | println!("{1} {0}", "hello", "world"); - | ^^^^^^^ + | ^^^^^^^ | help: try this | LL - println!("{1} {0}", "hello", "world"); -LL + println!("{1} hello", "world"); +LL + println!("world {0}", "hello"); | error: literal with an empty format string - --> $DIR/print_literal.rs:33:34 + --> $DIR/print_literal.rs:35:25 | LL | println!("{1} {0}", "hello", "world"); - | ^^^^^^^ + | ^^^^^^^ | help: try this | LL - println!("{1} {0}", "hello", "world"); -LL + println!("world {0}", "hello"); +LL + println!("{1} hello", "world"); | error: literal with an empty format string - --> $DIR/print_literal.rs:36:29 + --> $DIR/print_literal.rs:38:35 | LL | println!("{foo} {bar}", foo = "hello", bar = "world"); - | ^^^^^^^^^^^^^ + | ^^^^^^^ | help: try this | @@ -96,10 +108,10 @@ LL + println!("hello {bar}", bar = "world"); | error: literal with an empty format string - --> $DIR/print_literal.rs:36:44 + --> $DIR/print_literal.rs:38:50 | LL | println!("{foo} {bar}", foo = "hello", bar = "world"); - | ^^^^^^^^^^^^^ + | ^^^^^^^ | help: try this | @@ -108,28 +120,28 @@ LL + println!("{foo} world", foo = "hello"); | error: literal with an empty format string - --> $DIR/print_literal.rs:37:29 + --> $DIR/print_literal.rs:39:50 | LL | println!("{bar} {foo}", foo = "hello", bar = "world"); - | ^^^^^^^^^^^^^ + | ^^^^^^^ | help: try this | LL - println!("{bar} {foo}", foo = "hello", bar = "world"); -LL + println!("{bar} hello", bar = "world"); +LL + println!("world {foo}", foo = "hello"); | error: literal with an empty format string - --> $DIR/print_literal.rs:37:44 + --> $DIR/print_literal.rs:39:35 | LL | println!("{bar} {foo}", foo = "hello", bar = "world"); - | ^^^^^^^^^^^^^ + | ^^^^^^^ | help: try this | LL - println!("{bar} {foo}", foo = "hello", bar = "world"); -LL + println!("world {foo}", foo = "hello"); +LL + println!("{bar} hello", bar = "world"); | -error: aborting due to 11 previous errors +error: aborting due to 12 previous errors diff --git a/src/tools/clippy/tests/ui/print_with_newline.rs b/src/tools/clippy/tests/ui/print_with_newline.rs index a43a1fc4f5241..b8c29d207adae 100644 --- a/src/tools/clippy/tests/ui/print_with_newline.rs +++ b/src/tools/clippy/tests/ui/print_with_newline.rs @@ -48,5 +48,13 @@ fn main() { print!("\r\n"); print!("foo\r\n"); print!("\\r\n"); //~ ERROR - print!("foo\rbar\n") // ~ ERROR + print!("foo\rbar\n"); + + // Ignore expanded format strings + macro_rules! newline { + () => { + "\n" + }; + } + print!(newline!()); } diff --git a/src/tools/clippy/tests/ui/print_with_newline.stderr b/src/tools/clippy/tests/ui/print_with_newline.stderr index edbaa1cdf979f..b9f5675faec73 100644 --- a/src/tools/clippy/tests/ui/print_with_newline.stderr +++ b/src/tools/clippy/tests/ui/print_with_newline.stderr @@ -83,7 +83,7 @@ LL | | ); help: use `println!` instead | LL ~ println!( -LL ~ "" +LL ~ | error: using `print!()` with a format string that ends in a single newline @@ -98,7 +98,7 @@ LL | | ); help: use `println!` instead | LL ~ println!( -LL ~ r"" +LL ~ | error: using `print!()` with a format string that ends in a single newline @@ -113,17 +113,5 @@ LL - print!("/r/n"); //~ ERROR LL + println!("/r"); //~ ERROR | -error: using `print!()` with a format string that ends in a single newline - --> $DIR/print_with_newline.rs:51:5 - | -LL | print!("foo/rbar/n") // ~ ERROR - | ^^^^^^^^^^^^^^^^^^^^ - | -help: use `println!` instead - | -LL - print!("foo/rbar/n") // ~ ERROR -LL + println!("foo/rbar") // ~ ERROR - | - -error: aborting due to 10 previous errors +error: aborting due to 9 previous errors diff --git a/src/tools/clippy/tests/ui/println_empty_string.stderr b/src/tools/clippy/tests/ui/println_empty_string.stderr index 17fe4ea74790c..3cc8bb947bd35 100644 --- a/src/tools/clippy/tests/ui/println_empty_string.stderr +++ b/src/tools/clippy/tests/ui/println_empty_string.stderr @@ -1,28 +1,36 @@ -error: using `println!("")` +error: empty string literal in `println!` --> $DIR/println_empty_string.rs:6:5 | LL | println!(""); - | ^^^^^^^^^^^^ help: replace it with: `println!()` + | ^^^^^^^^^--^ + | | + | help: remove the empty string | = note: `-D clippy::println-empty-string` implied by `-D warnings` -error: using `println!("")` +error: empty string literal in `println!` --> $DIR/println_empty_string.rs:9:14 | LL | _ => println!(""), - | ^^^^^^^^^^^^ help: replace it with: `println!()` + | ^^^^^^^^^--^ + | | + | help: remove the empty string -error: using `eprintln!("")` +error: empty string literal in `eprintln!` --> $DIR/println_empty_string.rs:13:5 | LL | eprintln!(""); - | ^^^^^^^^^^^^^ help: replace it with: `eprintln!()` + | ^^^^^^^^^^--^ + | | + | help: remove the empty string -error: using `eprintln!("")` +error: empty string literal in `eprintln!` --> $DIR/println_empty_string.rs:16:14 | LL | _ => eprintln!(""), - | ^^^^^^^^^^^^^ help: replace it with: `eprintln!()` + | ^^^^^^^^^^--^ + | | + | help: remove the empty string error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/rename.fixed b/src/tools/clippy/tests/ui/rename.fixed index 9cbad2269a099..a6e7bdba77c65 100644 --- a/src/tools/clippy/tests/ui/rename.fixed +++ b/src/tools/clippy/tests/ui/rename.fixed @@ -32,6 +32,7 @@ #![allow(invalid_value)] #![allow(enum_intrinsics_non_enums)] #![allow(non_fmt_panics)] +#![allow(named_arguments_used_positionally)] #![allow(temporary_cstring_as_ptr)] #![allow(unknown_lints)] #![allow(unused_labels)] @@ -69,6 +70,7 @@ #![warn(invalid_value)] #![warn(enum_intrinsics_non_enums)] #![warn(non_fmt_panics)] +#![warn(named_arguments_used_positionally)] #![warn(temporary_cstring_as_ptr)] #![warn(unknown_lints)] #![warn(unused_labels)] diff --git a/src/tools/clippy/tests/ui/rename.rs b/src/tools/clippy/tests/ui/rename.rs index 9153c0dab0290..e8f57597d02b5 100644 --- a/src/tools/clippy/tests/ui/rename.rs +++ b/src/tools/clippy/tests/ui/rename.rs @@ -32,6 +32,7 @@ #![allow(invalid_value)] #![allow(enum_intrinsics_non_enums)] #![allow(non_fmt_panics)] +#![allow(named_arguments_used_positionally)] #![allow(temporary_cstring_as_ptr)] #![allow(unknown_lints)] #![allow(unused_labels)] @@ -69,6 +70,7 @@ #![warn(clippy::invalid_ref)] #![warn(clippy::mem_discriminant_non_enum)] #![warn(clippy::panic_params)] +#![warn(clippy::positional_named_format_parameters)] #![warn(clippy::temporary_cstring_as_ptr)] #![warn(clippy::unknown_clippy_lints)] #![warn(clippy::unused_label)] diff --git a/src/tools/clippy/tests/ui/rename.stderr b/src/tools/clippy/tests/ui/rename.stderr index 9c03ea914bb65..31865a7f66d60 100644 --- a/src/tools/clippy/tests/ui/rename.stderr +++ b/src/tools/clippy/tests/ui/rename.stderr @@ -1,5 +1,5 @@ error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names` - --> $DIR/rename.rs:38:9 + --> $DIR/rename.rs:39:9 | LL | #![warn(clippy::blacklisted_name)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_names` @@ -7,220 +7,226 @@ LL | #![warn(clippy::blacklisted_name)] = note: `-D renamed-and-removed-lints` implied by `-D warnings` error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions` - --> $DIR/rename.rs:39:9 + --> $DIR/rename.rs:40: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:40:9 + --> $DIR/rename.rs:41:9 | LL | #![warn(clippy::block_in_if_condition_stmt)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` - --> $DIR/rename.rs:41:9 + --> $DIR/rename.rs:42:9 | LL | #![warn(clippy::box_vec)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` - --> $DIR/rename.rs:42:9 + --> $DIR/rename.rs:43: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:43:9 + --> $DIR/rename.rs:44:9 | LL | #![warn(clippy::cyclomatic_complexity)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` - --> $DIR/rename.rs:44:9 + --> $DIR/rename.rs:45:9 | LL | #![warn(clippy::disallowed_method)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` - --> $DIR/rename.rs:45:9 + --> $DIR/rename.rs:46:9 | LL | #![warn(clippy::disallowed_type)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression` - --> $DIR/rename.rs:46:9 + --> $DIR/rename.rs:47:9 | LL | #![warn(clippy::eval_order_dependence)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` error: lint `clippy::for_loop_over_option` has been renamed to `clippy::for_loops_over_fallibles` - --> $DIR/rename.rs:47:9 + --> $DIR/rename.rs:48: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:48:9 + --> $DIR/rename.rs:49: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:49:9 + --> $DIR/rename.rs:50:9 | LL | #![warn(clippy::identity_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` - --> $DIR/rename.rs:50:9 + --> $DIR/rename.rs:51:9 | LL | #![warn(clippy::if_let_some_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr` - --> $DIR/rename.rs:51:9 + --> $DIR/rename.rs:52:9 | LL | #![warn(clippy::logic_bug)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr` error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` - --> $DIR/rename.rs:52:9 + --> $DIR/rename.rs:53:9 | LL | #![warn(clippy::new_without_default_derive)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` - --> $DIR/rename.rs:53:9 + --> $DIR/rename.rs:54:9 | LL | #![warn(clippy::option_and_then_some)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used` - --> $DIR/rename.rs:54:9 + --> $DIR/rename.rs:55:9 | LL | #![warn(clippy::option_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:55:9 + --> $DIR/rename.rs:56: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:56:9 + --> $DIR/rename.rs:57:9 | LL | #![warn(clippy::option_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:57:9 + --> $DIR/rename.rs:58:9 | LL | #![warn(clippy::option_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` - --> $DIR/rename.rs:58:9 + --> $DIR/rename.rs:59:9 | LL | #![warn(clippy::ref_in_deref)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` - --> $DIR/rename.rs:59:9 + --> $DIR/rename.rs:60:9 | LL | #![warn(clippy::result_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:60:9 + --> $DIR/rename.rs:61:9 | LL | #![warn(clippy::result_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` - --> $DIR/rename.rs:61:9 + --> $DIR/rename.rs:62:9 | LL | #![warn(clippy::result_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` - --> $DIR/rename.rs:62:9 + --> $DIR/rename.rs:63:9 | LL | #![warn(clippy::single_char_push_str)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` - --> $DIR/rename.rs:63:9 + --> $DIR/rename.rs:64:9 | LL | #![warn(clippy::stutter)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` - --> $DIR/rename.rs:64:9 + --> $DIR/rename.rs:65:9 | LL | #![warn(clippy::to_string_in_display)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` - --> $DIR/rename.rs:65:9 + --> $DIR/rename.rs:66:9 | LL | #![warn(clippy::zero_width_space)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` - --> $DIR/rename.rs:66:9 + --> $DIR/rename.rs:67:9 | LL | #![warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` - --> $DIR/rename.rs:67:9 + --> $DIR/rename.rs:68:9 | LL | #![warn(clippy::into_iter_on_array)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` - --> $DIR/rename.rs:68:9 + --> $DIR/rename.rs:69:9 | LL | #![warn(clippy::invalid_atomic_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` error: lint `clippy::invalid_ref` has been renamed to `invalid_value` - --> $DIR/rename.rs:69:9 + --> $DIR/rename.rs:70:9 | LL | #![warn(clippy::invalid_ref)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` - --> $DIR/rename.rs:70:9 + --> $DIR/rename.rs:71:9 | LL | #![warn(clippy::mem_discriminant_non_enum)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` - --> $DIR/rename.rs:71:9 + --> $DIR/rename.rs:72:9 | LL | #![warn(clippy::panic_params)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` +error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally` + --> $DIR/rename.rs:73:9 + | +LL | #![warn(clippy::positional_named_format_parameters)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally` + error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr` - --> $DIR/rename.rs:72:9 + --> $DIR/rename.rs:74:9 | LL | #![warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr` error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` - --> $DIR/rename.rs:73:9 + --> $DIR/rename.rs:75:9 | LL | #![warn(clippy::unknown_clippy_lints)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` error: lint `clippy::unused_label` has been renamed to `unused_labels` - --> $DIR/rename.rs:74:9 + --> $DIR/rename.rs:76:9 | LL | #![warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` -error: aborting due to 37 previous errors +error: aborting due to 38 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed b/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed index a920c63b199c1..f97583aa22f9a 100644 --- a/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed @@ -417,3 +417,12 @@ mod issue_9351 { predicates_are_satisfied(id("abc".to_string())); } } + +mod issue_9504 { + #![allow(dead_code)] + + async fn foo>(_: S) {} + async fn bar() { + foo(std::path::PathBuf::new().to_string_lossy().to_string()).await; + } +} diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.rs b/src/tools/clippy/tests/ui/unnecessary_to_owned.rs index 2128bdacddadf..aa5394a565790 100644 --- a/src/tools/clippy/tests/ui/unnecessary_to_owned.rs +++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.rs @@ -417,3 +417,12 @@ mod issue_9351 { predicates_are_satisfied(id("abc".to_string())); } } + +mod issue_9504 { + #![allow(dead_code)] + + async fn foo>(_: S) {} + async fn bar() { + foo(std::path::PathBuf::new().to_string_lossy().to_string()).await; + } +} diff --git a/src/tools/clippy/tests/ui/unused_peekable.rs b/src/tools/clippy/tests/ui/unused_peekable.rs index 153457e367165..7374dfdf92e87 100644 --- a/src/tools/clippy/tests/ui/unused_peekable.rs +++ b/src/tools/clippy/tests/ui/unused_peekable.rs @@ -57,12 +57,22 @@ fn valid() { impl PeekableConsumer { fn consume(&self, _: Peekable>) {} fn consume_mut_ref(&self, _: &mut Peekable>) {} + fn consume_assoc(_: Peekable>) {} + fn consume_assoc_mut_ref(_: &mut Peekable>) {} } - let peekable_consumer = PeekableConsumer; - let mut passed_along_to_method = std::iter::empty::().peekable(); - peekable_consumer.consume_mut_ref(&mut passed_along_to_method); - peekable_consumer.consume(passed_along_to_method); + + let peekable = std::iter::empty::().peekable(); + peekable_consumer.consume(peekable); + + let mut peekable = std::iter::empty::().peekable(); + peekable_consumer.consume_mut_ref(&mut peekable); + + let peekable = std::iter::empty::().peekable(); + PeekableConsumer::consume_assoc(peekable); + + let mut peekable = std::iter::empty::().peekable(); + PeekableConsumer::consume_assoc_mut_ref(&mut peekable); // `peek` called in another block let mut peekable_in_block = std::iter::empty::().peekable(); @@ -141,4 +151,19 @@ fn valid() { { peekable_last_expr.peek(); } + + let mut peek_in_closure = std::iter::empty::().peekable(); + let _ = || { + let _ = peek_in_closure.peek(); + }; + + trait PeekTrait {} + impl PeekTrait for Peekable where I: Iterator {} + + let mut peekable = std::iter::empty::().peekable(); + let _dyn = &mut peekable as &mut dyn PeekTrait; + + fn takes_dyn(_: &mut dyn PeekTrait) {} + let mut peekable = std::iter::empty::().peekable(); + takes_dyn(&mut peekable); } diff --git a/src/tools/clippy/tests/ui/use_self.fixed b/src/tools/clippy/tests/ui/use_self.fixed index 4f80aaecc902d..37986187da179 100644 --- a/src/tools/clippy/tests/ui/use_self.fixed +++ b/src/tools/clippy/tests/ui/use_self.fixed @@ -608,3 +608,12 @@ mod issue8845 { } } } + +mod issue6902 { + use serde::Serialize; + + #[derive(Serialize)] + pub enum Foo { + Bar = 1, + } +} diff --git a/src/tools/clippy/tests/ui/use_self.rs b/src/tools/clippy/tests/ui/use_self.rs index 52da72db53ce3..1b2b3337c92ed 100644 --- a/src/tools/clippy/tests/ui/use_self.rs +++ b/src/tools/clippy/tests/ui/use_self.rs @@ -608,3 +608,12 @@ mod issue8845 { } } } + +mod issue6902 { + use serde::Serialize; + + #[derive(Serialize)] + pub enum Foo { + Bar = 1, + } +} diff --git a/src/tools/clippy/tests/ui/write_literal.rs b/src/tools/clippy/tests/ui/write_literal.rs index 4466917441162..5892818aa9a61 100644 --- a/src/tools/clippy/tests/ui/write_literal.rs +++ b/src/tools/clippy/tests/ui/write_literal.rs @@ -25,11 +25,13 @@ fn main() { writeln!(v, "{} of {:b} people know binary, the other half doesn't", 1, 2); writeln!(v, "10 / 4 is {}", 2.5); writeln!(v, "2 + 1 = {}", 3); + writeln!(v, "From expansion {}", stringify!(not a string literal)); // these should throw warnings write!(v, "Hello {}", "world"); writeln!(v, "Hello {} {}", world, "world"); writeln!(v, "Hello {}", "world"); + writeln!(v, "{} {:.4}", "a literal", 5); // positional args don't change the fact // that we're using a literal -- this should diff --git a/src/tools/clippy/tests/ui/write_literal.stderr b/src/tools/clippy/tests/ui/write_literal.stderr index 3c5ec91d3e0fb..1e306ae28a263 100644 --- a/src/tools/clippy/tests/ui/write_literal.stderr +++ b/src/tools/clippy/tests/ui/write_literal.stderr @@ -1,5 +1,5 @@ error: literal with an empty format string - --> $DIR/write_literal.rs:30:27 + --> $DIR/write_literal.rs:31:27 | LL | write!(v, "Hello {}", "world"); | ^^^^^^^ @@ -12,7 +12,7 @@ LL + write!(v, "Hello world"); | error: literal with an empty format string - --> $DIR/write_literal.rs:31:39 + --> $DIR/write_literal.rs:32:39 | LL | writeln!(v, "Hello {} {}", world, "world"); | ^^^^^^^ @@ -24,7 +24,7 @@ LL + writeln!(v, "Hello {} world", world); | error: literal with an empty format string - --> $DIR/write_literal.rs:32:29 + --> $DIR/write_literal.rs:33:29 | LL | writeln!(v, "Hello {}", "world"); | ^^^^^^^ @@ -36,7 +36,19 @@ LL + writeln!(v, "Hello world"); | error: literal with an empty format string - --> $DIR/write_literal.rs:37:28 + --> $DIR/write_literal.rs:34:29 + | +LL | writeln!(v, "{} {:.4}", "a literal", 5); + | ^^^^^^^^^^^ + | +help: try this + | +LL - writeln!(v, "{} {:.4}", "a literal", 5); +LL + writeln!(v, "a literal {:.4}", 5); + | + +error: literal with an empty format string + --> $DIR/write_literal.rs:39:28 | LL | writeln!(v, "{0} {1}", "hello", "world"); | ^^^^^^^ @@ -48,7 +60,7 @@ LL + writeln!(v, "hello {1}", "world"); | error: literal with an empty format string - --> $DIR/write_literal.rs:37:37 + --> $DIR/write_literal.rs:39:37 | LL | writeln!(v, "{0} {1}", "hello", "world"); | ^^^^^^^ @@ -60,34 +72,34 @@ LL + writeln!(v, "{0} world", "hello"); | error: literal with an empty format string - --> $DIR/write_literal.rs:38:28 + --> $DIR/write_literal.rs:40:37 | LL | writeln!(v, "{1} {0}", "hello", "world"); - | ^^^^^^^ + | ^^^^^^^ | help: try this | LL - writeln!(v, "{1} {0}", "hello", "world"); -LL + writeln!(v, "{1} hello", "world"); +LL + writeln!(v, "world {0}", "hello"); | error: literal with an empty format string - --> $DIR/write_literal.rs:38:37 + --> $DIR/write_literal.rs:40:28 | LL | writeln!(v, "{1} {0}", "hello", "world"); - | ^^^^^^^ + | ^^^^^^^ | help: try this | LL - writeln!(v, "{1} {0}", "hello", "world"); -LL + writeln!(v, "world {0}", "hello"); +LL + writeln!(v, "{1} hello", "world"); | error: literal with an empty format string - --> $DIR/write_literal.rs:41:32 + --> $DIR/write_literal.rs:43:38 | LL | writeln!(v, "{foo} {bar}", foo = "hello", bar = "world"); - | ^^^^^^^^^^^^^ + | ^^^^^^^ | help: try this | @@ -96,10 +108,10 @@ LL + writeln!(v, "hello {bar}", bar = "world"); | error: literal with an empty format string - --> $DIR/write_literal.rs:41:47 + --> $DIR/write_literal.rs:43:53 | LL | writeln!(v, "{foo} {bar}", foo = "hello", bar = "world"); - | ^^^^^^^^^^^^^ + | ^^^^^^^ | help: try this | @@ -108,28 +120,28 @@ LL + writeln!(v, "{foo} world", foo = "hello"); | error: literal with an empty format string - --> $DIR/write_literal.rs:42:32 + --> $DIR/write_literal.rs:44:53 | LL | writeln!(v, "{bar} {foo}", foo = "hello", bar = "world"); - | ^^^^^^^^^^^^^ + | ^^^^^^^ | help: try this | LL - writeln!(v, "{bar} {foo}", foo = "hello", bar = "world"); -LL + writeln!(v, "{bar} hello", bar = "world"); +LL + writeln!(v, "world {foo}", foo = "hello"); | error: literal with an empty format string - --> $DIR/write_literal.rs:42:47 + --> $DIR/write_literal.rs:44:38 | LL | writeln!(v, "{bar} {foo}", foo = "hello", bar = "world"); - | ^^^^^^^^^^^^^ + | ^^^^^^^ | help: try this | LL - writeln!(v, "{bar} {foo}", foo = "hello", bar = "world"); -LL + writeln!(v, "world {foo}", foo = "hello"); +LL + writeln!(v, "{bar} hello", bar = "world"); | -error: aborting due to 11 previous errors +error: aborting due to 12 previous errors diff --git a/src/tools/clippy/tests/ui/write_literal_2.rs b/src/tools/clippy/tests/ui/write_literal_2.rs index ba0d7be5eaa68..55a11daa1d344 100644 --- a/src/tools/clippy/tests/ui/write_literal_2.rs +++ b/src/tools/clippy/tests/ui/write_literal_2.rs @@ -10,7 +10,7 @@ fn main() { writeln!(v, r"{}", r"{hello}"); writeln!(v, "{}", '\''); writeln!(v, "{}", '"'); - writeln!(v, r"{}", '"'); // don't lint + writeln!(v, r"{}", '"'); writeln!(v, r"{}", '\''); writeln!( v, @@ -24,4 +24,11 @@ fn main() { {} \\ {}", "1", "2", "3", ); + writeln!(v, "{}", "\\"); + writeln!(v, r"{}", "\\"); + writeln!(v, r#"{}"#, "\\"); + writeln!(v, "{}", r"\"); + writeln!(v, "{}", "\r"); + writeln!(v, r#"{}{}"#, '#', '"'); // hard mode + writeln!(v, r"{}", "\r"); // should not lint } diff --git a/src/tools/clippy/tests/ui/write_literal_2.stderr b/src/tools/clippy/tests/ui/write_literal_2.stderr index 9ff297069c402..d5956db9ff0b4 100644 --- a/src/tools/clippy/tests/ui/write_literal_2.stderr +++ b/src/tools/clippy/tests/ui/write_literal_2.stderr @@ -47,6 +47,12 @@ LL - writeln!(v, "{}", '"'); LL + writeln!(v, "/""); | +error: literal with an empty format string + --> $DIR/write_literal_2.rs:13:24 + | +LL | writeln!(v, r"{}", '"'); + | ^^^ + error: literal with an empty format string --> $DIR/write_literal_2.rs:14:24 | @@ -108,5 +114,77 @@ LL ~ {} / 3", LL ~ "1", "2", | -error: aborting due to 9 previous errors +error: literal with an empty format string + --> $DIR/write_literal_2.rs:27:23 + | +LL | writeln!(v, "{}", "/"); + | ^^^^ + | +help: try this + | +LL - writeln!(v, "{}", "/"); +LL + writeln!(v, "/"); + | + +error: literal with an empty format string + --> $DIR/write_literal_2.rs:28:24 + | +LL | writeln!(v, r"{}", "/"); + | ^^^^ + | +help: try this + | +LL - writeln!(v, r"{}", "/"); +LL + writeln!(v, r"/"); + | + +error: literal with an empty format string + --> $DIR/write_literal_2.rs:29:26 + | +LL | writeln!(v, r#"{}"#, "/"); + | ^^^^ + | +help: try this + | +LL - writeln!(v, r#"{}"#, "/"); +LL + writeln!(v, r#"/"#); + | + +error: literal with an empty format string + --> $DIR/write_literal_2.rs:30:23 + | +LL | writeln!(v, "{}", r"/"); + | ^^^^ + | +help: try this + | +LL - writeln!(v, "{}", r"/"); +LL + writeln!(v, "/"); + | + +error: literal with an empty format string + --> $DIR/write_literal_2.rs:31:23 + | +LL | writeln!(v, "{}", "/r"); + | ^^^^ + | +help: try this + | +LL - writeln!(v, "{}", "/r"); +LL + writeln!(v, "/r"); + | + +error: literal with an empty format string + --> $DIR/write_literal_2.rs:32:28 + | +LL | writeln!(v, r#"{}{}"#, '#', '"'); // hard mode + | ^^^ + +error: literal with an empty format string + --> $DIR/write_literal_2.rs:32:33 + | +LL | writeln!(v, r#"{}{}"#, '#', '"'); // hard mode + | ^^^ + +error: aborting due to 17 previous errors diff --git a/src/tools/clippy/tests/ui/write_with_newline.rs b/src/tools/clippy/tests/ui/write_with_newline.rs index 446d6914d3461..b79364c8758c5 100644 --- a/src/tools/clippy/tests/ui/write_with_newline.rs +++ b/src/tools/clippy/tests/ui/write_with_newline.rs @@ -56,4 +56,12 @@ fn main() { write!(v, "foo\r\n"); write!(v, "\\r\n"); //~ ERROR write!(v, "foo\rbar\n"); + + // Ignore expanded format strings + macro_rules! newline { + () => { + "\n" + }; + } + write!(v, newline!()); } diff --git a/src/tools/clippy/tests/ui/write_with_newline.stderr b/src/tools/clippy/tests/ui/write_with_newline.stderr index 5f55431be0bd9..2baaea166d8ef 100644 --- a/src/tools/clippy/tests/ui/write_with_newline.stderr +++ b/src/tools/clippy/tests/ui/write_with_newline.stderr @@ -5,7 +5,7 @@ LL | write!(v, "Hello/n"); | ^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::write-with-newline` implied by `-D warnings` -help: use `writeln!()` instead +help: use `writeln!` instead | LL - write!(v, "Hello/n"); LL + writeln!(v, "Hello"); @@ -17,7 +17,7 @@ error: using `write!()` with a format string that ends in a single newline LL | write!(v, "Hello {}/n", "world"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: use `writeln!()` instead +help: use `writeln!` instead | LL - write!(v, "Hello {}/n", "world"); LL + writeln!(v, "Hello {}", "world"); @@ -29,7 +29,7 @@ error: using `write!()` with a format string that ends in a single newline LL | write!(v, "Hello {} {}/n", "world", "#2"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: use `writeln!()` instead +help: use `writeln!` instead | LL - write!(v, "Hello {} {}/n", "world", "#2"); LL + writeln!(v, "Hello {} {}", "world", "#2"); @@ -41,7 +41,7 @@ error: using `write!()` with a format string that ends in a single newline LL | write!(v, "{}/n", 1265); | ^^^^^^^^^^^^^^^^^^^^^^^ | -help: use `writeln!()` instead +help: use `writeln!` instead | LL - write!(v, "{}/n", 1265); LL + writeln!(v, "{}", 1265); @@ -53,7 +53,7 @@ error: using `write!()` with a format string that ends in a single newline LL | write!(v, "/n"); | ^^^^^^^^^^^^^^^ | -help: use `writeln!()` instead +help: use `writeln!` instead | LL - write!(v, "/n"); LL + writeln!(v); @@ -65,7 +65,7 @@ error: using `write!()` with a format string that ends in a single newline LL | write!(v, "//n"); // should fail | ^^^^^^^^^^^^^^^^^ | -help: use `writeln!()` instead +help: use `writeln!` instead | LL - write!(v, "//n"); // should fail LL + writeln!(v, "/"); // should fail @@ -81,11 +81,10 @@ LL | | " LL | | ); | |_____^ | -help: use `writeln!()` instead +help: use `writeln!` instead | LL ~ writeln!( -LL | v, -LL ~ "" +LL ~ v | error: using `write!()` with a format string that ends in a single newline @@ -98,11 +97,10 @@ LL | | " LL | | ); | |_____^ | -help: use `writeln!()` instead +help: use `writeln!` instead | LL ~ writeln!( -LL | v, -LL ~ r"" +LL ~ v | error: using `write!()` with a format string that ends in a single newline @@ -111,23 +109,11 @@ error: using `write!()` with a format string that ends in a single newline LL | write!(v, "/r/n"); //~ ERROR | ^^^^^^^^^^^^^^^^^^ | -help: use `writeln!()` instead +help: use `writeln!` instead | LL - write!(v, "/r/n"); //~ ERROR LL + writeln!(v, "/r"); //~ ERROR | -error: using `write!()` with a format string that ends in a single newline - --> $DIR/write_with_newline.rs:58:5 - | -LL | write!(v, "foo/rbar/n"); - | ^^^^^^^^^^^^^^^^^^^^^^^ - | -help: use `writeln!()` instead - | -LL - write!(v, "foo/rbar/n"); -LL + writeln!(v, "foo/rbar"); - | - -error: aborting due to 10 previous errors +error: aborting due to 9 previous errors diff --git a/src/tools/clippy/tests/ui/writeln_empty_string.stderr b/src/tools/clippy/tests/ui/writeln_empty_string.stderr index ac65aadfc0e89..25e69ec48e7e2 100644 --- a/src/tools/clippy/tests/ui/writeln_empty_string.stderr +++ b/src/tools/clippy/tests/ui/writeln_empty_string.stderr @@ -1,16 +1,20 @@ -error: using `writeln!(v, "")` +error: empty string literal in `writeln!` --> $DIR/writeln_empty_string.rs:11:5 | LL | writeln!(v, ""); - | ^^^^^^^^^^^^^^^ help: replace it with: `writeln!(v)` + | ^^^^^^^^^^----^ + | | + | help: remove the empty string | = note: `-D clippy::writeln-empty-string` implied by `-D warnings` -error: using `writeln!(suggestion, "")` +error: empty string literal in `writeln!` --> $DIR/writeln_empty_string.rs:14:5 | LL | writeln!(suggestion, ""); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `writeln!(suggestion)` + | ^^^^^^^^^^^^^^^^^^^----^ + | | + | help: remove the empty string error: aborting due to 2 previous errors