]> git.lizzy.rs Git - rust.git/commitdiff
Merge commit '7248d06384c6a90de58c04c1f46be88821278d8b' into sync-from-clippy
authorDavid Koloski <dkoloski@google.com>
Wed, 21 Sep 2022 17:02:37 +0000 (13:02 -0400)
committerDavid Koloski <dkoloski@google.com>
Wed, 21 Sep 2022 17:13:27 +0000 (13:13 -0400)
111 files changed:
src/tools/clippy/CHANGELOG.md
src/tools/clippy/CONTRIBUTING.md
src/tools/clippy/book/src/development/adding_lints.md
src/tools/clippy/book/src/development/common_tools_writing_lints.md
src/tools/clippy/clippy_dev/src/new_lint.rs
src/tools/clippy/clippy_lints/src/almost_complete_letter_range.rs
src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs
src/tools/clippy/clippy_lints/src/bool_to_int_with_if.rs
src/tools/clippy/clippy_lints/src/booleans.rs
src/tools/clippy/clippy_lints/src/dereference.rs
src/tools/clippy/clippy_lints/src/derivable_impls.rs
src/tools/clippy/clippy_lints/src/format.rs
src/tools/clippy/clippy_lints/src/format_args.rs
src/tools/clippy/clippy_lints/src/large_stack_arrays.rs
src/tools/clippy/clippy_lints/src/lib.register_all.rs
src/tools/clippy/clippy_lints/src/lib.register_complexity.rs
src/tools/clippy/clippy_lints/src/lib.register_lints.rs
src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs
src/tools/clippy/clippy_lints/src/lib.rs
src/tools/clippy/clippy_lints/src/methods/err_expect.rs
src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/methods/mod.rs
src/tools/clippy/clippy_lints/src/methods/ok_expect.rs
src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
src/tools/clippy/clippy_lints/src/module_style.rs
src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs
src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs
src/tools/clippy/clippy_lints/src/renamed_lints.rs
src/tools/clippy/clippy_lints/src/unused_peekable.rs
src/tools/clippy/clippy_lints/src/use_self.rs
src/tools/clippy/clippy_lints/src/utils/conf.rs
src/tools/clippy/clippy_lints/src/write.rs
src/tools/clippy/clippy_utils/src/consts.rs
src/tools/clippy/clippy_utils/src/macros.rs
src/tools/clippy/clippy_utils/src/sugg.rs
src/tools/clippy/clippy_utils/src/ty.rs
src/tools/clippy/src/docs.rs
src/tools/clippy/src/docs/iter_kv_map.txt [new file with mode: 0644]
src/tools/clippy/src/docs/positional_named_format_parameters.txt [deleted file]
src/tools/clippy/src/docs/print_literal.txt
src/tools/clippy/src/docs/print_stderr.txt
src/tools/clippy/src/docs/print_stdout.txt
src/tools/clippy/src/docs/write_literal.txt
src/tools/clippy/tests/ui-cargo/module_style/fail_mod_remap/Cargo.toml [new file with mode: 0644]
src/tools/clippy/tests/ui-cargo/module_style/fail_mod_remap/src/bad.rs [new file with mode: 0644]
src/tools/clippy/tests/ui-cargo/module_style/fail_mod_remap/src/bad/inner.rs [new file with mode: 0644]
src/tools/clippy/tests/ui-cargo/module_style/fail_mod_remap/src/main.rs [new file with mode: 0644]
src/tools/clippy/tests/ui-cargo/module_style/fail_mod_remap/src/main.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs
src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr
src/tools/clippy/tests/ui/almost_complete_letter_range.fixed
src/tools/clippy/tests/ui/almost_complete_letter_range.rs
src/tools/clippy/tests/ui/almost_complete_letter_range.stderr
src/tools/clippy/tests/ui/arithmetic_side_effects.rs
src/tools/clippy/tests/ui/arithmetic_side_effects.stderr
src/tools/clippy/tests/ui/assertions_on_result_states.fixed
src/tools/clippy/tests/ui/assertions_on_result_states.rs
src/tools/clippy/tests/ui/assertions_on_result_states.stderr
src/tools/clippy/tests/ui/auxiliary/macro_rules.rs
src/tools/clippy/tests/ui/bool_to_int_with_if.fixed
src/tools/clippy/tests/ui/bool_to_int_with_if.rs
src/tools/clippy/tests/ui/bool_to_int_with_if.stderr
src/tools/clippy/tests/ui/collapsible_if.fixed
src/tools/clippy/tests/ui/collapsible_if.rs
src/tools/clippy/tests/ui/collapsible_if.stderr
src/tools/clippy/tests/ui/crashes/ice-9463.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/crashes/ice-9463.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/derivable_impls.fixed [new file with mode: 0644]
src/tools/clippy/tests/ui/derivable_impls.rs
src/tools/clippy/tests/ui/derivable_impls.stderr
src/tools/clippy/tests/ui/eprint_with_newline.rs
src/tools/clippy/tests/ui/eprint_with_newline.stderr
src/tools/clippy/tests/ui/explicit_write.fixed
src/tools/clippy/tests/ui/explicit_write.rs
src/tools/clippy/tests/ui/explicit_write.stderr
src/tools/clippy/tests/ui/format.fixed
src/tools/clippy/tests/ui/format.rs
src/tools/clippy/tests/ui/format.stderr
src/tools/clippy/tests/ui/iter_kv_map.fixed [new file with mode: 0644]
src/tools/clippy/tests/ui/iter_kv_map.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/iter_kv_map.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/large_enum_variant.rs
src/tools/clippy/tests/ui/large_enum_variant.stderr
src/tools/clippy/tests/ui/large_stack_arrays.rs
src/tools/clippy/tests/ui/large_stack_arrays.stderr
src/tools/clippy/tests/ui/len_without_is_empty.rs
src/tools/clippy/tests/ui/nonminimal_bool.rs
src/tools/clippy/tests/ui/nonminimal_bool.stderr
src/tools/clippy/tests/ui/positional_named_format_parameters.fixed [deleted file]
src/tools/clippy/tests/ui/positional_named_format_parameters.rs [deleted file]
src/tools/clippy/tests/ui/positional_named_format_parameters.stderr [deleted file]
src/tools/clippy/tests/ui/print_literal.rs
src/tools/clippy/tests/ui/print_literal.stderr
src/tools/clippy/tests/ui/print_with_newline.rs
src/tools/clippy/tests/ui/print_with_newline.stderr
src/tools/clippy/tests/ui/println_empty_string.stderr
src/tools/clippy/tests/ui/rename.fixed
src/tools/clippy/tests/ui/rename.rs
src/tools/clippy/tests/ui/rename.stderr
src/tools/clippy/tests/ui/unnecessary_to_owned.fixed
src/tools/clippy/tests/ui/unnecessary_to_owned.rs
src/tools/clippy/tests/ui/unused_peekable.rs
src/tools/clippy/tests/ui/use_self.fixed
src/tools/clippy/tests/ui/use_self.rs
src/tools/clippy/tests/ui/write_literal.rs
src/tools/clippy/tests/ui/write_literal.stderr
src/tools/clippy/tests/ui/write_literal_2.rs
src/tools/clippy/tests/ui/write_literal_2.stderr
src/tools/clippy/tests/ui/write_with_newline.rs
src/tools/clippy/tests/ui/write_with_newline.stderr
src/tools/clippy/tests/ui/writeln_empty_string.stderr

index d847e4c749481fbc06566e2c3bde97064e77f0f0..044cbff4b78e5a8156384bbc18e7a0d0a25ecb7c 100644 (file)
@@ -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
index 28b4cfd5f099520cf023e30fbec1d8953b9b29d9..6c977b2cacab537db551adb40801a1d277acc0ca 100644 (file)
@@ -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
 
index da781eb970df7be3ea597eb44a296da1c51813b0..b1e843bc7f4c86173dd8db2893921374abe5d384 100644 (file)
@@ -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
index 2bc275ceff0be50cad4cabffd08cbe40e1751958..f5aa06e4bf653136b31f17ca3fcf57417796c9c9 100644 (file)
@@ -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");
index 331b76484b8a5d5f62992a34d73de6fadbf72c93..02cb13a1d8afe1dc50b0a912ab979efd857050ea 100644 (file)
@@ -188,6 +188,7 @@ fn parse_manifest(contents: &str) -> Option<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() {{
index 59a7c535400699a9450753d9675cf8c49ed30c5f..073e4af1318e35044fb1d228ab6ae4e4e5ee7063 100644 (file)
@@ -4,6 +4,7 @@
 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,
index 7cd198ace86c0e3ff3f3edf4c9b44dc862b68429..656dc5feeb57029d3023de76251e8ef341d70ea2 100644 (file)
@@ -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 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
                     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 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
                         "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 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
                         "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 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
     }
 }
 
-/// 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()
 }
index a4b8cbb0d82aaf8294ee89a9b9303efb283f7b94..51e98cda845193958d907b4fc721587dda8118a7 100644 (file)
@@ -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()
-}
index 656d639f0efd9838c272c92d6aa0e5d2e3fa7179..03d262d5a59c64e2afbd25af634984080c9721b8 100644 (file)
@@ -237,7 +237,7 @@ fn recurse(&mut self, suggestion: &Bool) -> Option<()> {
                 }
             },
             &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);
             },
         }
index 45b5d79d4452247bd335409c67a52e13eb84d1ee..501f9ef78aebebc611eeeede1e82a6b1962e4ada 100644 (file)
@@ -297,13 +297,10 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                         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,
                             },
index ef9eeecc6a934f794859510ef414041e51509164..06ae5abeaeb9bcd1433a42b4ba36f5d33207b766 100644 (file)
@@ -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 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
                     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
+                            );
+                        }
                     );
                 }
             }
index 0c5851cdbed2a4241e0a3f2d35901f311dd732ec..f10d825695360c39ebcc22a05a1a69a1653680e7 100644 (file)
@@ -71,12 +71,12 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
             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,
index 2a55c48cf7731abb590b4d392d4512d90453e4fc..9e1eaf248b73cd4a0f83d46eeaa938d0ae62018c 100644 (file)
@@ -77,7 +77,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
             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) {
index 0acbd81aec3438ab99989d2fda6c8960257045b6..5857d81ab1f2094087cd5226850c748f4f160801 100644 (file)
@@ -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 @@ pub fn new(maximum_allowed_size: u64) -> Self {
 
 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, "[...]")
+                  ),
+              );
+          }
     }
 }
index 59d1760fe6064084a08132d20d5bcf3d70b1ee02..8718d5fa1da0b4225d0b960571e1c26be367813a 100644 (file)
     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),
     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),
index aa247352f88fb653ccbc6572c45bb74caedc63c7..185189a6af5b63f02de49bb0384e59f5c4553af8 100644 (file)
@@ -40,6 +40,7 @@
     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),
index 962e67220069a604992b0040cf38afec55d06656..02fcc8de50727c90179322b374176b460edf9a65 100644 (file)
     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,
     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,
index bede91f183e793ff697815825f7321a09bdaff10..6125d0f7a8628db472410845e91d5b40536b7f21 100644 (file)
@@ -35,5 +35,4 @@
     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),
 ])
index ceaaf5c6d6ed6b8f3522a2c9e4031ac59b316318..298566cb5b625f4f7ce968b65b7fc3dd22bb0e56 100644 (file)
@@ -40,7 +40,6 @@
 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));
index 570a1b87358ddb042699df7a3b3b778c7f125a46..720d9a68c85ee81bf43e2f34ed7fe7042415110b 100644 (file)
@@ -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<T, E>` is not None
         if let Some(data_type) = get_data_type(cx, result_type);
         // Tests if the T type in a `Result<T, E>` 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<Ty<'a>> {
         _ => 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 (file)
index 0000000..a7eecab
--- /dev/null
@@ -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,
+    }
+}
index 41942b20ea163f34e0e60d47b489b19870b145aa..cdde4c54d6378d283c1ff22262d6062e3794f535 100644 (file)
@@ -35,6 +35,7 @@
 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;
     "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<u32, u32> = HashMap::new();
+    /// let values = map.iter().map(|(_, value)| value).collect::<Vec<_>>();
+    /// ```
+    ///
+    /// Use instead:
+    /// ```
+    /// # use std::collections::HashMap;
+    /// let map: HashMap<u32, u32> = HashMap::new();
+    /// let values = map.values().collect::<Vec<_>>();
+    /// ```
+    #[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<RustcVersion>,
@@ -3159,6 +3191,7 @@ pub fn new(
     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 @@ fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                 (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);
                     }
index d64a9f320d90e175230009b348f76152e9d9462c..646fc4a7bcf37ce6fed29f76b91973ab4e731e96 100644 (file)
@@ -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<Ty<'a>> {
         _ => 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, &[]))
-}
index 8d3cede70f70d3073fe42c07531315715f72c4a3..79d784c342caccac92d0da8031836d4f82334964 100644 (file)
@@ -7,7 +7,7 @@
 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)
index 0a393657267b07833371498eb52b5015faf8c874..22071ab3044f4f7a8271f3df4954e4914c46d4d0 100644 (file)
@@ -2,7 +2,7 @@
 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 @@ fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) {
 
         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 @@ fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) {
         // `{ 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) {
index 4722c031006be465d66049cbab8acfc4ea59c4a8..f86dfb6b8df8497d49352fe5076a744d2976ac6d 100644 (file)
@@ -184,6 +184,10 @@ fn macro_braces(conf: FxHashSet<MacroMatcher>) -> FxHashMap<String, (String, Str
             name: "vec",
             braces: ("[", "]"),
         ),
+        macro_matcher!(
+            name: "matches",
+            braces: ("(", ")"),
+        ),
     ]
     .into_iter()
     .collect::<FxHashMap<_, _>>();
index 83b69fbb3116466f3799a5f9b3d81bb3bb8ae000..d24c57c0a4b8a5ccdcb342a3b89850f9a42d70a0 100644 (file)
@@ -42,27 +42,30 @@ pub fn new(mut allowed: FxHashSet<String>) -> Self {
         }
     }
 
-    /// Checks assign operators (+=, -=, *=, /=) of integers in a non-constant environment that
-    /// won't overflow.
-    fn has_valid_assign_op(op: &Spanned<hir::BinOpKind>, 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<hir::BinOpKind>, 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 @@ fn is_literal_integer(expr: &hir::Expr<'_>, expr_refs: Ty<'_>) -> bool {
     }
 
     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 @@ fn manage_bin_ops(
         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);
     }
 }
 
index 6bea6dc0773d5c2fef2d45e8dc300695ffe7de67..d320eea1c377d3d9284ee85a1beb49eccb0cbca1 100644 (file)
@@ -36,6 +36,7 @@
     ("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"),
index cc86564359459299f050ac36061cf4ed72b1b50f..f1cebf0f9923da572c18738731724b88500f2411 100644 (file)
@@ -6,6 +6,7 @@
 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 @@ fn new(cx: &'a LateContext<'tcx>, expected_hir_id: HirId) -> Self {
     }
 }
 
-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 @@ fn visit_expr(&mut self, ex: &'_ Expr<'_>) {
                                     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 @@ fn visit_expr(&mut self, ex: &'_ Expr<'_>) {
                     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;
                     },
                 }
             }
index 486ea5e5ccfa90c54386b6e7da4971136f9717fd..44ab9bca79596bd8763d52e5c6d9fd3fbad9d7af 100644 (file)
@@ -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 @@ enum StackItem {
 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 @@ fn check_item(&mut self, _cx: &LateContext<'_>, item: &Item<'_>) {
             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 @@ fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) {
                 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);
             }
index a8500beb25747aa9d95516cd24bbeaffac094e79..2be3fa99c811cde98fb4eef4f7342429bdefaacb 100644 (file)
@@ -476,7 +476,7 @@ pub fn format_error(error: Box<dyn Error>) -> 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();
index 640a09a7a912347396816d4fec4c0b41c5f09c55..06e7d70170171c97c0ffa5292fd738903407f830 100644 (file)
@@ -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
     /// 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
     /// 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
     /// (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");
     /// (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;
     "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 @@ pub struct 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, &macro_call, name);
+            },
+            sym::println_macro | sym::eprintln_macro | sym::writeln_macro => {
+                check_empty_string(cx, &format_args, &macro_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<Vec<Span>>,
-    complex_unnamed: Vec<Vec<Span>>,
-    named: Vec<(Symbol, Vec<Span>)>,
+        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<Item = &[Span]> {
-        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<Item = &[Span]> {
-        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<Span> = &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<SimpleFormatArgs> {
-        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<StrLit>, Option<Expr>) {
-        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::<usize>::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<Expr>, _rhs: &P<Expr>) {
-        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<usize>, c: Result<char, EscapeError>| {
-        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<String, UnescapeErr> {
+    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))
 }
index 96c55111fbaffbf7cb417ae9931ff01949db5888..1b8a9c05559a940642af8c8a39690122c331faea 100644 (file)
@@ -501,8 +501,8 @@ fn binop(&mut self, op: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> Option<Cons
                         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 @@ fn binop(&mut self, op: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> Option<Cons
                     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)),
index bd89ff977f87791dd6deacaf193b34e7781ffd62..a1808c0972009bbe2a147695882e60ded4cdf69b 100644 (file)
@@ -7,8 +7,8 @@
 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<usize>,
 }
 
-/// Parses the `fmt` arg of `Arguments::new_v1_formatted(pieces, args, fmt, _)`
-fn parse_rt_fmt<'tcx>(fmt_arg: &'tcx Expr<'tcx>) -> Option<impl Iterator<Item = ParamPosition> + 'tcx> {
-    fn parse_count(expr: &Expr<'_>) -> Option<usize> {
-        // ::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<usize> {
+            // ::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<impl Iterator<Item = ParamPosition> + '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 @@ fn new(spec: rpf::FormatSpec<'_>, positions: ParamPosition, values: &FormatArgsV
         })
     }
 
-    /// 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
     }
 }
 
index cca71bbf76e2877196898bec350b5435dbcbeaf5..f08275a4ac76bb8631ac096363faac8c466b20cc 100644 (file)
@@ -22,7 +22,7 @@
 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 @@ fn hir_from_snippet(expr: &hir::Expr<'_>, get_snippet: impl Fn(Span) -> Cow<'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 @@ fn hir_from_snippet(expr: &hir::Expr<'_>, get_snippet: impl Fn(Span) -> Cow<'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 @@ pub fn ast(cx: &EarlyContext<'_>, expr: &ast::Expr, default: &'a str) -> Self {
             | 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 @@ pub fn ast(cx: &EarlyContext<'_>, expr: &ast::Expr, default: &'a str) -> Self {
             | 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),
             ),
         }
     }
index 926ecf965932159813c541aae58b92844649cf99..56343880320db987130415b432ec9481086822ca 100644 (file)
@@ -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) {
index f3a5048e7fa824994f367ea83ee09805c27dec3b..6c89b4dde372dd04a360d4be038ffafa50de6357 100644 (file)
@@ -221,6 +221,7 @@ pub fn explain(lint: &str) {
     "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 @@ pub fn explain(lint: &str) {
     "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 (file)
index 0000000..a063c81
--- /dev/null
@@ -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<u32, u32> = HashMap::new();
+let values = map.iter().map(|(_, value)| value).collect::<Vec<_>>();
+```
+
+Use instead:
+```
+let map: HashMap<u32, u32> = HashMap::new();
+let values = map.values().collect::<Vec<_>>();
+```
\ 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 (file)
index e391d24..0000000
+++ /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
index 160073414f9af6a5529173affa132f27a6d97661..a6252a68780bb3ad37bab5db83808912e84eeb0e 100644 (file)
@@ -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");
index fc14511cd6a6569f6076cd9dafce259cde18656e..9c6edeeef125df630b79474b4ac115f5b980c705 100644 (file)
@@ -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
 ```
index 6c9a4b98e1e6685b28c944ca436a17ffadc83d84..d2cbd811d1b2a507db8b68c469fc6635f380ddeb 100644 (file)
@@ -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
 ```
index 9c41a48f9f73134c7aa1113aae76fa62f79db36c..a7a884d08711508864bd0c769f9a1275e904ea3c 100644 (file)
@@ -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 (file)
index 0000000..a822fad
--- /dev/null
@@ -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 (file)
index 0000000..509aad1
--- /dev/null
@@ -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 (file)
index 0000000..8b13789
--- /dev/null
@@ -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 (file)
index 0000000..ba4c8c8
--- /dev/null
@@ -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 (file)
index 0000000..46991ff
--- /dev/null
@@ -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
+
index 5b4adc868dff19259611c087a3623dfdacf2d3d0..ed8161acc0efb3010d535434ef9fd32a74b7d33d 100644 (file)
@@ -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
index 039b23b1bdb2f28a02e67619cdf4c5eb0b4f194f..d80ad49f3086d56d26abe5f5aecbad8450fa13d5 100644 (file)
@@ -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
 
index e69b40f35f4c6baf0cc841a6ed9c543fed5694b2..079b7c000dce1e7f953418a356fe2f538d44ad61 100644 (file)
@@ -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() {
index f2240981d45fa34baf23694176a80c73e1d06835..a66900a976efa92d549a8a194c78d3e376a98cdb 100644 (file)
@@ -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() {
index 5b5dc40ee54d0ab6da23e65905ffb5865a5f2b34..3de44c72c1b9d35ad50773e16b143200162da5dd 100644 (file)
@@ -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
 
index f5390c7464250dea1bf9456618e2e06457362f46..dd24f5aa592b3b17094f9d395ba58430bc9ccccd 100644 (file)
@@ -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() {}
index 6c4c8bdec0f0589ded293437129264d1bb412166..a2a856efbffc02f8765e34edd2a67d752ece3bf8 100644 (file)
@@ -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
 
index 795f435f24cd40b3e713959334ff266aaba3cc9f..2bb755290c508b812c6c6ce4a7b53fae5a47f521 100644 (file)
@@ -75,3 +75,9 @@ fn main() {
     let r: Result<Foo, Foo> = Err(Foo);
     assert!(r.is_err());
 }
+
+#[allow(dead_code)]
+fn issue9450() {
+    let res: Result<i32, i32> = Ok(1);
+    res.unwrap_err();
+}
index 1101aec1e1b34d5cfc29a591b1292ff6bc35d3f6..d8a9bd2f1c45576e191d3d02cf71deeb7cd8a8a3 100644 (file)
@@ -75,3 +75,9 @@ fn test_ref_copy_ok(r: &Result<CopyFoo, CopyFoo>) {
     let r: Result<Foo, Foo> = Err(Foo);
     assert!(r.is_err());
 }
+
+#[allow(dead_code)]
+fn issue9450() {
+    let res: Result<i32, i32> = Ok(1);
+    assert!(res.is_err())
+}
index 97a5f3dfca4a65f93f8d49f1f326fea941531c04..298d63c9c34fb38fc0a304b7aeb09c07db15cdcb 100644 (file)
@@ -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
 
index 83a0af6b87acad985fad701b01885bf3ba9f838c..ef3ca9aea380c0bfba020deffb65b601f56a90b4 100644 (file)
@@ -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';
+    };
+}
index 9c1098dc4c17aa10ea216d3502f8b763b461eb70..2c8339cdd7f8a0fa97d0c3c495fc9d90cfb611f3 100644 (file)
@@ -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
 
index 0c967dac6e2dd906155338b065ab573dca4e5018..5d9496f01775f84505c823fa594c21c43ae78b6b 100644 (file)
@@ -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 {
index 8647a9cffbede3e289c312d1d26f2831031b795d..e695440f6682ff3dd68792a60e6c905cf0f51c3f 100644 (file)
@@ -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
 
index 5b0e4a473c4ad708d167625ed559ec49c1cb6b35..6bb7682bae9530883c8035aa3ca0a1aebf5c5cbe 100644 (file)
@@ -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 {
index cd231a5d7abb0852161d0dab921956d654ae73b4..e216a9ee54c90ee959415569eb612f1a8010dfb9 100644 (file)
@@ -155,6 +155,11 @@ fn truth() -> bool { true }
         if matches!(true, true) {}
     }
 
+    // Issue #9375
+    if matches!(true, true) && truth() {
+        if matches!(true, true) {}
+    }
+
     if true {
         #[cfg(not(teehee))]
         if true {
index 6749612388fe3726374e02b864cc2d3e37343935..6327444df21d7641a151decebff28f3dcbca3a61 100644 (file)
@@ -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 (file)
index 0000000..41ef930
--- /dev/null
@@ -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 (file)
index 0000000..7daa08a
--- /dev/null
@@ -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 (file)
index 0000000..7dcdfb0
--- /dev/null
@@ -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<i32>,
+    e: FooND1,
+    f: FooND2,
+    g: HashMap<i32, i32>,
+    h: (i32, Vec<i32>),
+    i: [Vec<i32>; 3],
+    j: [i32; 5],
+    k: Option<i32>,
+    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<i32>);
+
+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<T> {
+    a: Option<T>,
+}
+
+// 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<T> 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<T> Default for RustIssue26925<T> {
+    fn default() -> Self {
+        Self { a: None }
+    }
+}
+
+struct SpecializedImpl<A, B> {
+    a: A,
+    b: B,
+}
+
+impl<T: Default> Default for SpecializedImpl<T, T> {
+    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<T> {
+    v: Vec<T>,
+}
+
+impl Default for SpecializedImpl2<String> {
+    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() {}
index a6412004726d33302d4e65a26d2fef3221fafb67..625cbcdde230ac14e03efefd0769562b56488188 100644 (file)
@@ -1,3 +1,7 @@
+// run-rustfix
+
+#![allow(dead_code)]
+
 use std::collections::HashMap;
 
 struct FooDefault<'a> {
index 49fb471a21962ce11c69a8b84bccf96a33af3b72..c1db5a58b1f5196085701cec8fa6e95308f445ce 100644 (file)
@@ -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
 
index 8df32649ad948b3d85aa3fae9a2bc468def694eb..de5e121be877460b07e36537f3b6e05ae07fa56d 100644 (file)
@@ -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!());
 }
index f137787bff0c786f5f1f5f85ab03c8e23b3816ef..0eefb9f0ca978bafd91c8d8186321c2f8a37a7d2 100644 (file)
@@ -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
 
index 74d0e5290282aff21105c871e8ba6223477e862b..35283725619ae8bcb5651e80310af5a80d4f2b92 100644 (file)
@@ -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
     {
index e7a698d3e012d35049febcde9222b519904e06e8..be864a55b663ffc9092c2b3083b3e1bc06e82063 100644 (file)
@@ -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
     {
index 29ae0cdece2499e6466a957b8409f1faf007397b..ff05f4343d77d8c600b5c7660d8f585acf8a3430 100644 (file)
@@ -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
 
index b56d6aec508d38bda3189d67fd5610f971166d4e..e0c5f692740a1edd0e4c0daecd32aaf3caebe953 100644 (file)
@@ -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);
 
index 4c1a3a840ed96721f5cd87d7496c3e1edec99f94..ff83cd64bf09c1dd84f4da82c4505ed43d360e6f 100644 (file)
@@ -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);
 
index 6c35caeb034d0fe5e817b2c51700a771dd68666d..0ef0ac655d3946a1ced143f01dbe9266f5627068 100644 (file)
@@ -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 (file)
index 0000000..83fee04
--- /dev/null
@@ -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<u32, u32> = HashMap::new();
+
+    let _ = map.keys().collect::<Vec<_>>();
+    let _ = map.values().collect::<Vec<_>>();
+    let _ = map.values().map(|v| v + 2).collect::<Vec<_>>();
+
+    let _ = map.clone().into_keys().collect::<Vec<_>>();
+    let _ = map.clone().into_keys().map(|key| key + 2).collect::<Vec<_>>();
+
+    let _ = map.clone().into_values().collect::<Vec<_>>();
+    let _ = map.clone().into_values().map(|val| val + 2).collect::<Vec<_>>();
+
+    let _ = map.clone().values().collect::<Vec<_>>();
+    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::<Vec<_>>();
+
+    // 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<u32, u32> = BTreeMap::new();
+
+    let _ = map.keys().collect::<Vec<_>>();
+    let _ = map.values().collect::<Vec<_>>();
+    let _ = map.values().map(|v| v + 2).collect::<Vec<_>>();
+
+    let _ = map.clone().into_keys().collect::<Vec<_>>();
+    let _ = map.clone().into_keys().map(|key| key + 2).collect::<Vec<_>>();
+
+    let _ = map.clone().into_values().collect::<Vec<_>>();
+    let _ = map.clone().into_values().map(|val| val + 2).collect::<Vec<_>>();
+
+    let _ = map.clone().values().collect::<Vec<_>>();
+    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::<Vec<_>>();
+
+    // 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 (file)
index 0000000..7a1f1fb
--- /dev/null
@@ -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<u32, u32> = HashMap::new();
+
+    let _ = map.iter().map(|(key, _)| key).collect::<Vec<_>>();
+    let _ = map.iter().map(|(_, value)| value).collect::<Vec<_>>();
+    let _ = map.iter().map(|(_, v)| v + 2).collect::<Vec<_>>();
+
+    let _ = map.clone().into_iter().map(|(key, _)| key).collect::<Vec<_>>();
+    let _ = map.clone().into_iter().map(|(key, _)| key + 2).collect::<Vec<_>>();
+
+    let _ = map.clone().into_iter().map(|(_, val)| val).collect::<Vec<_>>();
+    let _ = map.clone().into_iter().map(|(_, val)| val + 2).collect::<Vec<_>>();
+
+    let _ = map.clone().iter().map(|(_, val)| val).collect::<Vec<_>>();
+    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::<Vec<_>>();
+
+    // 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<u32, u32> = BTreeMap::new();
+
+    let _ = map.iter().map(|(key, _)| key).collect::<Vec<_>>();
+    let _ = map.iter().map(|(_, value)| value).collect::<Vec<_>>();
+    let _ = map.iter().map(|(_, v)| v + 2).collect::<Vec<_>>();
+
+    let _ = map.clone().into_iter().map(|(key, _)| key).collect::<Vec<_>>();
+    let _ = map.clone().into_iter().map(|(key, _)| key + 2).collect::<Vec<_>>();
+
+    let _ = map.clone().into_iter().map(|(_, val)| val).collect::<Vec<_>>();
+    let _ = map.clone().into_iter().map(|(_, val)| val + 2).collect::<Vec<_>>();
+
+    let _ = map.clone().iter().map(|(_, val)| val).collect::<Vec<_>>();
+    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::<Vec<_>>();
+
+    // 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 (file)
index 0000000..9b9b04c
--- /dev/null
@@ -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::<Vec<_>>();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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::<Vec<_>>();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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::<Vec<_>>();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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::<Vec<_>>();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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::<Vec<_>>();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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::<Vec<_>>();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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::<Vec<_>>();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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::<Vec<_>>();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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::<Vec<_>>();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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::<Vec<_>>();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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::<Vec<_>>();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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::<Vec<_>>();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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::<Vec<_>>();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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::<Vec<_>>();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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::<Vec<_>>();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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::<Vec<_>>();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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
+
index 717009e4c4ccadce5a5c5c93aa1607435c906edd..3b96f09d7b1d6efd49a8baef666e6b4c987515d2 100644 (file)
@@ -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 {
index c6ed97487c0e3a27aab80d8e6564b57bf00c8561..709972b4a6e49e397acdb442e34cbb6d139e4756 100644 (file)
@@ -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
index d9161bfcf15433f491bb5c58125f9b327b6736c5..6790765f803e06e57620f444379596b2d532aad9 100644 (file)
@@ -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],
index 58c0a77c1c84136c6205232aa03ef54be3498cf9..0d91b65b428407045b0141216b734316aefa8559 100644 (file)
@@ -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],
    |         ^^^^^^^^^^^^^^^
index 1e938e72b57775356d326a9f0bac9dcae6b57525..78397c2af346b0464fa0572caa11eb5fb0a406da 100644 (file)
@@ -274,7 +274,7 @@ async fn async_task(&self) -> bool {
     }
 
     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 {
index 24ae62bb05884104c59d25e7d20ed0260a35481d..e9b4367ca653cd5dcb95dd59108c76c26d37c87c 100644 (file)
@@ -57,3 +57,9 @@ fn check_expect() {
     #[expect(clippy::nonminimal_bool)]
     let _ = !!a;
 }
+
+fn issue9428() {
+    if matches!(true, true) && true {
+        println!("foo");
+    }
+}
index fc6a5ce1dc2eef4e6ffc2860aa19154b84c53614..91b5805aa97ab8f6b4c243c81bc840b1a4b3bb22 100644 (file)
@@ -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 (file)
index 4170e10..0000000
+++ /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 (file)
index 553d849..0000000
+++ /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 (file)
index 48ddb6d..0000000
+++ /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
-
index 8665a3bb28ae19b9eca30b426e4faf20ff47a6cc..3f6639c14585ad5aa69d133ba6bd5f70e02ab25d 100644 (file)
@@ -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
index 72aae0756033a5593826f90b900296a292a2d0df..23e6dbc3e341fdc0476872066594afe264d861f6 100644 (file)
@@ -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
 
index a43a1fc4f5241c33a5ebe6416196793ad2b057cf..b8c29d207adae044e8d9653660dc8f087d7dd193 100644 (file)
@@ -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!());
 }
index edbaa1cdf979fb0bc3f0f7a226cd555c70f10b97..b9f5675faec7389a6f8bfd9212dced7b7525d782 100644 (file)
@@ -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
 
index 17fe4ea74790c4de796cb0bbdf6342f7924fe65b..3cc8bb947bd352c0e889c2d29d4221f4754eee15 100644 (file)
@@ -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
 
index 9cbad2269a099c5b71756222744dabb78f9115a6..a6e7bdba77c65f1acac87f5323cf6e3993cb7070 100644 (file)
@@ -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)]
index 9153c0dab0290aa213307bdc47827e1f5af4ca1e..e8f57597d02b5d8d4a573166f145daa9a670182a 100644 (file)
@@ -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)]
index 9c03ea914bb65fef6c94b919523a36ab24d9e833..31865a7f66d60901231d84dc9f8c787d97f25736 100644 (file)
@@ -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
 
index a920c63b199c1e194697c33066be47f19c88e78c..f97583aa22f9a77247990c48aab8a68b9ae73715 100644 (file)
@@ -417,3 +417,12 @@ mod issue_9351 {
         predicates_are_satisfied(id("abc".to_string()));
     }
 }
+
+mod issue_9504 {
+    #![allow(dead_code)]
+
+    async fn foo<S: AsRef<str>>(_: S) {}
+    async fn bar() {
+        foo(std::path::PathBuf::new().to_string_lossy().to_string()).await;
+    }
+}
index 2128bdacddadf03fa7736e020583e5912b00a6e7..aa5394a565790c3f1478f12a56421745b4b18391 100644 (file)
@@ -417,3 +417,12 @@ pub fn main() {
         predicates_are_satisfied(id("abc".to_string()));
     }
 }
+
+mod issue_9504 {
+    #![allow(dead_code)]
+
+    async fn foo<S: AsRef<str>>(_: S) {}
+    async fn bar() {
+        foo(std::path::PathBuf::new().to_string_lossy().to_string()).await;
+    }
+}
index 153457e367165c6ce2fa6d3370a4c81a2645d265..7374dfdf92e87b5de8014c1cb27e01043cbcbe1b 100644 (file)
@@ -57,12 +57,22 @@ fn takes_peekable(_peek: Peekable<Empty<u32>>) {}
     impl PeekableConsumer {
         fn consume(&self, _: Peekable<Empty<u32>>) {}
         fn consume_mut_ref(&self, _: &mut Peekable<Empty<u32>>) {}
+        fn consume_assoc(_: Peekable<Empty<u32>>) {}
+        fn consume_assoc_mut_ref(_: &mut Peekable<Empty<u32>>) {}
     }
-
     let peekable_consumer = PeekableConsumer;
-    let mut passed_along_to_method = std::iter::empty::<u32>().peekable();
-    peekable_consumer.consume_mut_ref(&mut passed_along_to_method);
-    peekable_consumer.consume(passed_along_to_method);
+
+    let peekable = std::iter::empty::<u32>().peekable();
+    peekable_consumer.consume(peekable);
+
+    let mut peekable = std::iter::empty::<u32>().peekable();
+    peekable_consumer.consume_mut_ref(&mut peekable);
+
+    let peekable = std::iter::empty::<u32>().peekable();
+    PeekableConsumer::consume_assoc(peekable);
+
+    let mut peekable = std::iter::empty::<u32>().peekable();
+    PeekableConsumer::consume_assoc_mut_ref(&mut peekable);
 
     // `peek` called in another block
     let mut peekable_in_block = std::iter::empty::<u32>().peekable();
@@ -141,4 +151,19 @@ struct PeekableWrapper {
     {
         peekable_last_expr.peek();
     }
+
+    let mut peek_in_closure = std::iter::empty::<u32>().peekable();
+    let _ = || {
+        let _ = peek_in_closure.peek();
+    };
+
+    trait PeekTrait {}
+    impl<I> PeekTrait for Peekable<I> where I: Iterator {}
+
+    let mut peekable = std::iter::empty::<u32>().peekable();
+    let _dyn = &mut peekable as &mut dyn PeekTrait;
+
+    fn takes_dyn(_: &mut dyn PeekTrait) {}
+    let mut peekable = std::iter::empty::<u32>().peekable();
+    takes_dyn(&mut peekable);
 }
index 4f80aaecc902d1d49aa1d2c09ab19ce76787b26d..37986187da179b6acd40a6a006596a48343ae28a 100644 (file)
@@ -608,3 +608,12 @@ mod issue8845 {
         }
     }
 }
+
+mod issue6902 {
+    use serde::Serialize;
+
+    #[derive(Serialize)]
+    pub enum Foo {
+        Bar = 1,
+    }
+}
index 52da72db53ce3a9706847825d85b8a0fe062eb65..1b2b3337c92ed00fc30a08d00bb405c9dd4abe8d 100644 (file)
@@ -608,3 +608,12 @@ fn use_crate(&self) -> u8 {
         }
     }
 }
+
+mod issue6902 {
+    use serde::Serialize;
+
+    #[derive(Serialize)]
+    pub enum Foo {
+        Bar = 1,
+    }
+}
index 4466917441162f49b14ae65722cac9f1a7ae6378..5892818aa9a6173ac77d04968da4dc9c20f8de34 100644 (file)
@@ -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
index 3c5ec91d3e0fb8f3a6459b0cbfe2acd1d0c859a3..1e306ae28a263ca094c30bd57d29decd3fec839e 100644 (file)
@@ -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
 
index ba0d7be5eaa68dfe5234f0499db72cc8d759af99..55a11daa1d3443a65fd557661c6c9c311a51478d 100644 (file)
@@ -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
 }
index 9ff297069c402c3876e0678e4498919571d0f821..d5956db9ff0b4450d9c0ee01fb13383f0b879829 100644 (file)
@@ -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
 
index 446d6914d3461205a185daf7f71f6fabf399e3c7..b79364c8758c5809c32de86ced02ec9f17c79dc8 100644 (file)
@@ -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!());
 }
index 5f55431be0bd99a0abe28999aff27b3c668d1022..2baaea166d8ef189c094c195e58a19a63d1e75f3 100644 (file)
@@ -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
 
index ac65aadfc0e8926f4cf5d04dcde1f0906d5672e9..25e69ec48e7e28cb413a8f60a1b3b5969aaa3762 100644 (file)
@@ -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