]> git.lizzy.rs Git - rust.git/commitdiff
Auto merge of #7460 - camsteffen:run-from-source, r=Manishearth
authorbors <bors@rust-lang.org>
Thu, 15 Jul 2021 21:53:17 +0000 (21:53 +0000)
committerbors <bors@rust-lang.org>
Thu, 15 Jul 2021 21:53:17 +0000 (21:53 +0000)
Add instructions to run from source

changelog: none

We often get messages on Zulip asking how to install and run Clippy from source. This adds instructions to the readme. I also added a note explaining that `cargo install --path . --force` is bad, which I just decided after some investigating. I use macOS. It would be nice to get some tests on other platforms to see if this is correct.

51 files changed:
CHANGELOG.md
clippy_lints/src/copies.rs
clippy_lints/src/default_numeric_fallback.rs
clippy_lints/src/doc.rs
clippy_lints/src/eta_reduction.rs
clippy_lints/src/explicit_write.rs
clippy_lints/src/format.rs
clippy_lints/src/implicit_hasher.rs
clippy_lints/src/lib.rs
clippy_lints/src/methods/expect_fun_call.rs
clippy_lints/src/misc.rs
clippy_lints/src/mut_key.rs
clippy_lints/src/needless_bool.rs
clippy_lints/src/redundant_clone.rs
clippy_lints/src/types/mod.rs
clippy_lints/src/types/redundant_allocation.rs
clippy_lints/src/unit_types/unit_cmp.rs
clippy_lints/src/utils/internal_lints/metadata_collector.rs
clippy_utils/src/higher.rs
clippy_utils/src/lib.rs
clippy_utils/src/numeric_literal.rs
clippy_utils/src/paths.rs
clippy_utils/src/ty.rs
clippy_utils/src/usage.rs
rust-toolchain
tests/ui/assertions_on_constants.rs
tests/ui/branches_sharing_code/false_positives.rs [new file with mode: 0644]
tests/ui/default_numeric_fallback.rs [deleted file]
tests/ui/default_numeric_fallback.stderr [deleted file]
tests/ui/default_numeric_fallback_f64.fixed [new file with mode: 0644]
tests/ui/default_numeric_fallback_f64.rs [new file with mode: 0644]
tests/ui/default_numeric_fallback_f64.stderr [new file with mode: 0644]
tests/ui/default_numeric_fallback_i32.fixed [new file with mode: 0644]
tests/ui/default_numeric_fallback_i32.rs [new file with mode: 0644]
tests/ui/default_numeric_fallback_i32.stderr [new file with mode: 0644]
tests/ui/deprecated.stderr
tests/ui/eta.fixed
tests/ui/eta.rs
tests/ui/eta.stderr
tests/ui/explicit_write_non_rustfix.stderr
tests/ui/format.fixed
tests/ui/format.rs
tests/ui/redundant_allocation.fixed [deleted file]
tests/ui/redundant_allocation.rs
tests/ui/redundant_allocation.stderr
tests/ui/redundant_allocation_fixable.fixed [new file with mode: 0644]
tests/ui/redundant_allocation_fixable.rs [new file with mode: 0644]
tests/ui/redundant_allocation_fixable.stderr [new file with mode: 0644]
tests/ui/redundant_clone.fixed
tests/ui/redundant_clone.rs
tests/ui/redundant_clone.stderr

index ad3722d4d0bd86d84c8a39e0795f101f80079a8c..9f89f74e7450361f45def111ae1aeea75ef3e328 100644 (file)
@@ -592,7 +592,7 @@ Released 2021-02-11
 
 * Previously deprecated [`str_to_string`] and [`string_to_string`] have been un-deprecated
   as `restriction` lints [#6333](https://github.com/rust-lang/rust-clippy/pull/6333)
-* Deprecate `panic_params` lint. This is now available in rustc as `non_fmt_panic`
+* Deprecate `panic_params` lint. This is now available in rustc as `non_fmt_panics`
   [#6351](https://github.com/rust-lang/rust-clippy/pull/6351)
 * Move [`map_err_ignore`] to `restriction`
   [#6416](https://github.com/rust-lang/rust-clippy/pull/6416)
index aea1accccc6581838f8710390682d8cda33f3819..9cbcde597686e8c212c45ce95d4e28ad954624cf 100644 (file)
     ///
     /// **Why is this bad?** Duplicate code is less maintainable.
     ///
-    /// **Known problems:** Hopefully none.
+    /// **Known problems:**
+    /// * The lint doesn't check if the moved expressions modify values that are beeing used in
+    ///   the if condition. The suggestion can in that case modify the behavior of the program.
+    ///   See [rust-clippy#7452](https://github.com/rust-lang/rust-clippy/issues/7452)
     ///
     /// **Example:**
     /// ```ignore
@@ -358,8 +361,7 @@ fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> Option<
         expr_eq &= block_expr_eq;
     }
 
-    let has_expr = blocks[0].expr.is_some();
-    if has_expr && !expr_eq {
+    if !expr_eq {
         end_eq = 0;
     }
 
index a125376bffa9fa405089ba1cc483e3543496aee9..e719a1b0abf8744dd2f483d542c9c017e96e6b67 100644 (file)
@@ -1,5 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet;
+use clippy_utils::numeric_literal;
+use clippy_utils::source::snippet_opt;
 use if_chain::if_chain;
 use rustc_ast::ast::{LitFloatType, LitIntType, LitKind};
 use rustc_errors::Applicability;
@@ -78,16 +79,25 @@ fn check_lit(&self, lit: &Lit, lit_ty: Ty<'tcx>) {
                 if let Some(ty_bound) = self.ty_bounds.last();
                 if matches!(lit.node,
                             LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed));
-                if !ty_bound.is_integral();
+                if !ty_bound.is_numeric();
                 then {
-                    let suffix = match lit_ty.kind() {
-                        ty::Int(IntTy::I32) => "i32",
-                        ty::Float(FloatTy::F64) => "f64",
+                    let (suffix, is_float) = match lit_ty.kind() {
+                        ty::Int(IntTy::I32) => ("i32", false),
+                        ty::Float(FloatTy::F64) => ("f64", true),
                         // Default numeric fallback never results in other types.
                         _ => return,
                     };
 
-                    let sugg = format!("{}_{}", snippet(self.cx, lit.span, ""), suffix);
+                    let src = if let Some(src) = snippet_opt(self.cx, lit.span) {
+                        src
+                    } else {
+                        match lit.node {
+                            LitKind::Int(src, _) => format!("{}", src),
+                            LitKind::Float(src, _) => format!("{}", src),
+                            _ => return,
+                        }
+                    };
+                    let sugg = numeric_literal::format(&src, Some(suffix), is_float);
                     span_lint_and_sugg(
                         self.cx,
                         DEFAULT_NUMERIC_FALLBACK,
@@ -219,10 +229,10 @@ enum TyBound<'tcx> {
 }
 
 impl<'tcx> TyBound<'tcx> {
-    fn is_integral(self) -> bool {
+    fn is_numeric(self) -> bool {
         match self {
             TyBound::Any => true,
-            TyBound::Ty(t) => t.is_integral(),
+            TyBound::Ty(t) => t.is_numeric(),
             TyBound::Nothing => false,
         }
     }
index 17d868f1ec8243e8b746126c8401a06bfb1ab580..0c19988a975a85b156031f4616084317be3c6059 100644 (file)
@@ -26,6 +26,7 @@
 use rustc_span::{sym, FileName, Pos};
 use std::io;
 use std::ops::Range;
+use std::thread;
 use url::Url;
 
 declare_clippy_lint! {
@@ -584,17 +585,17 @@ fn get_current_span(spans: &[(usize, Span)], idx: usize) -> (usize, Span) {
 }
 
 fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) {
-    fn has_needless_main(code: &str, edition: Edition) -> bool {
+    fn has_needless_main(code: String, edition: Edition) -> bool {
         rustc_driver::catch_fatal_errors(|| {
-            rustc_span::with_session_globals(edition, || {
-                let filename = FileName::anon_source_code(code);
+            rustc_span::create_session_globals_then(edition, || {
+                let filename = FileName::anon_source_code(&code);
 
                 let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
                 let emitter = EmitterWriter::new(box io::sink(), None, false, false, false, None, false);
                 let handler = Handler::with_emitter(false, None, box emitter);
                 let sess = ParseSess::with_span_handler(handler, sm);
 
-                let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code.into()) {
+                let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code) {
                     Ok(p) => p,
                     Err(errs) => {
                         for mut err in errs {
@@ -649,7 +650,13 @@ fn has_needless_main(code: &str, edition: Edition) -> bool {
         .unwrap_or_default()
     }
 
-    if has_needless_main(text, edition) {
+    // Because of the global session, we need to create a new session in a different thread with
+    // the edition we need.
+    let text = text.to_owned();
+    if thread::spawn(move || has_needless_main(text, edition))
+        .join()
+        .expect("thread::spawn failed")
+    {
         span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest");
     }
 }
index 8d066f305ee85e44b8dab91d14b04516fe195f87..667eb8eb283bbb09997d47f8589bd86118e53e52 100644 (file)
@@ -1,15 +1,16 @@
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::higher;
 use clippy_utils::higher::VecArgs;
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::{implements_trait, type_is_unsafe_function};
+use clippy_utils::usage::UsedAfterExprVisitor;
+use clippy_utils::{get_enclosing_loop_or_closure, higher};
 use clippy_utils::{is_adjusted, iter_input_pats};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{def_id, Expr, ExprKind, Param, PatKind, QPath};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty::{self, Ty};
+use rustc_middle::ty::{self, ClosureKind, Ty};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 
 declare_clippy_lint! {
@@ -86,7 +87,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
     }
 }
 
-fn check_closure(cx: &LateContext<'_>, expr: &Expr<'_>) {
+fn check_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
     if let ExprKind::Closure(_, decl, eid, _, _) = expr.kind {
         let body = cx.tcx.hir().body(eid);
         let ex = &body.value;
@@ -131,7 +132,18 @@ fn check_closure(cx: &LateContext<'_>, expr: &Expr<'_>) {
 
             then {
                 span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| {
-                    if let Some(snippet) = snippet_opt(cx, caller.span) {
+                    if let Some(mut snippet) = snippet_opt(cx, caller.span) {
+                        if_chain! {
+                            if let ty::Closure(_, substs) = fn_ty.kind();
+                            if let ClosureKind::FnMut = substs.as_closure().kind();
+                            if UsedAfterExprVisitor::is_found(cx, caller)
+                                || get_enclosing_loop_or_closure(cx.tcx, expr).is_some();
+
+                            then {
+                                // Mutable closure is used after current expr; we cannot consume it.
+                                snippet = format!("&mut {}", snippet);
+                            }
+                        }
                         diag.span_suggestion(
                             expr.span,
                             "replace the closure with the function itself",
index da4936ff25b6d22d19e38f502e8d32c106cce991..66724294804a8414d1dfbbda3ac5050051fc4349 100644 (file)
@@ -1,9 +1,9 @@
-use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
+use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
+use clippy_utils::higher::FormatArgsExpn;
 use clippy_utils::{is_expn_of, match_function_call, paths};
 use if_chain::if_chain;
-use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
-use rustc_hir::{BorrowKind, Expr, ExprKind};
+use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::sym;
@@ -34,29 +34,26 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         if_chain! {
             // match call to unwrap
-            if let ExprKind::MethodCall(unwrap_fun, _, unwrap_args, _) = expr.kind;
+            if let ExprKind::MethodCall(unwrap_fun, _, [write_call], _) = expr.kind;
             if unwrap_fun.ident.name == sym::unwrap;
             // match call to write_fmt
-            if !unwrap_args.is_empty();
-            if let ExprKind::MethodCall(write_fun, _, write_args, _) =
-                unwrap_args[0].kind;
+            if let ExprKind::MethodCall(write_fun, _, [write_recv, write_arg], _) = write_call.kind;
             if write_fun.ident.name == sym!(write_fmt);
             // match calls to std::io::stdout() / std::io::stderr ()
-            if !write_args.is_empty();
-            if let Some(dest_name) = if match_function_call(cx, &write_args[0], &paths::STDOUT).is_some() {
+            if let Some(dest_name) = if match_function_call(cx, write_recv, &paths::STDOUT).is_some() {
                 Some("stdout")
-            } else if match_function_call(cx, &write_args[0], &paths::STDERR).is_some() {
+            } else if match_function_call(cx, write_recv, &paths::STDERR).is_some() {
                 Some("stderr")
             } else {
                 None
             };
+            if let Some(format_args) = FormatArgsExpn::parse(write_arg);
             then {
-                let write_span = unwrap_args[0].span;
                 let calling_macro =
                     // ordering is important here, since `writeln!` uses `write!` internally
-                    if is_expn_of(write_span, "writeln").is_some() {
+                    if is_expn_of(write_call.span, "writeln").is_some() {
                         Some("writeln")
-                    } else if is_expn_of(write_span, "write").is_some() {
+                    } else if is_expn_of(write_call.span, "write").is_some() {
                         Some("write")
                     } else {
                         None
@@ -70,82 +67,40 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                 // We need to remove the last trailing newline from the string because the
                 // underlying `fmt::write` function doesn't know whether `println!` or `print!` was
                 // used.
-                if let Some(mut write_output) = write_output_string(write_args) {
+                let (used, sugg_mac) = if let Some(macro_name) = calling_macro {
+                    (
+                        format!("{}!({}(), ...)", macro_name, dest_name),
+                        macro_name.replace("write", "print"),
+                    )
+                } else {
+                    (
+                        format!("{}().write_fmt(...)", dest_name),
+                        "print".into(),
+                    )
+                };
+                let msg = format!("use of `{}.unwrap()`", used);
+                if let [write_output] = *format_args.format_string_symbols {
+                    let mut write_output = write_output.to_string();
                     if write_output.ends_with('\n') {
                         write_output.pop();
                     }
 
-                    if let Some(macro_name) = calling_macro {
-                        span_lint_and_sugg(
-                            cx,
-                            EXPLICIT_WRITE,
-                            expr.span,
-                            &format!(
-                                "use of `{}!({}(), ...).unwrap()`",
-                                macro_name,
-                                dest_name
-                            ),
-                            "try this",
-                            format!("{}{}!(\"{}\")", prefix, macro_name.replace("write", "print"), write_output.escape_default()),
-                            Applicability::MachineApplicable
-                        );
-                    } else {
-                        span_lint_and_sugg(
-                            cx,
-                            EXPLICIT_WRITE,
-                            expr.span,
-                            &format!("use of `{}().write_fmt(...).unwrap()`", dest_name),
-                            "try this",
-                            format!("{}print!(\"{}\")", prefix, write_output.escape_default()),
-                            Applicability::MachineApplicable
-                        );
-                    }
+                    let sugg = format!("{}{}!(\"{}\")", prefix, sugg_mac, write_output.escape_default());
+                    span_lint_and_sugg(
+                        cx,
+                        EXPLICIT_WRITE,
+                        expr.span,
+                        &msg,
+                        "try this",
+                        sugg,
+                        Applicability::MachineApplicable
+                    );
                 } else {
                     // We don't have a proper suggestion
-                    if let Some(macro_name) = calling_macro {
-                        span_lint(
-                            cx,
-                            EXPLICIT_WRITE,
-                            expr.span,
-                            &format!(
-                                "use of `{}!({}(), ...).unwrap()`. Consider using `{}{}!` instead",
-                                macro_name,
-                                dest_name,
-                                prefix,
-                                macro_name.replace("write", "print")
-                            )
-                        );
-                    } else {
-                        span_lint(
-                            cx,
-                            EXPLICIT_WRITE,
-                            expr.span,
-                            &format!("use of `{}().write_fmt(...).unwrap()`. Consider using `{}print!` instead", dest_name, prefix),
-                        );
-                    }
+                    let help = format!("consider using `{}{}!` instead", prefix, sugg_mac);
+                    span_lint_and_help(cx, EXPLICIT_WRITE, expr.span, &msg, None, &help);
                 }
-
             }
         }
     }
 }
-
-// Extract the output string from the given `write_args`.
-fn write_output_string(write_args: &[Expr<'_>]) -> Option<String> {
-    if_chain! {
-        // Obtain the string that should be printed
-        if write_args.len() > 1;
-        if let ExprKind::Call(_, output_args) = write_args[1].kind;
-        if !output_args.is_empty();
-        if let ExprKind::AddrOf(BorrowKind::Ref, _, output_string_expr) = output_args[0].kind;
-        if let ExprKind::Array(string_exprs) = output_string_expr.kind;
-        // we only want to provide an automatic suggestion for simple (non-format) strings
-        if string_exprs.len() == 1;
-        if let ExprKind::Lit(ref lit) = string_exprs[0].kind;
-        if let LitKind::Str(ref write_output, _) = lit.node;
-        then {
-            return Some(write_output.to_string())
-        }
-    }
-    None
-}
index c2b055ed6488e4e9bdeb0d34e1980a16c8e09fdc..ca3490d8edad98c95df69f46abab20acf2335791 100644 (file)
@@ -1,17 +1,16 @@
-use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::paths;
-use clippy_utils::source::{snippet, snippet_opt};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::higher::FormatExpn;
+use clippy_utils::last_path_segment;
+use clippy_utils::source::{snippet_opt, snippet_with_applicability};
 use clippy_utils::sugg::Sugg;
-use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{is_expn_of, last_path_segment, match_def_path, match_function_call};
 use if_chain::if_chain;
-use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
-use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, MatchSource, PatKind};
-use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_hir::{BorrowKind, Expr, ExprKind, QPath};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::Span;
-use rustc_span::sym;
+use rustc_span::symbol::kw;
+use rustc_span::{sym, Span};
 
 declare_clippy_lint! {
     /// **What it does:** Checks for the use of `format!("string literal with no
 
 impl<'tcx> LateLintPass<'tcx> for UselessFormat {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        let span = match is_expn_of(expr.span, "format") {
-            Some(s) if !s.from_expansion() => s,
+        let FormatExpn { call_site, format_args } = match FormatExpn::parse(expr) {
+            Some(e) if !e.call_site.from_expansion() => e,
             _ => return,
         };
 
-        // Operate on the only argument of `alloc::fmt::format`.
-        if let Some(sugg) = on_new_v1(cx, expr) {
-            span_useless_format(cx, span, "consider using `.to_string()`", sugg);
-        } else if let Some(sugg) = on_new_v1_fmt(cx, expr) {
-            span_useless_format(cx, span, "consider using `.to_string()`", sugg);
-        }
-    }
-}
-
-fn span_useless_format<T: LintContext>(cx: &T, span: Span, help: &str, mut sugg: String) {
-    let to_replace = span.source_callsite();
-
-    // The callsite span contains the statement semicolon for some reason.
-    let snippet = snippet(cx, to_replace, "..");
-    if snippet.ends_with(';') {
-        sugg.push(';');
-    }
-
-    span_lint_and_then(cx, USELESS_FORMAT, span, "useless use of `format!`", |diag| {
-        diag.span_suggestion(
-            to_replace,
-            help,
-            sugg,
-            Applicability::MachineApplicable, // snippet
-        );
-    });
-}
-
-fn on_argumentv1_new<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>]) -> Option<String> {
-    if_chain! {
-        if let ExprKind::AddrOf(BorrowKind::Ref, _, format_args) = expr.kind;
-        if let ExprKind::Array(elems) = arms[0].body.kind;
-        if elems.len() == 1;
-        if let Some(args) = match_function_call(cx, &elems[0], &paths::FMT_ARGUMENTV1_NEW);
-        // matches `core::fmt::Display::fmt`
-        if args.len() == 2;
-        if let ExprKind::Path(ref qpath) = args[1].kind;
-        if let Some(did) = cx.qpath_res(qpath, args[1].hir_id).opt_def_id();
-        if match_def_path(cx, did, &paths::DISPLAY_FMT_METHOD);
-        // check `(arg0,)` in match block
-        if let PatKind::Tuple(pats, None) = arms[0].pat.kind;
-        if pats.len() == 1;
-        then {
-            let ty = cx.typeck_results().pat_ty(pats[0]).peel_refs();
-            if *ty.kind() != rustc_middle::ty::Str && !is_type_diagnostic_item(cx, ty, sym::string_type) {
-                return None;
-            }
-            if let ExprKind::Lit(ref lit) = format_args.kind {
-                if let LitKind::Str(ref s, _) = lit.node {
-                    return Some(format!("{:?}.to_string()", s.as_str()));
+        let mut applicability = Applicability::MachineApplicable;
+        if format_args.value_args.is_empty() {
+            if_chain! {
+                if let [e] = &*format_args.format_string_parts;
+                if let ExprKind::Lit(lit) = &e.kind;
+                if let Some(s_src) = snippet_opt(cx, lit.span);
+                then {
+                    // Simulate macro expansion, converting {{ and }} to { and }.
+                    let s_expand = s_src.replace("{{", "{").replace("}}", "}");
+                    let sugg = format!("{}.to_string()", s_expand);
+                    span_useless_format(cx, call_site, sugg, applicability);
                 }
-            } else {
-                let sugg = Sugg::hir(cx, format_args, "<arg>");
-                if let ExprKind::MethodCall(path, _, _, _) = format_args.kind {
-                    if path.ident.name == sym!(to_string) {
-                        return Some(format!("{}", sugg));
-                    }
-                } else if let ExprKind::Binary(..) = format_args.kind {
-                    return Some(format!("{}", sugg));
+            }
+        } else if let [value] = *format_args.value_args {
+            if_chain! {
+                if format_args.format_string_symbols == [kw::Empty];
+                if match cx.typeck_results().expr_ty(value).peel_refs().kind() {
+                    ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(sym::string_type, adt.did),
+                    ty::Str => true,
+                    _ => false,
+                };
+                if format_args.args.iter().all(|e| is_display_arg(e));
+                if format_args.fmt_expr.map_or(true, |e| check_unformatted(e));
+                then {
+                    let is_new_string = match value.kind {
+                        ExprKind::Binary(..) => true,
+                        ExprKind::MethodCall(path, ..) => path.ident.name.as_str() == "to_string",
+                        _ => false,
+                    };
+                    let sugg = if is_new_string {
+                        snippet_with_applicability(cx, value.span, "..", &mut applicability).into_owned()
+                    } else {
+                        let sugg = Sugg::hir_with_applicability(cx, value, "<arg>", &mut applicability);
+                        format!("{}.to_string()", sugg.maybe_par())
+                    };
+                    span_useless_format(cx, call_site, sugg, applicability);
                 }
-                return Some(format!("{}.to_string()", sugg.maybe_par()));
             }
-        }
+        };
     }
-    None
 }
 
-fn on_new_v1<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<String> {
-    if_chain! {
-        if let Some(args) = match_function_call(cx, expr, &paths::FMT_ARGUMENTS_NEW_V1);
-        if args.len() == 2;
-        // Argument 1 in `new_v1()`
-        if let ExprKind::AddrOf(BorrowKind::Ref, _, arr) = args[0].kind;
-        if let ExprKind::Array(pieces) = arr.kind;
-        if pieces.len() == 1;
-        if let ExprKind::Lit(ref lit) = pieces[0].kind;
-        if let LitKind::Str(ref s, _) = lit.node;
-        // Argument 2 in `new_v1()`
-        if let ExprKind::AddrOf(BorrowKind::Ref, _, arg1) = args[1].kind;
-        if let ExprKind::Match(matchee, arms, MatchSource::Normal) = arg1.kind;
-        if arms.len() == 1;
-        if let ExprKind::Tup(tup) = matchee.kind;
-        then {
-            // `format!("foo")` expansion contains `match () { () => [], }`
-            if tup.is_empty() {
-                if let Some(s_src) = snippet_opt(cx, lit.span) {
-                    // Simulate macro expansion, converting {{ and }} to { and }.
-                    let s_expand = s_src.replace("{{", "{").replace("}}", "}");
-                    return Some(format!("{}.to_string()", s_expand));
-                }
-            } else if s.as_str().is_empty() {
-                return on_argumentv1_new(cx, &tup[0], arms);
-            }
-        }
+fn span_useless_format(cx: &LateContext<'_>, span: Span, mut sugg: String, mut applicability: Applicability) {
+    // The callsite span contains the statement semicolon for some reason.
+    if snippet_with_applicability(cx, span, "..", &mut applicability).ends_with(';') {
+        sugg.push(';');
     }
-    None
+
+    span_lint_and_sugg(
+        cx,
+        USELESS_FORMAT,
+        span,
+        "useless use of `format!`",
+        "consider using `.to_string()`",
+        sugg,
+        applicability,
+    );
 }
 
-fn on_new_v1_fmt<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<String> {
+fn is_display_arg(expr: &Expr<'_>) -> bool {
     if_chain! {
-        if let Some(args) = match_function_call(cx, expr, &paths::FMT_ARGUMENTS_NEW_V1_FORMATTED);
-        if args.len() == 3;
-        if check_unformatted(&args[2]);
-        // Argument 1 in `new_v1_formatted()`
-        if let ExprKind::AddrOf(BorrowKind::Ref, _, arr) = args[0].kind;
-        if let ExprKind::Array(pieces) = arr.kind;
-        if pieces.len() == 1;
-        if let ExprKind::Lit(ref lit) = pieces[0].kind;
-        if let LitKind::Str(..) = lit.node;
-        // Argument 2 in `new_v1_formatted()`
-        if let ExprKind::AddrOf(BorrowKind::Ref, _, arg1) = args[1].kind;
-        if let ExprKind::Match(matchee, arms, MatchSource::Normal) = arg1.kind;
-        if arms.len() == 1;
-        if let ExprKind::Tup(tup) = matchee.kind;
-        then {
-            return on_argumentv1_new(cx, &tup[0], arms);
-        }
+        if let ExprKind::Call(_, [_, fmt]) = expr.kind;
+        if let ExprKind::Path(QPath::Resolved(_, path)) = fmt.kind;
+        if let [.., t, _] = path.segments;
+        if t.ident.name.as_str() == "Display";
+        then { true } else { false }
     }
-    None
 }
 
 /// Checks if the expression matches
@@ -184,10 +131,9 @@ fn on_new_v1_fmt<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<S
 fn check_unformatted(expr: &Expr<'_>) -> bool {
     if_chain! {
         if let ExprKind::AddrOf(BorrowKind::Ref, _, expr) = expr.kind;
-        if let ExprKind::Array(exprs) = expr.kind;
-        if exprs.len() == 1;
+        if let ExprKind::Array([expr]) = expr.kind;
         // struct `core::fmt::rt::v1::Argument`
-        if let ExprKind::Struct(_, fields, _) = exprs[0].kind;
+        if let ExprKind::Struct(_, fields, _) = expr.kind;
         if let Some(format_field) = fields.iter().find(|f| f.ident.name == sym::format);
         // struct `core::fmt::rt::v1::FormatSpec`
         if let ExprKind::Struct(_, fields, _) = format_field.expr.kind;
index 03fe0d16d480f792f8138a3480812604ab183cb4..4f8f3aa96cb1b124add8817ab4358da09f6c9245 100644 (file)
@@ -1,5 +1,3 @@
-#![allow(rustc::default_hash_types)]
-
 use std::borrow::Cow;
 use std::collections::BTreeMap;
 
index 82519a9b5b73b1a16a8147d8412cbfb7a3cc8fdb..1af3a215f4468b4211bd7aa28a1e74ce8648604f 100644 (file)
@@ -2177,7 +2177,7 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) {
     ls.register_renamed("clippy::unused_label", "unused_labels");
     ls.register_renamed("clippy::drop_bounds", "drop_bounds");
     ls.register_renamed("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr");
-    ls.register_renamed("clippy::panic_params", "non_fmt_panic");
+    ls.register_renamed("clippy::panic_params", "non_fmt_panics");
     ls.register_renamed("clippy::unknown_clippy_lints", "unknown_lints");
 }
 
index 03cb41697d509dd46717ed6d1a1c43094749f605..f8ee31a00df821ebc0e6f7834d666b3adb3a40d5 100644 (file)
@@ -1,8 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::is_expn_of;
-use clippy_utils::source::{snippet, snippet_with_applicability};
+use clippy_utils::higher::FormatExpn;
+use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::ty::is_type_diagnostic_item;
-use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
@@ -94,27 +93,6 @@ fn can_be_static_str(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool {
         }
     }
 
-    fn generate_format_arg_snippet(
-        cx: &LateContext<'_>,
-        a: &hir::Expr<'_>,
-        applicability: &mut Applicability,
-    ) -> Vec<String> {
-        if_chain! {
-            if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, format_arg) = a.kind;
-            if let hir::ExprKind::Match(format_arg_expr, _, _) = format_arg.kind;
-            if let hir::ExprKind::Tup(format_arg_expr_tup) = format_arg_expr.kind;
-
-            then {
-                format_arg_expr_tup
-                    .iter()
-                    .map(|a| snippet_with_applicability(cx, a.span, "..", applicability).into_owned())
-                    .collect()
-            } else {
-                unreachable!()
-            }
-        }
-    }
-
     fn is_call(node: &hir::ExprKind<'_>) -> bool {
         match node {
             hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, expr) => {
@@ -150,36 +128,22 @@ fn is_call(node: &hir::ExprKind<'_>) -> bool {
     let mut applicability = Applicability::MachineApplicable;
 
     //Special handling for `format!` as arg_root
-    if_chain! {
-        if let hir::ExprKind::Block(block, None) = &arg_root.kind;
-        if block.stmts.len() == 1;
-        if let hir::StmtKind::Local(local) = &block.stmts[0].kind;
-        if let Some(arg_root) = &local.init;
-        if let hir::ExprKind::Call(inner_fun, inner_args) = arg_root.kind;
-        if is_expn_of(inner_fun.span, "format").is_some() && inner_args.len() == 1;
-        if let hir::ExprKind::Call(_, format_args) = &inner_args[0].kind;
-        then {
-            let fmt_spec = &format_args[0];
-            let fmt_args = &format_args[1];
-
-            let mut args = vec![snippet(cx, fmt_spec.span, "..").into_owned()];
-
-            args.extend(generate_format_arg_snippet(cx, fmt_args, &mut applicability));
-
-            let sugg = args.join(", ");
-
-            span_lint_and_sugg(
-                cx,
-                EXPECT_FUN_CALL,
-                span_replace_word,
-                &format!("use of `{}` followed by a function call", name),
-                "try this",
-                format!("unwrap_or_else({} panic!({}))", closure_args, sugg),
-                applicability,
-            );
-
-            return;
-        }
+    if let Some(format_expn) = FormatExpn::parse(arg_root) {
+        let span = match *format_expn.format_args.value_args {
+            [] => format_expn.format_args.format_string_span,
+            [.., last] => format_expn.format_args.format_string_span.to(last.span),
+        };
+        let sugg = snippet_with_applicability(cx, span, "..", &mut applicability);
+        span_lint_and_sugg(
+            cx,
+            EXPECT_FUN_CALL,
+            span_replace_word,
+            &format!("use of `{}` followed by a function call", name),
+            "try this",
+            format!("unwrap_or_else({} panic!({}))", closure_args, sugg),
+            applicability,
+        );
+        return;
     }
 
     let mut arg_root_snippet: Cow<'_, _> = snippet_with_applicability(cx, arg_root.span, "..", &mut applicability);
index 804c04fe1b838894926bdfe1d1b54713b70667b4..7cfce2e61cca59e153cf08caf4c40fb08880cd99 100644 (file)
@@ -662,14 +662,7 @@ fn in_attributes_expansion(expr: &Expr<'_>) -> bool {
     use rustc_span::hygiene::MacroKind;
     if expr.span.from_expansion() {
         let data = expr.span.ctxt().outer_expn_data();
-        matches!(
-            data.kind,
-            ExpnKind::Macro {
-                kind: MacroKind::Attr,
-                name: _,
-                proc_macro: _
-            }
-        )
+        matches!(data.kind, ExpnKind::Macro(MacroKind::Attr, _))
     } else {
         false
     }
index 6c87136e5e14e0a235e697e4bd071d7f1cefcff3..4dbbb14c504fa445f334b4b008ac248ed911a17b 100644 (file)
@@ -120,8 +120,8 @@ fn is_mutable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span) -> bo
         },
         Tuple(..) => ty.tuple_fields().any(|ty| is_mutable_type(cx, ty, span)),
         Adt(..) => {
-            cx.tcx.layout_of(cx.param_env.and(ty)).is_ok()
-                && !ty.has_escaping_bound_vars()
+            !ty.has_escaping_bound_vars()
+                && cx.tcx.layout_of(cx.param_env.and(ty)).is_ok()
                 && !ty.is_freeze(cx.tcx.at(span), cx.param_env)
         },
         _ => false,
index 3b3736fd3a19114601421a34cd60e8cd0993f5b9..780690548e52b7008d3ccd590a4592050e054161 100644 (file)
@@ -71,6 +71,9 @@
 impl<'tcx> LateLintPass<'tcx> for NeedlessBool {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
         use self::Expression::{Bool, RetBool};
+        if e.span.from_expansion() {
+            return;
+        }
         if let ExprKind::If(pred, then_block, Some(else_expr)) = e.kind {
             let reduce = |ret, not| {
                 let mut applicability = Applicability::MachineApplicable;
index 380557c81a19a9d1162e5fb0b3ad03784846664a..56ef95a88c88081da4b83fbc44da66b8759df5cf 100644 (file)
@@ -12,6 +12,7 @@
 use rustc_middle::mir::{
     self, traversal,
     visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor as _},
+    Mutability,
 };
 use rustc_middle::ty::{self, fold::TypeVisitor, Ty};
 use rustc_mir::dataflow::{Analysis, AnalysisDomain, GenKill, GenKillAnalysis, ResultsCursor};
@@ -87,13 +88,18 @@ fn check_fn(
 
         let mir = cx.tcx.optimized_mir(def_id.to_def_id());
 
+        let possible_origin = {
+            let mut vis = PossibleOriginVisitor::new(mir);
+            vis.visit_body(mir);
+            vis.into_map(cx)
+        };
         let maybe_storage_live_result = MaybeStorageLive
             .into_engine(cx.tcx, mir)
             .pass_name("redundant_clone")
             .iterate_to_fixpoint()
             .into_results_cursor(mir);
         let mut possible_borrower = {
-            let mut vis = PossibleBorrowerVisitor::new(cx, mir);
+            let mut vis = PossibleBorrowerVisitor::new(cx, mir, possible_origin);
             vis.visit_body(mir);
             vis.into_map(cx, maybe_storage_live_result)
         };
@@ -509,14 +515,20 @@ struct PossibleBorrowerVisitor<'a, 'tcx> {
     possible_borrower: TransitiveRelation<mir::Local>,
     body: &'a mir::Body<'tcx>,
     cx: &'a LateContext<'tcx>,
+    possible_origin: FxHashMap<mir::Local, HybridBitSet<mir::Local>>,
 }
 
 impl<'a, 'tcx> PossibleBorrowerVisitor<'a, 'tcx> {
-    fn new(cx: &'a LateContext<'tcx>, body: &'a mir::Body<'tcx>) -> Self {
+    fn new(
+        cx: &'a LateContext<'tcx>,
+        body: &'a mir::Body<'tcx>,
+        possible_origin: FxHashMap<mir::Local, HybridBitSet<mir::Local>>,
+    ) -> Self {
         Self {
             possible_borrower: TransitiveRelation::default(),
             cx,
             body,
+            possible_origin,
         }
     }
 
@@ -585,21 +597,105 @@ fn visit_terminator(&mut self, terminator: &mir::Terminator<'_>, _loc: mir::Loca
             ..
         } = &terminator.kind
         {
+            // TODO add doc
             // If the call returns something with lifetimes,
             // let's conservatively assume the returned value contains lifetime of all the arguments.
             // For example, given `let y: Foo<'a> = foo(x)`, `y` is considered to be a possible borrower of `x`.
-            if ContainsRegion.visit_ty(self.body.local_decls[*dest].ty).is_continue() {
-                return;
-            }
+
+            let mut immutable_borrowers = vec![];
+            let mut mutable_borrowers = vec![];
 
             for op in args {
                 match op {
                     mir::Operand::Copy(p) | mir::Operand::Move(p) => {
-                        self.possible_borrower.add(p.local, *dest);
+                        if let ty::Ref(_, _, Mutability::Mut) = self.body.local_decls[p.local].ty.kind() {
+                            mutable_borrowers.push(p.local);
+                        } else {
+                            immutable_borrowers.push(p.local);
+                        }
                     },
                     mir::Operand::Constant(..) => (),
                 }
             }
+
+            let mut mutable_variables: Vec<mir::Local> = mutable_borrowers
+                .iter()
+                .filter_map(|r| self.possible_origin.get(r))
+                .flat_map(HybridBitSet::iter)
+                .collect();
+
+            if ContainsRegion.visit_ty(self.body.local_decls[*dest].ty).is_break() {
+                mutable_variables.push(*dest);
+            }
+
+            for y in mutable_variables {
+                for x in &immutable_borrowers {
+                    self.possible_borrower.add(*x, y);
+                }
+                for x in &mutable_borrowers {
+                    self.possible_borrower.add(*x, y);
+                }
+            }
+        }
+    }
+}
+
+/// Collect possible borrowed for every `&mut` local.
+/// For exampel, `_1 = &mut _2` generate _1: {_2,...}
+/// Known Problems: not sure all borrowed are tracked
+struct PossibleOriginVisitor<'a, 'tcx> {
+    possible_origin: TransitiveRelation<mir::Local>,
+    body: &'a mir::Body<'tcx>,
+}
+
+impl<'a, 'tcx> PossibleOriginVisitor<'a, 'tcx> {
+    fn new(body: &'a mir::Body<'tcx>) -> Self {
+        Self {
+            possible_origin: TransitiveRelation::default(),
+            body,
+        }
+    }
+
+    fn into_map(self, cx: &LateContext<'tcx>) -> FxHashMap<mir::Local, HybridBitSet<mir::Local>> {
+        let mut map = FxHashMap::default();
+        for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) {
+            if is_copy(cx, self.body.local_decls[row].ty) {
+                continue;
+            }
+
+            let borrowers = self.possible_origin.reachable_from(&row);
+            if !borrowers.is_empty() {
+                let mut bs = HybridBitSet::new_empty(self.body.local_decls.len());
+                for &c in borrowers {
+                    if c != mir::Local::from_usize(0) {
+                        bs.insert(c);
+                    }
+                }
+
+                if !bs.is_empty() {
+                    map.insert(row, bs);
+                }
+            }
+        }
+        map
+    }
+}
+
+impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleOriginVisitor<'a, 'tcx> {
+    fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) {
+        let lhs = place.local;
+        match rvalue {
+            // Only consider `&mut`, which can modify origin place
+            mir::Rvalue::Ref(_, rustc_middle::mir::BorrowKind::Mut { .. }, borrowed) |
+            // _2: &mut _;
+            // _3 = move _2
+            mir::Rvalue::Use(mir::Operand::Move(borrowed))  |
+            // _3 = move _2 as &mut _;
+            mir::Rvalue::Cast(_, mir::Operand::Move(borrowed), _)
+                => {
+                self.possible_origin.add(lhs, borrowed.local);
+            },
+            _ => {},
         }
     }
 }
index c26268a875cc06123b05968d2d3ca9e5701647f1..7d629b5455b62913f4b480ce67fed09e65c1c4f1 100644 (file)
 declare_clippy_lint! {
     /// **What it does:** Checks for use of redundant allocations anywhere in the code.
     ///
-    /// **Why is this bad?** Expressions such as `Rc<&T>`, `Rc<Rc<T>>`, `Rc<Box<T>>`, `Box<&T>`
-    /// add an unnecessary level of indirection.
+    /// **Why is this bad?** Expressions such as `Rc<&T>`, `Rc<Rc<T>>`, `Rc<Arc<T>>`, `Rc<Box<T>>`, Arc<&T>`, `Arc<Rc<T>>`,
+    /// `Arc<Arc<T>>`, `Arc<Box<T>>`, `Box<&T>`, `Box<Rc<T>>`, `Box<Arc<T>>`, `Box<Box<T>>`, add an unnecessary level of indirection.
     ///
     /// **Known problems:** None.
     ///
index c0c1f340583c1b53cb126407fe8ccb96c53aa9da..8e83dcbf704e0930fecd8384910e51acbc10fa9e 100644 (file)
@@ -1,5 +1,5 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::{snippet, snippet_with_applicability};
 use clippy_utils::{get_qpath_generic_tys, is_ty_param_diagnostic_item, is_ty_param_lang_item};
 use rustc_errors::Applicability;
 use rustc_hir::{self as hir, def_id::DefId, LangItem, QPath, TyKind};
@@ -9,74 +9,99 @@
 use super::{utils, REDUNDANT_ALLOCATION};
 
 pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
-    if Some(def_id) == cx.tcx.lang_items().owned_box() {
-        if let Some(span) = utils::match_borrows_parameter(cx, qpath) {
-            let mut applicability = Applicability::MachineApplicable;
-            span_lint_and_sugg(
-                cx,
-                REDUNDANT_ALLOCATION,
-                hir_ty.span,
-                "usage of `Box<&T>`",
-                "try",
-                snippet_with_applicability(cx, span, "..", &mut applicability).to_string(),
-                applicability,
-            );
-            return true;
-        }
+    let outer_sym = if Some(def_id) == cx.tcx.lang_items().owned_box() {
+        "Box"
+    } else if cx.tcx.is_diagnostic_item(sym::Rc, def_id) {
+        "Rc"
+    } else if cx.tcx.is_diagnostic_item(sym::Arc, def_id) {
+        "Arc"
+    } else {
+        return false;
+    };
+
+    if let Some(span) = utils::match_borrows_parameter(cx, qpath) {
+        let mut applicability = Applicability::MaybeIncorrect;
+        let generic_snippet = snippet_with_applicability(cx, span, "..", &mut applicability);
+        span_lint_and_then(
+            cx,
+            REDUNDANT_ALLOCATION,
+            hir_ty.span,
+            &format!("usage of `{}<{}>`", outer_sym, generic_snippet),
+            |diag| {
+                diag.span_suggestion(hir_ty.span, "try", format!("{}", generic_snippet), applicability);
+                diag.note(&format!(
+                    "`{generic}` is already a pointer, `{outer}<{generic}>` allocates a pointer on the heap",
+                    outer = outer_sym,
+                    generic = generic_snippet
+                ));
+            },
+        );
+        return true;
     }
 
-    if cx.tcx.is_diagnostic_item(sym::Rc, def_id) {
-        if let Some(ty) = is_ty_param_diagnostic_item(cx, qpath, sym::Rc) {
-            let mut applicability = Applicability::MachineApplicable;
-            span_lint_and_sugg(
-                cx,
-                REDUNDANT_ALLOCATION,
-                hir_ty.span,
-                "usage of `Rc<Rc<T>>`",
-                "try",
-                snippet_with_applicability(cx, ty.span, "..", &mut applicability).to_string(),
-                applicability,
-            );
-            true
-        } else if let Some(ty) = is_ty_param_lang_item(cx, qpath, LangItem::OwnedBox) {
-            let qpath = match &ty.kind {
-                TyKind::Path(qpath) => qpath,
-                _ => return false,
-            };
-            let inner_span = match get_qpath_generic_tys(qpath).next() {
-                Some(ty) => ty.span,
-                None => return false,
-            };
-            let mut applicability = Applicability::MachineApplicable;
-            span_lint_and_sugg(
-                cx,
-                REDUNDANT_ALLOCATION,
-                hir_ty.span,
-                "usage of `Rc<Box<T>>`",
-                "try",
-                format!(
-                    "Rc<{}>",
-                    snippet_with_applicability(cx, inner_span, "..", &mut applicability)
-                ),
-                applicability,
-            );
-            true
-        } else {
-            utils::match_borrows_parameter(cx, qpath).map_or(false, |span| {
-                let mut applicability = Applicability::MachineApplicable;
-                span_lint_and_sugg(
-                    cx,
-                    REDUNDANT_ALLOCATION,
+    let (inner_sym, ty) = if let Some(ty) = is_ty_param_lang_item(cx, qpath, LangItem::OwnedBox) {
+        ("Box", ty)
+    } else if let Some(ty) = is_ty_param_diagnostic_item(cx, qpath, sym::Rc) {
+        ("Rc", ty)
+    } else if let Some(ty) = is_ty_param_diagnostic_item(cx, qpath, sym::Arc) {
+        ("Arc", ty)
+    } else {
+        return false;
+    };
+
+    let inner_qpath = match &ty.kind {
+        TyKind::Path(inner_qpath) => inner_qpath,
+        _ => return false,
+    };
+    let inner_span = match get_qpath_generic_tys(inner_qpath).next() {
+        Some(ty) => ty.span,
+        None => return false,
+    };
+    if inner_sym == outer_sym {
+        let mut applicability = Applicability::MaybeIncorrect;
+        let generic_snippet = snippet_with_applicability(cx, inner_span, "..", &mut applicability);
+        span_lint_and_then(
+            cx,
+            REDUNDANT_ALLOCATION,
+            hir_ty.span,
+            &format!("usage of `{}<{}<{}>>`", outer_sym, inner_sym, generic_snippet),
+            |diag| {
+                diag.span_suggestion(
                     hir_ty.span,
-                    "usage of `Rc<&T>`",
                     "try",
-                    snippet_with_applicability(cx, span, "..", &mut applicability).to_string(),
+                    format!("{}<{}>", outer_sym, generic_snippet),
                     applicability,
                 );
-                true
-            })
-        }
+                diag.note(&format!(
+                    "`{inner}<{generic}>` is already on the heap, `{outer}<{inner}<{generic}>>` makes an extra allocation",
+                    outer = outer_sym,
+                    inner = inner_sym,
+                    generic = generic_snippet
+                ));
+            },
+        );
     } else {
-        false
+        let generic_snippet = snippet(cx, inner_span, "..");
+        span_lint_and_then(
+            cx,
+            REDUNDANT_ALLOCATION,
+            hir_ty.span,
+            &format!("usage of `{}<{}<{}>>`", outer_sym, inner_sym, generic_snippet),
+            |diag| {
+                diag.note(&format!(
+                    "`{inner}<{generic}>` is already on the heap, `{outer}<{inner}<{generic}>>` makes an extra allocation",
+                    outer = outer_sym,
+                    inner = inner_sym,
+                    generic = generic_snippet
+                ));
+                diag.help(&format!(
+                    "consider using just `{outer}<{generic}>` or `{inner}<{generic}>`",
+                    outer = outer_sym,
+                    inner = inner_sym,
+                    generic = generic_snippet
+                ));
+            },
+        );
     }
+    true
 }
index 0454214651683afbfae4ede5b4afdcbeedf0f0aa..85257f3113cb774adc43f326707c0182b1eac616 100644 (file)
@@ -8,12 +8,7 @@
 pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
     if expr.span.from_expansion() {
         if let Some(callee) = expr.span.source_callee() {
-            if let ExpnKind::Macro {
-                kind: MacroKind::Bang,
-                name: symbol,
-                proc_macro: _,
-            } = callee.kind
-            {
+            if let ExpnKind::Macro(MacroKind::Bang, symbol) = callee.kind {
                 if let ExprKind::Binary(ref cmp, left, _) = expr.kind {
                     let op = cmp.node;
                     if op.is_comparison() && cx.typeck_results().expr_ty(left).is_unit() {
index e877af09e28900d94f5491a828b93bffabed4388..3eccc89cdeb808f813a32177d5af76f6819c0fed 100644 (file)
@@ -520,7 +520,9 @@ fn get_lint_group_and_level_or_lint(
     lint_name: &str,
     item: &'hir Item<'_>,
 ) -> Option<(String, &'static str)> {
-    let result = cx.lint_store.check_lint_name(lint_name, Some(sym::clippy));
+    let result = cx
+        .lint_store
+        .check_lint_name(cx.sess(), lint_name, Some(sym::clippy), &[]);
     if let CheckLintNameResult::Tool(Ok(lint_lst)) = result {
         if let Some(group) = get_lint_group(cx, lint_lst[0]) {
             if EXCLUDED_LINT_GROUPS.contains(&group.as_str()) {
index 8be36756b3332a295963c89aa3dace545fd5570d..3e3e472e99fb69215e7e07b2c70d34c4842674dd 100644 (file)
@@ -5,11 +5,11 @@
 
 use crate::{is_expn_of, match_def_path, paths};
 use if_chain::if_chain;
-use rustc_ast::ast;
+use rustc_ast::ast::{self, LitKind};
 use rustc_hir as hir;
 use rustc_hir::{BorrowKind, Expr, ExprKind, StmtKind, UnOp};
 use rustc_lint::LateContext;
-use rustc_span::source_map::Span;
+use rustc_span::{sym, ExpnKind, Span, Symbol};
 
 /// Converts a hir binary operator to the corresponding `ast` type.
 #[must_use]
@@ -266,3 +266,107 @@ fn ast_matchblock(matchblock_expr: &'tcx Expr<'tcx>) -> Option<Vec<&Expr<'_>>> {
     }
     None
 }
+
+/// A parsed `format!` expansion
+pub struct FormatExpn<'tcx> {
+    /// Span of `format!(..)`
+    pub call_site: Span,
+    /// Inner `format_args!` expansion
+    pub format_args: FormatArgsExpn<'tcx>,
+}
+
+impl FormatExpn<'tcx> {
+    /// Parses an expanded `format!` invocation
+    pub fn parse(expr: &'tcx Expr<'tcx>) -> Option<Self> {
+        if_chain! {
+            if let ExprKind::Block(block, _) = expr.kind;
+            if let [stmt] = block.stmts;
+            if let StmtKind::Local(local) = stmt.kind;
+            if let Some(init) = local.init;
+            if let ExprKind::Call(_, [format_args]) = init.kind;
+            let expn_data = expr.span.ctxt().outer_expn_data();
+            if let ExpnKind::Macro(_, sym::format) = expn_data.kind;
+            if let Some(format_args) = FormatArgsExpn::parse(format_args);
+            then {
+                Some(FormatExpn {
+                    call_site: expn_data.call_site,
+                    format_args,
+                })
+            } else {
+                None
+            }
+        }
+    }
+}
+
+/// A parsed `format_args!` expansion
+pub struct FormatArgsExpn<'tcx> {
+    /// Span of the first argument, the format string
+    pub format_string_span: Span,
+    /// Values passed after the format string
+    pub value_args: Vec<&'tcx Expr<'tcx>>,
+
+    /// String literal expressions which represent the format string split by "{}"
+    pub format_string_parts: &'tcx [Expr<'tcx>],
+    /// Symbols corresponding to [`format_string_parts`]
+    pub format_string_symbols: Vec<Symbol>,
+    /// Expressions like `ArgumentV1::new(arg0, Debug::fmt)`
+    pub args: &'tcx [Expr<'tcx>],
+    /// The final argument passed to `Arguments::new_v1_formatted`, if applicable
+    pub fmt_expr: Option<&'tcx Expr<'tcx>>,
+}
+
+impl FormatArgsExpn<'tcx> {
+    /// Parses an expanded `format_args!` or `format_args_nl!` invocation
+    pub fn parse(expr: &'tcx Expr<'tcx>) -> Option<Self> {
+        if_chain! {
+            if let ExpnKind::Macro(_, name) = expr.span.ctxt().outer_expn_data().kind;
+            let name = name.as_str();
+            if name.ends_with("format_args") || name.ends_with("format_args_nl");
+            if let ExprKind::Call(_, args) = expr.kind;
+            if let Some((strs_ref, args, fmt_expr)) = match args {
+                // Arguments::new_v1
+                [strs_ref, args] => Some((strs_ref, args, None)),
+                // Arguments::new_v1_formatted
+                [strs_ref, args, fmt_expr] => Some((strs_ref, args, Some(fmt_expr))),
+                _ => None,
+            };
+            if let ExprKind::AddrOf(BorrowKind::Ref, _, strs_arr) = strs_ref.kind;
+            if let ExprKind::Array(format_string_parts) = strs_arr.kind;
+            if let Some(format_string_symbols) = format_string_parts
+                .iter()
+                .map(|e| {
+                    if let ExprKind::Lit(lit) = &e.kind {
+                        if let LitKind::Str(symbol, _style) = lit.node {
+                            return Some(symbol);
+                        }
+                    }
+                    None
+                })
+                .collect();
+            if let ExprKind::AddrOf(BorrowKind::Ref, _, args) = args.kind;
+            if let ExprKind::Match(args, [arm], _) = args.kind;
+            if let ExprKind::Tup(value_args) = args.kind;
+            if let Some(value_args) = value_args
+                .iter()
+                .map(|e| match e.kind {
+                    ExprKind::AddrOf(_, _, e) => Some(e),
+                    _ => None,
+                })
+                .collect();
+            if let ExprKind::Array(args) = arm.body.kind;
+            then {
+                Some(FormatArgsExpn {
+                    format_string_span: strs_ref.span,
+                    value_args,
+                    format_string_parts,
+                    format_string_symbols,
+                    args,
+                    fmt_expr,
+                })
+            } else {
+                None
+            }
+        }
+    }
+}
index 364ae536ecd54ae3654739635791b5e34727ca66..6db221ab0fdfdf2a3893c623e14afc0d7eb479a5 100644 (file)
@@ -483,7 +483,7 @@ fn item_child_by_name<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, name: &str) -> Opt
         _ => return Res::Err,
     };
     let tcx = cx.tcx;
-    let crates = tcx.crates();
+    let crates = tcx.crates(());
     let krate = try_res!(crates.iter().find(|&&num| tcx.crate_name(num).as_str() == krate));
     let first = try_res!(item_child_by_name(tcx, krate.as_def_id(), first));
     let last = path
@@ -953,12 +953,7 @@ pub fn is_expn_of(mut span: Span, name: &str) -> Option<Span> {
             let data = span.ctxt().outer_expn_data();
             let new_span = data.call_site;
 
-            if let ExpnKind::Macro {
-                kind: MacroKind::Bang,
-                name: mac_name,
-                proc_macro: _,
-            } = data.kind
-            {
+            if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind {
                 if mac_name.as_str() == name {
                     return Some(new_span);
                 }
@@ -986,12 +981,7 @@ pub fn is_direct_expn_of(span: Span, name: &str) -> Option<Span> {
         let data = span.ctxt().outer_expn_data();
         let new_span = data.call_site;
 
-        if let ExpnKind::Macro {
-            kind: MacroKind::Bang,
-            name: mac_name,
-            proc_macro: _,
-        } = data.kind
-        {
+        if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind {
             if mac_name.as_str() == name {
                 return Some(new_span);
             }
index 546706d51d7b597c3f6d03b90a1448152533d37b..4a28c7dd9a04a3885251ef7ab44e7d5ea65aa7fe 100644 (file)
@@ -162,6 +162,9 @@ pub fn format(&self) -> String {
         }
 
         if let Some(suffix) = self.suffix {
+            if output.ends_with('.') {
+                output.push('0');
+            }
             output.push('_');
             output.push_str(suffix);
         }
index 9ebfbd6b423d816785724a4a08b92aa0ce3d7715..c960eec306414620449cd6d05e189080b8752867 100644 (file)
@@ -38,7 +38,6 @@
 pub const DEREF_MUT_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "DerefMut", "deref_mut"];
 pub const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"];
 pub const DIR_BUILDER: [&str; 3] = ["std", "fs", "DirBuilder"];
-pub const DISPLAY_FMT_METHOD: [&str; 4] = ["core", "fmt", "Display", "fmt"];
 pub const DISPLAY_TRAIT: [&str; 3] = ["core", "fmt", "Display"];
 pub const DOUBLE_ENDED_ITERATOR: [&str; 4] = ["core", "iter", "traits", "DoubleEndedIterator"];
 pub const DROP: [&str; 3] = ["core", "mem", "drop"];
@@ -50,9 +49,6 @@
 pub const F64_EPSILON: [&str; 4] = ["core", "f64", "<impl f64>", "EPSILON"];
 pub const FILE: [&str; 3] = ["std", "fs", "File"];
 pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"];
-pub const FMT_ARGUMENTS_NEW_V1: [&str; 4] = ["core", "fmt", "Arguments", "new_v1"];
-pub const FMT_ARGUMENTS_NEW_V1_FORMATTED: [&str; 4] = ["core", "fmt", "Arguments", "new_v1_formatted"];
-pub const FMT_ARGUMENTV1_NEW: [&str; 4] = ["core", "fmt", "ArgumentV1", "new"];
 pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"];
 pub const FROM_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "FromIterator"];
 pub const FROM_ITERATOR_METHOD: [&str; 6] = ["core", "iter", "traits", "collect", "FromIterator", "from_iter"];
index 9e271b71204b588625de12ad89e52bbca7416f73..3f5c5604d43f5e5b481cb370ad45739cf05e3942 100644 (file)
@@ -14,6 +14,7 @@
 use rustc_span::sym;
 use rustc_span::symbol::{Ident, Symbol};
 use rustc_span::DUMMY_SP;
+use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits::query::normalize::AtExt;
 
 use crate::{match_def_path, must_use_attr};
@@ -112,6 +113,7 @@ pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option<
 }
 
 /// Checks whether a type implements a trait.
+/// The function returns false in case the type contains an inference variable.
 /// See also `get_trait_def_id`.
 pub fn implements_trait<'tcx>(
     cx: &LateContext<'tcx>,
@@ -119,16 +121,19 @@ pub fn implements_trait<'tcx>(
     trait_id: DefId,
     ty_params: &[GenericArg<'tcx>],
 ) -> bool {
-    // Do not check on infer_types to avoid panic in evaluate_obligation.
-    if ty.has_infer_types() {
-        return false;
-    }
+    // Clippy shouldn't have infer types
+    assert!(!ty.needs_infer());
+
     let ty = cx.tcx.erase_regions(ty);
     if ty.has_escaping_bound_vars() {
         return false;
     }
     let ty_params = cx.tcx.mk_substs(ty_params.iter());
-    cx.tcx.type_implements_trait((trait_id, ty, ty_params, cx.param_env))
+    cx.tcx.infer_ctxt().enter(|infcx| {
+        infcx
+            .type_implements_trait(trait_id, ty, ty_params, cx.param_env)
+            .must_apply_modulo_regions()
+    })
 }
 
 /// Checks whether this type implements `Drop`.
index 2c55021ac88374f0c8fe450e8d8d3bbd75127b6d..182d8cb11ea80df397453a5bd62cb995ca8636ad 100644 (file)
@@ -199,3 +199,50 @@ pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool {
     recursive_visitor.visit_expr(expression);
     recursive_visitor.seen_return_break_continue
 }
+
+pub struct UsedAfterExprVisitor<'a, 'tcx> {
+    cx: &'a LateContext<'tcx>,
+    expr: &'tcx Expr<'tcx>,
+    definition: HirId,
+    past_expr: bool,
+    used_after_expr: bool,
+}
+impl<'a, 'tcx> UsedAfterExprVisitor<'a, 'tcx> {
+    pub fn is_found(cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
+        utils::path_to_local(expr).map_or(false, |definition| {
+            let mut visitor = UsedAfterExprVisitor {
+                cx,
+                expr,
+                definition,
+                past_expr: false,
+                used_after_expr: false,
+            };
+            utils::get_enclosing_block(cx, definition).map_or(false, |block| {
+                visitor.visit_block(block);
+                visitor.used_after_expr
+            })
+        })
+    }
+}
+
+impl<'a, 'tcx> intravisit::Visitor<'tcx> for UsedAfterExprVisitor<'a, 'tcx> {
+    type Map = Map<'tcx>;
+
+    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+        NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
+    }
+
+    fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
+        if self.used_after_expr {
+            return;
+        }
+
+        if expr.hir_id == self.expr.hir_id {
+            self.past_expr = true;
+        } else if self.past_expr && utils::path_to_local_id(expr, self.definition) {
+            self.used_after_expr = true;
+        } else {
+            intravisit::walk_expr(self, expr);
+        }
+    }
+}
index 2d3c65c1d3982fa68d7555c7308ee11aecbbde2a..bf9cfb61b46d1a0e5d9895a91f50d36cb4699b5d 100644 (file)
@@ -1,3 +1,3 @@
 [toolchain]
-channel = "nightly-2021-07-01"
+channel = "nightly-2021-07-15"
 components = ["llvm-tools-preview", "rustc-dev", "rust-src"]
index 6617ca183a8cb2cbd37c0fa60c3b4fe1a1eb23cc..2180f848d62cd256f7538d100d6cf5c634398ec2 100644 (file)
@@ -1,4 +1,4 @@
-#![allow(non_fmt_panic)]
+#![allow(non_fmt_panics)]
 
 macro_rules! assert_const {
     ($len:expr) => {
diff --git a/tests/ui/branches_sharing_code/false_positives.rs b/tests/ui/branches_sharing_code/false_positives.rs
new file mode 100644 (file)
index 0000000..7f42df4
--- /dev/null
@@ -0,0 +1,28 @@
+#![allow(dead_code)]
+#![deny(clippy::if_same_then_else, clippy::branches_sharing_code)]
+
+// ##################################
+// # Issue clippy#7369
+// ##################################
+#[derive(Debug)]
+pub struct FooBar {
+    foo: Vec<u32>,
+}
+
+impl FooBar {
+    pub fn bar(&mut self) {
+        if true {
+            self.foo.pop();
+        } else {
+            self.baz();
+
+            self.foo.pop();
+
+            self.baz()
+        }
+    }
+
+    fn baz(&mut self) {}
+}
+
+fn main() {}
diff --git a/tests/ui/default_numeric_fallback.rs b/tests/ui/default_numeric_fallback.rs
deleted file mode 100644 (file)
index c0625fd..0000000
+++ /dev/null
@@ -1,159 +0,0 @@
-// aux-build:macro_rules.rs
-
-#![warn(clippy::default_numeric_fallback)]
-#![allow(unused)]
-#![allow(clippy::never_loop)]
-#![allow(clippy::no_effect)]
-#![allow(clippy::unnecessary_operation)]
-#![allow(clippy::branches_sharing_code)]
-
-#[macro_use]
-extern crate macro_rules;
-
-mod basic_expr {
-    fn test() {
-        // Should lint unsuffixed literals typed `i32`.
-        let x = 22;
-        let x = [1, 2, 3];
-        let x = if true { (1, 2) } else { (3, 4) };
-        let x = match 1 {
-            1 => 1,
-            _ => 2,
-        };
-
-        // Should lint unsuffixed literals typed `f64`.
-        let x = 0.12;
-
-        // Should NOT lint suffixed literals.
-        let x = 22_i32;
-        let x = 0.12_f64;
-
-        // Should NOT lint literals in init expr if `Local` has a type annotation.
-        let x: f64 = 0.1;
-        let x: [i32; 3] = [1, 2, 3];
-        let x: (i32, i32) = if true { (1, 2) } else { (3, 4) };
-        let x: _ = 1;
-    }
-}
-
-mod nested_local {
-    fn test() {
-        let x: _ = {
-            // Should lint this because this literal is not bound to any types.
-            let y = 1;
-
-            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
-            1
-        };
-
-        let x: _ = if true {
-            // Should lint this because this literal is not bound to any types.
-            let y = 1;
-
-            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
-            1
-        } else {
-            // Should lint this because this literal is not bound to any types.
-            let y = 1;
-
-            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
-            2
-        };
-    }
-}
-
-mod function_def {
-    fn ret_i32() -> i32 {
-        // Even though the output type is specified,
-        // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
-        1
-    }
-
-    fn test() {
-        // Should lint this because return type is inferred to `i32` and NOT bound to a concrete
-        // type.
-        let f = || -> _ { 1 };
-
-        // Even though the output type is specified,
-        // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
-        let f = || -> i32 { 1 };
-    }
-}
-
-mod function_calls {
-    fn concrete_arg(x: i32) {}
-
-    fn generic_arg<T>(t: T) {}
-
-    fn test() {
-        // Should NOT lint this because the argument type is bound to a concrete type.
-        concrete_arg(1);
-
-        // Should lint this because the argument type is inferred to `i32` and NOT bound to a concrete type.
-        generic_arg(1);
-
-        // Should lint this because the argument type is inferred to `i32` and NOT bound to a concrete type.
-        let x: _ = generic_arg(1);
-    }
-}
-
-mod struct_ctor {
-    struct ConcreteStruct {
-        x: i32,
-    }
-
-    struct GenericStruct<T> {
-        x: T,
-    }
-
-    fn test() {
-        // Should NOT lint this because the field type is bound to a concrete type.
-        ConcreteStruct { x: 1 };
-
-        // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
-        GenericStruct { x: 1 };
-
-        // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
-        let _ = GenericStruct { x: 1 };
-    }
-}
-
-mod method_calls {
-    struct StructForMethodCallTest {}
-
-    impl StructForMethodCallTest {
-        fn concrete_arg(&self, x: i32) {}
-
-        fn generic_arg<T>(&self, t: T) {}
-    }
-
-    fn test() {
-        let s = StructForMethodCallTest {};
-
-        // Should NOT lint this because the argument type is bound to a concrete type.
-        s.concrete_arg(1);
-
-        // Should lint this because the argument type is bound to a concrete type.
-        s.generic_arg(1);
-    }
-}
-
-mod in_macro {
-    macro_rules! internal_macro {
-        () => {
-            let x = 22;
-        };
-    }
-
-    // Should lint in internal macro.
-    fn internal() {
-        internal_macro!();
-    }
-
-    // Should NOT lint in external macro.
-    fn external() {
-        default_numeric_fallback!();
-    }
-}
-
-fn main() {}
diff --git a/tests/ui/default_numeric_fallback.stderr b/tests/ui/default_numeric_fallback.stderr
deleted file mode 100644 (file)
index 5862cd9..0000000
+++ /dev/null
@@ -1,159 +0,0 @@
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:16:17
-   |
-LL |         let x = 22;
-   |                 ^^ help: consider adding suffix: `22_i32`
-   |
-   = note: `-D clippy::default-numeric-fallback` implied by `-D warnings`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:17:18
-   |
-LL |         let x = [1, 2, 3];
-   |                  ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:17:21
-   |
-LL |         let x = [1, 2, 3];
-   |                     ^ help: consider adding suffix: `2_i32`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:17:24
-   |
-LL |         let x = [1, 2, 3];
-   |                        ^ help: consider adding suffix: `3_i32`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:18:28
-   |
-LL |         let x = if true { (1, 2) } else { (3, 4) };
-   |                            ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:18:31
-   |
-LL |         let x = if true { (1, 2) } else { (3, 4) };
-   |                               ^ help: consider adding suffix: `2_i32`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:18:44
-   |
-LL |         let x = if true { (1, 2) } else { (3, 4) };
-   |                                            ^ help: consider adding suffix: `3_i32`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:18:47
-   |
-LL |         let x = if true { (1, 2) } else { (3, 4) };
-   |                                               ^ help: consider adding suffix: `4_i32`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:19:23
-   |
-LL |         let x = match 1 {
-   |                       ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:20:13
-   |
-LL |             1 => 1,
-   |             ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:20:18
-   |
-LL |             1 => 1,
-   |                  ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:21:18
-   |
-LL |             _ => 2,
-   |                  ^ help: consider adding suffix: `2_i32`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:25:17
-   |
-LL |         let x = 0.12;
-   |                 ^^^^ help: consider adding suffix: `0.12_f64`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:43:21
-   |
-LL |             let y = 1;
-   |                     ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:51:21
-   |
-LL |             let y = 1;
-   |                     ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:57:21
-   |
-LL |             let y = 1;
-   |                     ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:69:9
-   |
-LL |         1
-   |         ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:75:27
-   |
-LL |         let f = || -> _ { 1 };
-   |                           ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:79:29
-   |
-LL |         let f = || -> i32 { 1 };
-   |                             ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:93:21
-   |
-LL |         generic_arg(1);
-   |                     ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:96:32
-   |
-LL |         let x: _ = generic_arg(1);
-   |                                ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:114:28
-   |
-LL |         GenericStruct { x: 1 };
-   |                            ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:117:36
-   |
-LL |         let _ = GenericStruct { x: 1 };
-   |                                    ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:137:23
-   |
-LL |         s.generic_arg(1);
-   |                       ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback.rs:144:21
-   |
-LL |             let x = 22;
-   |                     ^^ help: consider adding suffix: `22_i32`
-...
-LL |         internal_macro!();
-   |         ------------------ in this macro invocation
-   |
-   = note: this error originates in the macro `internal_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error: aborting due to 25 previous errors
-
diff --git a/tests/ui/default_numeric_fallback_f64.fixed b/tests/ui/default_numeric_fallback_f64.fixed
new file mode 100644 (file)
index 0000000..1b0e754
--- /dev/null
@@ -0,0 +1,174 @@
+// run-rustfix
+// aux-build:macro_rules.rs
+
+#![warn(clippy::default_numeric_fallback)]
+#![allow(unused)]
+#![allow(clippy::never_loop)]
+#![allow(clippy::no_effect)]
+#![allow(clippy::unnecessary_operation)]
+#![allow(clippy::branches_sharing_code)]
+#![allow(clippy::match_single_binding)]
+
+#[macro_use]
+extern crate macro_rules;
+
+mod basic_expr {
+    fn test() {
+        // Should lint unsuffixed literals typed `f64`.
+        let x = 0.12_f64;
+        let x = [1.0_f64, 2.0_f64, 3.0_f64];
+        let x = if true { (1.0_f64, 2.0_f64) } else { (3.0_f64, 4.0_f64) };
+        let x = match 1.0_f64 {
+            _ => 1.0_f64,
+        };
+
+        // Should NOT lint suffixed literals.
+        let x = 0.12_f64;
+
+        // Should NOT lint literals in init expr if `Local` has a type annotation.
+        let x: f64 = 0.1;
+        let x: [f64; 3] = [1., 2., 3.];
+        let x: (f64, f64) = if true { (1., 2.) } else { (3., 4.) };
+        let x: _ = 1.;
+    }
+}
+
+mod nested_local {
+    fn test() {
+        let x: _ = {
+            // Should lint this because this literal is not bound to any types.
+            let y = 1.0_f64;
+
+            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+            1.
+        };
+
+        let x: _ = if true {
+            // Should lint this because this literal is not bound to any types.
+            let y = 1.0_f64;
+
+            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+            1.
+        } else {
+            // Should lint this because this literal is not bound to any types.
+            let y = 1.0_f64;
+
+            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+            2.
+        };
+    }
+}
+
+mod function_def {
+    fn ret_f64() -> f64 {
+        // Even though the output type is specified,
+        // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
+        1.0_f64
+    }
+
+    fn test() {
+        // Should lint this because return type is inferred to `f64` and NOT bound to a concrete
+        // type.
+        let f = || -> _ { 1.0_f64 };
+
+        // Even though the output type is specified,
+        // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
+        let f = || -> f64 { 1.0_f64 };
+    }
+}
+
+mod function_calls {
+    fn concrete_arg(f: f64) {}
+
+    fn generic_arg<T>(t: T) {}
+
+    fn test() {
+        // Should NOT lint this because the argument type is bound to a concrete type.
+        concrete_arg(1.);
+
+        // Should lint this because the argument type is inferred to `f64` and NOT bound to a concrete type.
+        generic_arg(1.0_f64);
+
+        // Should lint this because the argument type is inferred to `f64` and NOT bound to a concrete type.
+        let x: _ = generic_arg(1.0_f64);
+    }
+}
+
+mod struct_ctor {
+    struct ConcreteStruct {
+        x: f64,
+    }
+
+    struct GenericStruct<T> {
+        x: T,
+    }
+
+    fn test() {
+        // Should NOT lint this because the field type is bound to a concrete type.
+        ConcreteStruct { x: 1. };
+
+        // Should lint this because the field type is inferred to `f64` and NOT bound to a concrete type.
+        GenericStruct { x: 1.0_f64 };
+
+        // Should lint this because the field type is inferred to `f64` and NOT bound to a concrete type.
+        let _ = GenericStruct { x: 1.0_f64 };
+    }
+}
+
+mod enum_ctor {
+    enum ConcreteEnum {
+        X(f64),
+    }
+
+    enum GenericEnum<T> {
+        X(T),
+    }
+
+    fn test() {
+        // Should NOT lint this because the field type is bound to a concrete type.
+        ConcreteEnum::X(1.);
+
+        // Should lint this because the field type is inferred to `f64` and NOT bound to a concrete type.
+        GenericEnum::X(1.0_f64);
+    }
+}
+
+mod method_calls {
+    struct StructForMethodCallTest {}
+
+    impl StructForMethodCallTest {
+        fn concrete_arg(&self, f: f64) {}
+
+        fn generic_arg<T>(&self, t: T) {}
+    }
+
+    fn test() {
+        let s = StructForMethodCallTest {};
+
+        // Should NOT lint this because the argument type is bound to a concrete type.
+        s.concrete_arg(1.);
+
+        // Should lint this because the argument type is bound to a concrete type.
+        s.generic_arg(1.0_f64);
+    }
+}
+
+mod in_macro {
+    macro_rules! internal_macro {
+        () => {
+            let x = 22.0_f64;
+        };
+    }
+
+    // Should lint in internal macro.
+    fn internal() {
+        internal_macro!();
+    }
+
+    // Should NOT lint in external macro.
+    fn external() {
+        default_numeric_fallback!();
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/default_numeric_fallback_f64.rs b/tests/ui/default_numeric_fallback_f64.rs
new file mode 100644 (file)
index 0000000..e968777
--- /dev/null
@@ -0,0 +1,174 @@
+// run-rustfix
+// aux-build:macro_rules.rs
+
+#![warn(clippy::default_numeric_fallback)]
+#![allow(unused)]
+#![allow(clippy::never_loop)]
+#![allow(clippy::no_effect)]
+#![allow(clippy::unnecessary_operation)]
+#![allow(clippy::branches_sharing_code)]
+#![allow(clippy::match_single_binding)]
+
+#[macro_use]
+extern crate macro_rules;
+
+mod basic_expr {
+    fn test() {
+        // Should lint unsuffixed literals typed `f64`.
+        let x = 0.12;
+        let x = [1., 2., 3.];
+        let x = if true { (1., 2.) } else { (3., 4.) };
+        let x = match 1. {
+            _ => 1.,
+        };
+
+        // Should NOT lint suffixed literals.
+        let x = 0.12_f64;
+
+        // Should NOT lint literals in init expr if `Local` has a type annotation.
+        let x: f64 = 0.1;
+        let x: [f64; 3] = [1., 2., 3.];
+        let x: (f64, f64) = if true { (1., 2.) } else { (3., 4.) };
+        let x: _ = 1.;
+    }
+}
+
+mod nested_local {
+    fn test() {
+        let x: _ = {
+            // Should lint this because this literal is not bound to any types.
+            let y = 1.;
+
+            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+            1.
+        };
+
+        let x: _ = if true {
+            // Should lint this because this literal is not bound to any types.
+            let y = 1.;
+
+            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+            1.
+        } else {
+            // Should lint this because this literal is not bound to any types.
+            let y = 1.;
+
+            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+            2.
+        };
+    }
+}
+
+mod function_def {
+    fn ret_f64() -> f64 {
+        // Even though the output type is specified,
+        // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
+        1.
+    }
+
+    fn test() {
+        // Should lint this because return type is inferred to `f64` and NOT bound to a concrete
+        // type.
+        let f = || -> _ { 1. };
+
+        // Even though the output type is specified,
+        // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
+        let f = || -> f64 { 1. };
+    }
+}
+
+mod function_calls {
+    fn concrete_arg(f: f64) {}
+
+    fn generic_arg<T>(t: T) {}
+
+    fn test() {
+        // Should NOT lint this because the argument type is bound to a concrete type.
+        concrete_arg(1.);
+
+        // Should lint this because the argument type is inferred to `f64` and NOT bound to a concrete type.
+        generic_arg(1.);
+
+        // Should lint this because the argument type is inferred to `f64` and NOT bound to a concrete type.
+        let x: _ = generic_arg(1.);
+    }
+}
+
+mod struct_ctor {
+    struct ConcreteStruct {
+        x: f64,
+    }
+
+    struct GenericStruct<T> {
+        x: T,
+    }
+
+    fn test() {
+        // Should NOT lint this because the field type is bound to a concrete type.
+        ConcreteStruct { x: 1. };
+
+        // Should lint this because the field type is inferred to `f64` and NOT bound to a concrete type.
+        GenericStruct { x: 1. };
+
+        // Should lint this because the field type is inferred to `f64` and NOT bound to a concrete type.
+        let _ = GenericStruct { x: 1. };
+    }
+}
+
+mod enum_ctor {
+    enum ConcreteEnum {
+        X(f64),
+    }
+
+    enum GenericEnum<T> {
+        X(T),
+    }
+
+    fn test() {
+        // Should NOT lint this because the field type is bound to a concrete type.
+        ConcreteEnum::X(1.);
+
+        // Should lint this because the field type is inferred to `f64` and NOT bound to a concrete type.
+        GenericEnum::X(1.);
+    }
+}
+
+mod method_calls {
+    struct StructForMethodCallTest {}
+
+    impl StructForMethodCallTest {
+        fn concrete_arg(&self, f: f64) {}
+
+        fn generic_arg<T>(&self, t: T) {}
+    }
+
+    fn test() {
+        let s = StructForMethodCallTest {};
+
+        // Should NOT lint this because the argument type is bound to a concrete type.
+        s.concrete_arg(1.);
+
+        // Should lint this because the argument type is bound to a concrete type.
+        s.generic_arg(1.);
+    }
+}
+
+mod in_macro {
+    macro_rules! internal_macro {
+        () => {
+            let x = 22.;
+        };
+    }
+
+    // Should lint in internal macro.
+    fn internal() {
+        internal_macro!();
+    }
+
+    // Should NOT lint in external macro.
+    fn external() {
+        default_numeric_fallback!();
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/default_numeric_fallback_f64.stderr b/tests/ui/default_numeric_fallback_f64.stderr
new file mode 100644 (file)
index 0000000..961c7cb
--- /dev/null
@@ -0,0 +1,147 @@
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_f64.rs:18:17
+   |
+LL |         let x = 0.12;
+   |                 ^^^^ help: consider adding suffix: `0.12_f64`
+   |
+   = note: `-D clippy::default-numeric-fallback` implied by `-D warnings`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_f64.rs:19:18
+   |
+LL |         let x = [1., 2., 3.];
+   |                  ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_f64.rs:19:22
+   |
+LL |         let x = [1., 2., 3.];
+   |                      ^^ help: consider adding suffix: `2.0_f64`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_f64.rs:19:26
+   |
+LL |         let x = [1., 2., 3.];
+   |                          ^^ help: consider adding suffix: `3.0_f64`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_f64.rs:20:28
+   |
+LL |         let x = if true { (1., 2.) } else { (3., 4.) };
+   |                            ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_f64.rs:20:32
+   |
+LL |         let x = if true { (1., 2.) } else { (3., 4.) };
+   |                                ^^ help: consider adding suffix: `2.0_f64`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_f64.rs:20:46
+   |
+LL |         let x = if true { (1., 2.) } else { (3., 4.) };
+   |                                              ^^ help: consider adding suffix: `3.0_f64`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_f64.rs:20:50
+   |
+LL |         let x = if true { (1., 2.) } else { (3., 4.) };
+   |                                                  ^^ help: consider adding suffix: `4.0_f64`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_f64.rs:21:23
+   |
+LL |         let x = match 1. {
+   |                       ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_f64.rs:22:18
+   |
+LL |             _ => 1.,
+   |                  ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_f64.rs:40:21
+   |
+LL |             let y = 1.;
+   |                     ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_f64.rs:48:21
+   |
+LL |             let y = 1.;
+   |                     ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_f64.rs:54:21
+   |
+LL |             let y = 1.;
+   |                     ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_f64.rs:66:9
+   |
+LL |         1.
+   |         ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_f64.rs:72:27
+   |
+LL |         let f = || -> _ { 1. };
+   |                           ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_f64.rs:76:29
+   |
+LL |         let f = || -> f64 { 1. };
+   |                             ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_f64.rs:90:21
+   |
+LL |         generic_arg(1.);
+   |                     ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_f64.rs:93:32
+   |
+LL |         let x: _ = generic_arg(1.);
+   |                                ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_f64.rs:111:28
+   |
+LL |         GenericStruct { x: 1. };
+   |                            ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_f64.rs:114:36
+   |
+LL |         let _ = GenericStruct { x: 1. };
+   |                                    ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_f64.rs:132:24
+   |
+LL |         GenericEnum::X(1.);
+   |                        ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_f64.rs:152:23
+   |
+LL |         s.generic_arg(1.);
+   |                       ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_f64.rs:159:21
+   |
+LL |             let x = 22.;
+   |                     ^^^ help: consider adding suffix: `22.0_f64`
+...
+LL |         internal_macro!();
+   |         ------------------ in this macro invocation
+   |
+   = note: this error originates in the macro `internal_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 23 previous errors
+
diff --git a/tests/ui/default_numeric_fallback_i32.fixed b/tests/ui/default_numeric_fallback_i32.fixed
new file mode 100644 (file)
index 0000000..55c082f
--- /dev/null
@@ -0,0 +1,173 @@
+// run-rustfix
+// aux-build:macro_rules.rs
+
+#![warn(clippy::default_numeric_fallback)]
+#![allow(unused)]
+#![allow(clippy::never_loop)]
+#![allow(clippy::no_effect)]
+#![allow(clippy::unnecessary_operation)]
+#![allow(clippy::branches_sharing_code)]
+
+#[macro_use]
+extern crate macro_rules;
+
+mod basic_expr {
+    fn test() {
+        // Should lint unsuffixed literals typed `i32`.
+        let x = 22_i32;
+        let x = [1_i32, 2_i32, 3_i32];
+        let x = if true { (1_i32, 2_i32) } else { (3_i32, 4_i32) };
+        let x = match 1_i32 {
+            1_i32 => 1_i32,
+            _ => 2_i32,
+        };
+
+        // Should NOT lint suffixed literals.
+        let x = 22_i32;
+
+        // Should NOT lint literals in init expr if `Local` has a type annotation.
+        let x: [i32; 3] = [1, 2, 3];
+        let x: (i32, i32) = if true { (1, 2) } else { (3, 4) };
+        let x: _ = 1;
+    }
+}
+
+mod nested_local {
+    fn test() {
+        let x: _ = {
+            // Should lint this because this literal is not bound to any types.
+            let y = 1_i32;
+
+            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+            1
+        };
+
+        let x: _ = if true {
+            // Should lint this because this literal is not bound to any types.
+            let y = 1_i32;
+
+            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+            1
+        } else {
+            // Should lint this because this literal is not bound to any types.
+            let y = 1_i32;
+
+            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+            2
+        };
+    }
+}
+
+mod function_def {
+    fn ret_i32() -> i32 {
+        // Even though the output type is specified,
+        // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
+        1_i32
+    }
+
+    fn test() {
+        // Should lint this because return type is inferred to `i32` and NOT bound to a concrete
+        // type.
+        let f = || -> _ { 1_i32 };
+
+        // Even though the output type is specified,
+        // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
+        let f = || -> i32 { 1_i32 };
+    }
+}
+
+mod function_calls {
+    fn concrete_arg(x: i32) {}
+
+    fn generic_arg<T>(t: T) {}
+
+    fn test() {
+        // Should NOT lint this because the argument type is bound to a concrete type.
+        concrete_arg(1);
+
+        // Should lint this because the argument type is inferred to `i32` and NOT bound to a concrete type.
+        generic_arg(1_i32);
+
+        // Should lint this because the argument type is inferred to `i32` and NOT bound to a concrete type.
+        let x: _ = generic_arg(1_i32);
+    }
+}
+
+mod struct_ctor {
+    struct ConcreteStruct {
+        x: i32,
+    }
+
+    struct GenericStruct<T> {
+        x: T,
+    }
+
+    fn test() {
+        // Should NOT lint this because the field type is bound to a concrete type.
+        ConcreteStruct { x: 1 };
+
+        // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
+        GenericStruct { x: 1_i32 };
+
+        // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
+        let _ = GenericStruct { x: 1_i32 };
+    }
+}
+
+mod enum_ctor {
+    enum ConcreteEnum {
+        X(i32),
+    }
+
+    enum GenericEnum<T> {
+        X(T),
+    }
+
+    fn test() {
+        // Should NOT lint this because the field type is bound to a concrete type.
+        ConcreteEnum::X(1);
+
+        // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
+        GenericEnum::X(1_i32);
+    }
+}
+
+mod method_calls {
+    struct StructForMethodCallTest {}
+
+    impl StructForMethodCallTest {
+        fn concrete_arg(&self, x: i32) {}
+
+        fn generic_arg<T>(&self, t: T) {}
+    }
+
+    fn test() {
+        let s = StructForMethodCallTest {};
+
+        // Should NOT lint this because the argument type is bound to a concrete type.
+        s.concrete_arg(1);
+
+        // Should lint this because the argument type is bound to a concrete type.
+        s.generic_arg(1_i32);
+    }
+}
+
+mod in_macro {
+    macro_rules! internal_macro {
+        () => {
+            let x = 22_i32;
+        };
+    }
+
+    // Should lint in internal macro.
+    fn internal() {
+        internal_macro!();
+    }
+
+    // Should NOT lint in external macro.
+    fn external() {
+        default_numeric_fallback!();
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/default_numeric_fallback_i32.rs b/tests/ui/default_numeric_fallback_i32.rs
new file mode 100644 (file)
index 0000000..e0a4828
--- /dev/null
@@ -0,0 +1,173 @@
+// run-rustfix
+// aux-build:macro_rules.rs
+
+#![warn(clippy::default_numeric_fallback)]
+#![allow(unused)]
+#![allow(clippy::never_loop)]
+#![allow(clippy::no_effect)]
+#![allow(clippy::unnecessary_operation)]
+#![allow(clippy::branches_sharing_code)]
+
+#[macro_use]
+extern crate macro_rules;
+
+mod basic_expr {
+    fn test() {
+        // Should lint unsuffixed literals typed `i32`.
+        let x = 22;
+        let x = [1, 2, 3];
+        let x = if true { (1, 2) } else { (3, 4) };
+        let x = match 1 {
+            1 => 1,
+            _ => 2,
+        };
+
+        // Should NOT lint suffixed literals.
+        let x = 22_i32;
+
+        // Should NOT lint literals in init expr if `Local` has a type annotation.
+        let x: [i32; 3] = [1, 2, 3];
+        let x: (i32, i32) = if true { (1, 2) } else { (3, 4) };
+        let x: _ = 1;
+    }
+}
+
+mod nested_local {
+    fn test() {
+        let x: _ = {
+            // Should lint this because this literal is not bound to any types.
+            let y = 1;
+
+            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+            1
+        };
+
+        let x: _ = if true {
+            // Should lint this because this literal is not bound to any types.
+            let y = 1;
+
+            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+            1
+        } else {
+            // Should lint this because this literal is not bound to any types.
+            let y = 1;
+
+            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+            2
+        };
+    }
+}
+
+mod function_def {
+    fn ret_i32() -> i32 {
+        // Even though the output type is specified,
+        // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
+        1
+    }
+
+    fn test() {
+        // Should lint this because return type is inferred to `i32` and NOT bound to a concrete
+        // type.
+        let f = || -> _ { 1 };
+
+        // Even though the output type is specified,
+        // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
+        let f = || -> i32 { 1 };
+    }
+}
+
+mod function_calls {
+    fn concrete_arg(x: i32) {}
+
+    fn generic_arg<T>(t: T) {}
+
+    fn test() {
+        // Should NOT lint this because the argument type is bound to a concrete type.
+        concrete_arg(1);
+
+        // Should lint this because the argument type is inferred to `i32` and NOT bound to a concrete type.
+        generic_arg(1);
+
+        // Should lint this because the argument type is inferred to `i32` and NOT bound to a concrete type.
+        let x: _ = generic_arg(1);
+    }
+}
+
+mod struct_ctor {
+    struct ConcreteStruct {
+        x: i32,
+    }
+
+    struct GenericStruct<T> {
+        x: T,
+    }
+
+    fn test() {
+        // Should NOT lint this because the field type is bound to a concrete type.
+        ConcreteStruct { x: 1 };
+
+        // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
+        GenericStruct { x: 1 };
+
+        // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
+        let _ = GenericStruct { x: 1 };
+    }
+}
+
+mod enum_ctor {
+    enum ConcreteEnum {
+        X(i32),
+    }
+
+    enum GenericEnum<T> {
+        X(T),
+    }
+
+    fn test() {
+        // Should NOT lint this because the field type is bound to a concrete type.
+        ConcreteEnum::X(1);
+
+        // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
+        GenericEnum::X(1);
+    }
+}
+
+mod method_calls {
+    struct StructForMethodCallTest {}
+
+    impl StructForMethodCallTest {
+        fn concrete_arg(&self, x: i32) {}
+
+        fn generic_arg<T>(&self, t: T) {}
+    }
+
+    fn test() {
+        let s = StructForMethodCallTest {};
+
+        // Should NOT lint this because the argument type is bound to a concrete type.
+        s.concrete_arg(1);
+
+        // Should lint this because the argument type is bound to a concrete type.
+        s.generic_arg(1);
+    }
+}
+
+mod in_macro {
+    macro_rules! internal_macro {
+        () => {
+            let x = 22;
+        };
+    }
+
+    // Should lint in internal macro.
+    fn internal() {
+        internal_macro!();
+    }
+
+    // Should NOT lint in external macro.
+    fn external() {
+        default_numeric_fallback!();
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/default_numeric_fallback_i32.stderr b/tests/ui/default_numeric_fallback_i32.stderr
new file mode 100644 (file)
index 0000000..5edf48b
--- /dev/null
@@ -0,0 +1,159 @@
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:17:17
+   |
+LL |         let x = 22;
+   |                 ^^ help: consider adding suffix: `22_i32`
+   |
+   = note: `-D clippy::default-numeric-fallback` implied by `-D warnings`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:18:18
+   |
+LL |         let x = [1, 2, 3];
+   |                  ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:18:21
+   |
+LL |         let x = [1, 2, 3];
+   |                     ^ help: consider adding suffix: `2_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:18:24
+   |
+LL |         let x = [1, 2, 3];
+   |                        ^ help: consider adding suffix: `3_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:19:28
+   |
+LL |         let x = if true { (1, 2) } else { (3, 4) };
+   |                            ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:19:31
+   |
+LL |         let x = if true { (1, 2) } else { (3, 4) };
+   |                               ^ help: consider adding suffix: `2_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:19:44
+   |
+LL |         let x = if true { (1, 2) } else { (3, 4) };
+   |                                            ^ help: consider adding suffix: `3_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:19:47
+   |
+LL |         let x = if true { (1, 2) } else { (3, 4) };
+   |                                               ^ help: consider adding suffix: `4_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:20:23
+   |
+LL |         let x = match 1 {
+   |                       ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:21:13
+   |
+LL |             1 => 1,
+   |             ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:21:18
+   |
+LL |             1 => 1,
+   |                  ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:22:18
+   |
+LL |             _ => 2,
+   |                  ^ help: consider adding suffix: `2_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:39:21
+   |
+LL |             let y = 1;
+   |                     ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:47:21
+   |
+LL |             let y = 1;
+   |                     ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:53:21
+   |
+LL |             let y = 1;
+   |                     ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:65:9
+   |
+LL |         1
+   |         ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:71:27
+   |
+LL |         let f = || -> _ { 1 };
+   |                           ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:75:29
+   |
+LL |         let f = || -> i32 { 1 };
+   |                             ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:89:21
+   |
+LL |         generic_arg(1);
+   |                     ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:92:32
+   |
+LL |         let x: _ = generic_arg(1);
+   |                                ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:110:28
+   |
+LL |         GenericStruct { x: 1 };
+   |                            ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:113:36
+   |
+LL |         let _ = GenericStruct { x: 1 };
+   |                                    ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:131:24
+   |
+LL |         GenericEnum::X(1);
+   |                        ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:151:23
+   |
+LL |         s.generic_arg(1);
+   |                       ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback_i32.rs:158:21
+   |
+LL |             let x = 22;
+   |                     ^^ help: consider adding suffix: `22_i32`
+...
+LL |         internal_macro!();
+   |         ------------------ in this macro invocation
+   |
+   = note: this error originates in the macro `internal_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 25 previous errors
+
index 0af6b500115dc1ae828261e7f38baa9cf6281ebf..c0002e5354310ab9bcc32c2f0753e534c02ff9eb 100644 (file)
@@ -60,11 +60,11 @@ error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cs
 LL | #[warn(clippy::temporary_cstring_as_ptr)]
    |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr`
 
-error: lint `clippy::panic_params` has been renamed to `non_fmt_panic`
+error: lint `clippy::panic_params` has been renamed to `non_fmt_panics`
   --> $DIR/deprecated.rs:11:8
    |
 LL | #[warn(clippy::panic_params)]
-   |        ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panic`
+   |        ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics`
 
 error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints`
   --> $DIR/deprecated.rs:12:8
index 9e752311c67785c55478511712db79a6a003109d..91b837f9a85884d19cc74471d262c1016987cf79 100644 (file)
@@ -220,3 +220,19 @@ impl std::ops::Deref for Bar {
 fn test_deref_with_trait_method() {
     let _ = [Bar].iter().map(|s| s.to_string()).collect::<Vec<_>>();
 }
+
+fn mutable_closure_used_again(x: Vec<i32>, y: Vec<i32>, z: Vec<i32>) {
+    let mut res = Vec::new();
+    let mut add_to_res = |n| res.push(n);
+    x.into_iter().for_each(&mut add_to_res);
+    y.into_iter().for_each(&mut add_to_res);
+    z.into_iter().for_each(add_to_res);
+}
+
+fn mutable_closure_in_loop() {
+    let mut value = 0;
+    let mut closure = |n| value += n;
+    for _ in 0..5 {
+        Some(1).map(&mut closure);
+    }
+}
index 44be4628cbd34227eb51118c6698aa38f9d67efd..1b53700289db303678d57850d0b59eb7a17b998d 100644 (file)
@@ -220,3 +220,19 @@ fn deref(&self) -> &str {
 fn test_deref_with_trait_method() {
     let _ = [Bar].iter().map(|s| s.to_string()).collect::<Vec<_>>();
 }
+
+fn mutable_closure_used_again(x: Vec<i32>, y: Vec<i32>, z: Vec<i32>) {
+    let mut res = Vec::new();
+    let mut add_to_res = |n| res.push(n);
+    x.into_iter().for_each(|x| add_to_res(x));
+    y.into_iter().for_each(|x| add_to_res(x));
+    z.into_iter().for_each(|x| add_to_res(x));
+}
+
+fn mutable_closure_in_loop() {
+    let mut value = 0;
+    let mut closure = |n| value += n;
+    for _ in 0..5 {
+        Some(1).map(|n| closure(n));
+    }
+}
index 8795d3b42c65af72146bf63c1d110246d81347f0..28da8941346192d0a6e6c61c478a1cc57bb5415d 100644 (file)
@@ -82,5 +82,29 @@ error: redundant closure
 LL |     let a = Some(1u8).map(|a| closure(a));
    |                           ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `closure`
 
-error: aborting due to 13 previous errors
+error: redundant closure
+  --> $DIR/eta.rs:227:28
+   |
+LL |     x.into_iter().for_each(|x| add_to_res(x));
+   |                            ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res`
+
+error: redundant closure
+  --> $DIR/eta.rs:228:28
+   |
+LL |     y.into_iter().for_each(|x| add_to_res(x));
+   |                            ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res`
+
+error: redundant closure
+  --> $DIR/eta.rs:229:28
+   |
+LL |     z.into_iter().for_each(|x| add_to_res(x));
+   |                            ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `add_to_res`
+
+error: redundant closure
+  --> $DIR/eta.rs:236:21
+   |
+LL |         Some(1).map(|n| closure(n));
+   |                     ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut closure`
+
+error: aborting due to 17 previous errors
 
index 77cadb99bb5510601922ea7034a003a2826669a2..b94ec6403ddcea3c63bfd188bbd6914679581c70 100644 (file)
@@ -1,10 +1,11 @@
-error: use of `writeln!(stderr(), ...).unwrap()`. Consider using `eprintln!` instead
+error: use of `writeln!(stderr(), ...).unwrap()`
   --> $DIR/explicit_write_non_rustfix.rs:7:5
    |
 LL |     writeln!(std::io::stderr(), "foo {}", bar).unwrap();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: `-D clippy::explicit-write` implied by `-D warnings`
+   = help: consider using `eprintln!` instead
 
 error: aborting due to previous error
 
index e4cfb005fd1d0d8e8a0ac4bd42392dddb56df35c..5dd64140e81165880f3d6285c60825838d96a0e2 100644 (file)
@@ -69,4 +69,6 @@ fn main() {
     // Wrap it with braces
     let v: Vec<String> = vec!["foo".to_string(), "bar".to_string()];
     let _s: String = (&*v.join("\n")).to_string();
+
+    format!("prepend {:+}", "s");
 }
index 683957f0ff0f79cb452811b66b0db83d90f55c8b..4599fb5207ea85c280f07b863d39f798f6262eaa 100644 (file)
@@ -71,4 +71,6 @@ fn main() {
     // Wrap it with braces
     let v: Vec<String> = vec!["foo".to_string(), "bar".to_string()];
     let _s: String = format!("{}", &*v.join("\n"));
+
+    format!("prepend {:+}", "s");
 }
diff --git a/tests/ui/redundant_allocation.fixed b/tests/ui/redundant_allocation.fixed
deleted file mode 100644 (file)
index 6514fd6..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-// run-rustfix
-#![warn(clippy::all)]
-#![allow(clippy::boxed_local, clippy::needless_pass_by_value)]
-#![allow(clippy::blacklisted_name, unused_variables, dead_code)]
-
-use std::boxed::Box;
-use std::rc::Rc;
-
-pub struct MyStruct {}
-
-pub struct SubT<T> {
-    foo: T,
-}
-
-pub enum MyEnum {
-    One,
-    Two,
-}
-
-// Rc<&T>
-
-pub fn test1<T>(foo: &T) {}
-
-pub fn test2(foo: &MyStruct) {}
-
-pub fn test3(foo: &MyEnum) {}
-
-pub fn test4_neg(foo: Rc<SubT<&usize>>) {}
-
-// Rc<Rc<T>>
-
-pub fn test5(a: Rc<bool>) {}
-
-// Rc<Box<T>>
-
-pub fn test6(a: Rc<bool>) {}
-
-// Box<&T>
-
-pub fn test7<T>(foo: &T) {}
-
-pub fn test8(foo: &MyStruct) {}
-
-pub fn test9(foo: &MyEnum) {}
-
-pub fn test10_neg(foo: Box<SubT<&usize>>) {}
-
-fn main() {}
index 677b3e56d4dcebe5ea58a9996b5cac7c5332cddc..1b4f2a66c705ec088d5396bc3dbed187e12579df 100644 (file)
@@ -1,10 +1,7 @@
-// run-rustfix
 #![warn(clippy::all)]
 #![allow(clippy::boxed_local, clippy::needless_pass_by_value)]
 #![allow(clippy::blacklisted_name, unused_variables, dead_code)]
-
-use std::boxed::Box;
-use std::rc::Rc;
+#![allow(unused_imports)]
 
 pub struct MyStruct {}
 
@@ -17,32 +14,67 @@ pub enum MyEnum {
     Two,
 }
 
-// Rc<&T>
+mod outer_box {
+    use crate::MyEnum;
+    use crate::MyStruct;
+    use crate::SubT;
+    use std::boxed::Box;
+    use std::rc::Rc;
+    use std::sync::Arc;
 
-pub fn test1<T>(foo: Rc<&T>) {}
+    pub fn box_test6<T>(foo: Box<Rc<T>>) {}
 
-pub fn test2(foo: Rc<&MyStruct>) {}
+    pub fn box_test7<T>(foo: Box<Arc<T>>) {}
 
-pub fn test3(foo: Rc<&MyEnum>) {}
+    pub fn box_test8() -> Box<Rc<SubT<usize>>> {
+        unimplemented!();
+    }
 
-pub fn test4_neg(foo: Rc<SubT<&usize>>) {}
+    pub fn box_test9<T>(foo: Box<Arc<T>>) -> Box<Arc<SubT<T>>> {
+        unimplemented!();
+    }
+}
 
-// Rc<Rc<T>>
+mod outer_rc {
+    use crate::MyEnum;
+    use crate::MyStruct;
+    use crate::SubT;
+    use std::boxed::Box;
+    use std::rc::Rc;
+    use std::sync::Arc;
 
-pub fn test5(a: Rc<Rc<bool>>) {}
+    pub fn rc_test5(a: Rc<Box<bool>>) {}
 
-// Rc<Box<T>>
+    pub fn rc_test7(a: Rc<Arc<bool>>) {}
 
-pub fn test6(a: Rc<Box<bool>>) {}
+    pub fn rc_test8() -> Rc<Box<SubT<usize>>> {
+        unimplemented!();
+    }
 
-// Box<&T>
+    pub fn rc_test9<T>(foo: Rc<Arc<T>>) -> Rc<Arc<SubT<T>>> {
+        unimplemented!();
+    }
+}
 
-pub fn test7<T>(foo: Box<&T>) {}
+mod outer_arc {
+    use crate::MyEnum;
+    use crate::MyStruct;
+    use crate::SubT;
+    use std::boxed::Box;
+    use std::rc::Rc;
+    use std::sync::Arc;
 
-pub fn test8(foo: Box<&MyStruct>) {}
+    pub fn arc_test5(a: Arc<Box<bool>>) {}
 
-pub fn test9(foo: Box<&MyEnum>) {}
+    pub fn arc_test6(a: Arc<Rc<bool>>) {}
 
-pub fn test10_neg(foo: Box<SubT<&usize>>) {}
+    pub fn arc_test8() -> Arc<Box<SubT<usize>>> {
+        unimplemented!();
+    }
+
+    pub fn arc_test9<T>(foo: Arc<Rc<T>>) -> Arc<Rc<SubT<T>>> {
+        unimplemented!();
+    }
+}
 
 fn main() {}
index 92e4f67f5db6e40e6f2f0f9bd8ed422b3ec32c0c..fdab74eb538e3e7c88cc341707db01439c91395b 100644 (file)
-error: usage of `Rc<&T>`
-  --> $DIR/redundant_allocation.rs:22:22
+error: usage of `Box<Rc<T>>`
+  --> $DIR/redundant_allocation.rs:25:30
    |
-LL | pub fn test1<T>(foo: Rc<&T>) {}
-   |                      ^^^^^^ help: try: `&T`
+LL |     pub fn box_test6<T>(foo: Box<Rc<T>>) {}
+   |                              ^^^^^^^^^^
    |
    = note: `-D clippy::redundant-allocation` implied by `-D warnings`
+   = note: `Rc<T>` is already on the heap, `Box<Rc<T>>` makes an extra allocation
+   = help: consider using just `Box<T>` or `Rc<T>`
 
-error: usage of `Rc<&T>`
-  --> $DIR/redundant_allocation.rs:24:19
+error: usage of `Box<Arc<T>>`
+  --> $DIR/redundant_allocation.rs:27:30
    |
-LL | pub fn test2(foo: Rc<&MyStruct>) {}
-   |                   ^^^^^^^^^^^^^ help: try: `&MyStruct`
+LL |     pub fn box_test7<T>(foo: Box<Arc<T>>) {}
+   |                              ^^^^^^^^^^^
+   |
+   = note: `Arc<T>` is already on the heap, `Box<Arc<T>>` makes an extra allocation
+   = help: consider using just `Box<T>` or `Arc<T>`
+
+error: usage of `Box<Rc<SubT<usize>>>`
+  --> $DIR/redundant_allocation.rs:29:27
+   |
+LL |     pub fn box_test8() -> Box<Rc<SubT<usize>>> {
+   |                           ^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `Rc<SubT<usize>>` is already on the heap, `Box<Rc<SubT<usize>>>` makes an extra allocation
+   = help: consider using just `Box<SubT<usize>>` or `Rc<SubT<usize>>`
+
+error: usage of `Box<Arc<T>>`
+  --> $DIR/redundant_allocation.rs:33:30
+   |
+LL |     pub fn box_test9<T>(foo: Box<Arc<T>>) -> Box<Arc<SubT<T>>> {
+   |                              ^^^^^^^^^^^
+   |
+   = note: `Arc<T>` is already on the heap, `Box<Arc<T>>` makes an extra allocation
+   = help: consider using just `Box<T>` or `Arc<T>`
+
+error: usage of `Box<Arc<SubT<T>>>`
+  --> $DIR/redundant_allocation.rs:33:46
+   |
+LL |     pub fn box_test9<T>(foo: Box<Arc<T>>) -> Box<Arc<SubT<T>>> {
+   |                                              ^^^^^^^^^^^^^^^^^
+   |
+   = note: `Arc<SubT<T>>` is already on the heap, `Box<Arc<SubT<T>>>` makes an extra allocation
+   = help: consider using just `Box<SubT<T>>` or `Arc<SubT<T>>`
+
+error: usage of `Rc<Box<bool>>`
+  --> $DIR/redundant_allocation.rs:46:24
+   |
+LL |     pub fn rc_test5(a: Rc<Box<bool>>) {}
+   |                        ^^^^^^^^^^^^^
+   |
+   = note: `Box<bool>` is already on the heap, `Rc<Box<bool>>` makes an extra allocation
+   = help: consider using just `Rc<bool>` or `Box<bool>`
+
+error: usage of `Rc<Arc<bool>>`
+  --> $DIR/redundant_allocation.rs:48:24
+   |
+LL |     pub fn rc_test7(a: Rc<Arc<bool>>) {}
+   |                        ^^^^^^^^^^^^^
+   |
+   = note: `Arc<bool>` is already on the heap, `Rc<Arc<bool>>` makes an extra allocation
+   = help: consider using just `Rc<bool>` or `Arc<bool>`
+
+error: usage of `Rc<Box<SubT<usize>>>`
+  --> $DIR/redundant_allocation.rs:50:26
+   |
+LL |     pub fn rc_test8() -> Rc<Box<SubT<usize>>> {
+   |                          ^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `Box<SubT<usize>>` is already on the heap, `Rc<Box<SubT<usize>>>` makes an extra allocation
+   = help: consider using just `Rc<SubT<usize>>` or `Box<SubT<usize>>`
+
+error: usage of `Rc<Arc<T>>`
+  --> $DIR/redundant_allocation.rs:54:29
+   |
+LL |     pub fn rc_test9<T>(foo: Rc<Arc<T>>) -> Rc<Arc<SubT<T>>> {
+   |                             ^^^^^^^^^^
+   |
+   = note: `Arc<T>` is already on the heap, `Rc<Arc<T>>` makes an extra allocation
+   = help: consider using just `Rc<T>` or `Arc<T>`
 
-error: usage of `Rc<&T>`
-  --> $DIR/redundant_allocation.rs:26:19
+error: usage of `Rc<Arc<SubT<T>>>`
+  --> $DIR/redundant_allocation.rs:54:44
+   |
+LL |     pub fn rc_test9<T>(foo: Rc<Arc<T>>) -> Rc<Arc<SubT<T>>> {
+   |                                            ^^^^^^^^^^^^^^^^
    |
-LL | pub fn test3(foo: Rc<&MyEnum>) {}
-   |                   ^^^^^^^^^^^ help: try: `&MyEnum`
+   = note: `Arc<SubT<T>>` is already on the heap, `Rc<Arc<SubT<T>>>` makes an extra allocation
+   = help: consider using just `Rc<SubT<T>>` or `Arc<SubT<T>>`
 
-error: usage of `Rc<Rc<T>>`
-  --> $DIR/redundant_allocation.rs:32:17
+error: usage of `Arc<Box<bool>>`
+  --> $DIR/redundant_allocation.rs:67:25
    |
-LL | pub fn test5(a: Rc<Rc<bool>>) {}
-   |                 ^^^^^^^^^^^^ help: try: `Rc<bool>`
+LL |     pub fn arc_test5(a: Arc<Box<bool>>) {}
+   |                         ^^^^^^^^^^^^^^
+   |
+   = note: `Box<bool>` is already on the heap, `Arc<Box<bool>>` makes an extra allocation
+   = help: consider using just `Arc<bool>` or `Box<bool>`
 
-error: usage of `Rc<Box<T>>`
-  --> $DIR/redundant_allocation.rs:36:17
+error: usage of `Arc<Rc<bool>>`
+  --> $DIR/redundant_allocation.rs:69:25
+   |
+LL |     pub fn arc_test6(a: Arc<Rc<bool>>) {}
+   |                         ^^^^^^^^^^^^^
    |
-LL | pub fn test6(a: Rc<Box<bool>>) {}
-   |                 ^^^^^^^^^^^^^ help: try: `Rc<bool>`
+   = note: `Rc<bool>` is already on the heap, `Arc<Rc<bool>>` makes an extra allocation
+   = help: consider using just `Arc<bool>` or `Rc<bool>`
 
-error: usage of `Box<&T>`
-  --> $DIR/redundant_allocation.rs:40:22
+error: usage of `Arc<Box<SubT<usize>>>`
+  --> $DIR/redundant_allocation.rs:71:27
    |
-LL | pub fn test7<T>(foo: Box<&T>) {}
-   |                      ^^^^^^^ help: try: `&T`
+LL |     pub fn arc_test8() -> Arc<Box<SubT<usize>>> {
+   |                           ^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `Box<SubT<usize>>` is already on the heap, `Arc<Box<SubT<usize>>>` makes an extra allocation
+   = help: consider using just `Arc<SubT<usize>>` or `Box<SubT<usize>>`
 
-error: usage of `Box<&T>`
-  --> $DIR/redundant_allocation.rs:42:19
+error: usage of `Arc<Rc<T>>`
+  --> $DIR/redundant_allocation.rs:75:30
+   |
+LL |     pub fn arc_test9<T>(foo: Arc<Rc<T>>) -> Arc<Rc<SubT<T>>> {
+   |                              ^^^^^^^^^^
    |
-LL | pub fn test8(foo: Box<&MyStruct>) {}
-   |                   ^^^^^^^^^^^^^^ help: try: `&MyStruct`
+   = note: `Rc<T>` is already on the heap, `Arc<Rc<T>>` makes an extra allocation
+   = help: consider using just `Arc<T>` or `Rc<T>`
 
-error: usage of `Box<&T>`
-  --> $DIR/redundant_allocation.rs:44:19
+error: usage of `Arc<Rc<SubT<T>>>`
+  --> $DIR/redundant_allocation.rs:75:45
+   |
+LL |     pub fn arc_test9<T>(foo: Arc<Rc<T>>) -> Arc<Rc<SubT<T>>> {
+   |                                             ^^^^^^^^^^^^^^^^
    |
-LL | pub fn test9(foo: Box<&MyEnum>) {}
-   |                   ^^^^^^^^^^^^ help: try: `&MyEnum`
+   = note: `Rc<SubT<T>>` is already on the heap, `Arc<Rc<SubT<T>>>` makes an extra allocation
+   = help: consider using just `Arc<SubT<T>>` or `Rc<SubT<T>>`
 
-error: aborting due to 8 previous errors
+error: aborting due to 15 previous errors
 
diff --git a/tests/ui/redundant_allocation_fixable.fixed b/tests/ui/redundant_allocation_fixable.fixed
new file mode 100644 (file)
index 0000000..ef089b2
--- /dev/null
@@ -0,0 +1,75 @@
+// run-rustfix
+#![warn(clippy::all)]
+#![allow(clippy::boxed_local, clippy::needless_pass_by_value)]
+#![allow(clippy::blacklisted_name, unused_variables, dead_code)]
+#![allow(unused_imports)]
+
+pub struct MyStruct {}
+
+pub struct SubT<T> {
+    foo: T,
+}
+
+pub enum MyEnum {
+    One,
+    Two,
+}
+
+mod outer_box {
+    use crate::MyEnum;
+    use crate::MyStruct;
+    use crate::SubT;
+    use std::boxed::Box;
+    use std::rc::Rc;
+    use std::sync::Arc;
+
+    pub fn box_test1<T>(foo: &T) {}
+
+    pub fn box_test2(foo: &MyStruct) {}
+
+    pub fn box_test3(foo: &MyEnum) {}
+
+    pub fn box_test4_neg(foo: Box<SubT<&usize>>) {}
+
+    pub fn box_test5<T>(foo: Box<T>) {}
+}
+
+mod outer_rc {
+    use crate::MyEnum;
+    use crate::MyStruct;
+    use crate::SubT;
+    use std::boxed::Box;
+    use std::rc::Rc;
+    use std::sync::Arc;
+
+    pub fn rc_test1<T>(foo: &T) {}
+
+    pub fn rc_test2(foo: &MyStruct) {}
+
+    pub fn rc_test3(foo: &MyEnum) {}
+
+    pub fn rc_test4_neg(foo: Rc<SubT<&usize>>) {}
+
+    pub fn rc_test6(a: Rc<bool>) {}
+}
+
+mod outer_arc {
+    use crate::MyEnum;
+    use crate::MyStruct;
+    use crate::SubT;
+    use std::boxed::Box;
+    use std::rc::Rc;
+    use std::sync::Arc;
+
+    pub fn arc_test1<T>(foo: &T) {}
+
+    pub fn arc_test2(foo: &MyStruct) {}
+
+    pub fn arc_test3(foo: &MyEnum) {}
+
+    pub fn arc_test4_neg(foo: Arc<SubT<&usize>>) {}
+
+    pub fn arc_test7(a: Arc<bool>) {}
+}
+
+fn main() {}
diff --git a/tests/ui/redundant_allocation_fixable.rs b/tests/ui/redundant_allocation_fixable.rs
new file mode 100644 (file)
index 0000000..fefa877
--- /dev/null
@@ -0,0 +1,75 @@
+// run-rustfix
+#![warn(clippy::all)]
+#![allow(clippy::boxed_local, clippy::needless_pass_by_value)]
+#![allow(clippy::blacklisted_name, unused_variables, dead_code)]
+#![allow(unused_imports)]
+
+pub struct MyStruct {}
+
+pub struct SubT<T> {
+    foo: T,
+}
+
+pub enum MyEnum {
+    One,
+    Two,
+}
+
+mod outer_box {
+    use crate::MyEnum;
+    use crate::MyStruct;
+    use crate::SubT;
+    use std::boxed::Box;
+    use std::rc::Rc;
+    use std::sync::Arc;
+
+    pub fn box_test1<T>(foo: Box<&T>) {}
+
+    pub fn box_test2(foo: Box<&MyStruct>) {}
+
+    pub fn box_test3(foo: Box<&MyEnum>) {}
+
+    pub fn box_test4_neg(foo: Box<SubT<&usize>>) {}
+
+    pub fn box_test5<T>(foo: Box<Box<T>>) {}
+}
+
+mod outer_rc {
+    use crate::MyEnum;
+    use crate::MyStruct;
+    use crate::SubT;
+    use std::boxed::Box;
+    use std::rc::Rc;
+    use std::sync::Arc;
+
+    pub fn rc_test1<T>(foo: Rc<&T>) {}
+
+    pub fn rc_test2(foo: Rc<&MyStruct>) {}
+
+    pub fn rc_test3(foo: Rc<&MyEnum>) {}
+
+    pub fn rc_test4_neg(foo: Rc<SubT<&usize>>) {}
+
+    pub fn rc_test6(a: Rc<Rc<bool>>) {}
+}
+
+mod outer_arc {
+    use crate::MyEnum;
+    use crate::MyStruct;
+    use crate::SubT;
+    use std::boxed::Box;
+    use std::rc::Rc;
+    use std::sync::Arc;
+
+    pub fn arc_test1<T>(foo: Arc<&T>) {}
+
+    pub fn arc_test2(foo: Arc<&MyStruct>) {}
+
+    pub fn arc_test3(foo: Arc<&MyEnum>) {}
+
+    pub fn arc_test4_neg(foo: Arc<SubT<&usize>>) {}
+
+    pub fn arc_test7(a: Arc<Arc<bool>>) {}
+}
+
+fn main() {}
diff --git a/tests/ui/redundant_allocation_fixable.stderr b/tests/ui/redundant_allocation_fixable.stderr
new file mode 100644 (file)
index 0000000..fdd76ef
--- /dev/null
@@ -0,0 +1,99 @@
+error: usage of `Box<&T>`
+  --> $DIR/redundant_allocation_fixable.rs:26:30
+   |
+LL |     pub fn box_test1<T>(foo: Box<&T>) {}
+   |                              ^^^^^^^ help: try: `&T`
+   |
+   = note: `-D clippy::redundant-allocation` implied by `-D warnings`
+   = note: `&T` is already a pointer, `Box<&T>` allocates a pointer on the heap
+
+error: usage of `Box<&MyStruct>`
+  --> $DIR/redundant_allocation_fixable.rs:28:27
+   |
+LL |     pub fn box_test2(foo: Box<&MyStruct>) {}
+   |                           ^^^^^^^^^^^^^^ help: try: `&MyStruct`
+   |
+   = note: `&MyStruct` is already a pointer, `Box<&MyStruct>` allocates a pointer on the heap
+
+error: usage of `Box<&MyEnum>`
+  --> $DIR/redundant_allocation_fixable.rs:30:27
+   |
+LL |     pub fn box_test3(foo: Box<&MyEnum>) {}
+   |                           ^^^^^^^^^^^^ help: try: `&MyEnum`
+   |
+   = note: `&MyEnum` is already a pointer, `Box<&MyEnum>` allocates a pointer on the heap
+
+error: usage of `Box<Box<T>>`
+  --> $DIR/redundant_allocation_fixable.rs:34:30
+   |
+LL |     pub fn box_test5<T>(foo: Box<Box<T>>) {}
+   |                              ^^^^^^^^^^^ help: try: `Box<T>`
+   |
+   = note: `Box<T>` is already on the heap, `Box<Box<T>>` makes an extra allocation
+
+error: usage of `Rc<&T>`
+  --> $DIR/redundant_allocation_fixable.rs:45:29
+   |
+LL |     pub fn rc_test1<T>(foo: Rc<&T>) {}
+   |                             ^^^^^^ help: try: `&T`
+   |
+   = note: `&T` is already a pointer, `Rc<&T>` allocates a pointer on the heap
+
+error: usage of `Rc<&MyStruct>`
+  --> $DIR/redundant_allocation_fixable.rs:47:26
+   |
+LL |     pub fn rc_test2(foo: Rc<&MyStruct>) {}
+   |                          ^^^^^^^^^^^^^ help: try: `&MyStruct`
+   |
+   = note: `&MyStruct` is already a pointer, `Rc<&MyStruct>` allocates a pointer on the heap
+
+error: usage of `Rc<&MyEnum>`
+  --> $DIR/redundant_allocation_fixable.rs:49:26
+   |
+LL |     pub fn rc_test3(foo: Rc<&MyEnum>) {}
+   |                          ^^^^^^^^^^^ help: try: `&MyEnum`
+   |
+   = note: `&MyEnum` is already a pointer, `Rc<&MyEnum>` allocates a pointer on the heap
+
+error: usage of `Rc<Rc<bool>>`
+  --> $DIR/redundant_allocation_fixable.rs:53:24
+   |
+LL |     pub fn rc_test6(a: Rc<Rc<bool>>) {}
+   |                        ^^^^^^^^^^^^ help: try: `Rc<bool>`
+   |
+   = note: `Rc<bool>` is already on the heap, `Rc<Rc<bool>>` makes an extra allocation
+
+error: usage of `Arc<&T>`
+  --> $DIR/redundant_allocation_fixable.rs:64:30
+   |
+LL |     pub fn arc_test1<T>(foo: Arc<&T>) {}
+   |                              ^^^^^^^ help: try: `&T`
+   |
+   = note: `&T` is already a pointer, `Arc<&T>` allocates a pointer on the heap
+
+error: usage of `Arc<&MyStruct>`
+  --> $DIR/redundant_allocation_fixable.rs:66:27
+   |
+LL |     pub fn arc_test2(foo: Arc<&MyStruct>) {}
+   |                           ^^^^^^^^^^^^^^ help: try: `&MyStruct`
+   |
+   = note: `&MyStruct` is already a pointer, `Arc<&MyStruct>` allocates a pointer on the heap
+
+error: usage of `Arc<&MyEnum>`
+  --> $DIR/redundant_allocation_fixable.rs:68:27
+   |
+LL |     pub fn arc_test3(foo: Arc<&MyEnum>) {}
+   |                           ^^^^^^^^^^^^ help: try: `&MyEnum`
+   |
+   = note: `&MyEnum` is already a pointer, `Arc<&MyEnum>` allocates a pointer on the heap
+
+error: usage of `Arc<Arc<bool>>`
+  --> $DIR/redundant_allocation_fixable.rs:72:25
+   |
+LL |     pub fn arc_test7(a: Arc<Arc<bool>>) {}
+   |                         ^^^^^^^^^^^^^^ help: try: `Arc<bool>`
+   |
+   = note: `Arc<bool>` is already on the heap, `Arc<Arc<bool>>` makes an extra allocation
+
+error: aborting due to 12 previous errors
+
index f5da703cd1dea5e9d524f8c2950ec629525faec2..2d711082746e73aab40867f0bcc2285137e5aca2 100644 (file)
@@ -55,6 +55,8 @@ fn main() {
     issue_5405();
     manually_drop();
     clone_then_move_cloned();
+    hashmap_neg();
+    false_negative_5707();
 }
 
 #[derive(Clone)]
@@ -206,3 +208,29 @@ fn clone_then_move_cloned() {
     let mut x = S(String::new());
     x.0.clone().chars().for_each(|_| x.m());
 }
+
+fn hashmap_neg() {
+    // issue 5707
+    use std::collections::HashMap;
+    use std::path::PathBuf;
+
+    let p = PathBuf::from("/");
+
+    let mut h: HashMap<&str, &str> = HashMap::new();
+    h.insert("orig-p", p.to_str().unwrap());
+
+    let mut q = p.clone();
+    q.push("foo");
+
+    println!("{:?} {}", h, q.display());
+}
+
+fn false_negative_5707() {
+    fn foo(_x: &Alpha, _y: &mut Alpha) {}
+
+    let x = Alpha;
+    let mut y = Alpha;
+    foo(&x, &mut y);
+    let _z = x.clone(); // pr 7346 can't lint on `x`
+    drop(y);
+}
index fd7f31a1cc5b6b944a635ff0282477600f7268b7..bd3d7365229fb83ccd13ca555e53d9941f867c43 100644 (file)
@@ -55,6 +55,8 @@ fn main() {
     issue_5405();
     manually_drop();
     clone_then_move_cloned();
+    hashmap_neg();
+    false_negative_5707();
 }
 
 #[derive(Clone)]
@@ -206,3 +208,29 @@ fn m(&mut self) {}
     let mut x = S(String::new());
     x.0.clone().chars().for_each(|_| x.m());
 }
+
+fn hashmap_neg() {
+    // issue 5707
+    use std::collections::HashMap;
+    use std::path::PathBuf;
+
+    let p = PathBuf::from("/");
+
+    let mut h: HashMap<&str, &str> = HashMap::new();
+    h.insert("orig-p", p.to_str().unwrap());
+
+    let mut q = p.clone();
+    q.push("foo");
+
+    println!("{:?} {}", h, q.display());
+}
+
+fn false_negative_5707() {
+    fn foo(_x: &Alpha, _y: &mut Alpha) {}
+
+    let x = Alpha;
+    let mut y = Alpha;
+    foo(&x, &mut y);
+    let _z = x.clone(); // pr 7346 can't lint on `x`
+    drop(y);
+}
index 529a6de91e266baa93294822e2337b1401e035b2..fbc90493ae94b85986e8381ef82e395fbc202e9e 100644 (file)
@@ -108,61 +108,61 @@ LL |     let _t = tup.0.clone();
    |              ^^^^^
 
 error: redundant clone
-  --> $DIR/redundant_clone.rs:63:25
+  --> $DIR/redundant_clone.rs:65:25
    |
 LL |     if b { (a.clone(), a.clone()) } else { (Alpha, a) }
    |                         ^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/redundant_clone.rs:63:24
+  --> $DIR/redundant_clone.rs:65:24
    |
 LL |     if b { (a.clone(), a.clone()) } else { (Alpha, a) }
    |                        ^
 
 error: redundant clone
-  --> $DIR/redundant_clone.rs:120:15
+  --> $DIR/redundant_clone.rs:122:15
    |
 LL |     let _s = s.clone();
    |               ^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/redundant_clone.rs:120:14
+  --> $DIR/redundant_clone.rs:122:14
    |
 LL |     let _s = s.clone();
    |              ^
 
 error: redundant clone
-  --> $DIR/redundant_clone.rs:121:15
+  --> $DIR/redundant_clone.rs:123:15
    |
 LL |     let _t = t.clone();
    |               ^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/redundant_clone.rs:121:14
+  --> $DIR/redundant_clone.rs:123:14
    |
 LL |     let _t = t.clone();
    |              ^
 
 error: redundant clone
-  --> $DIR/redundant_clone.rs:131:19
+  --> $DIR/redundant_clone.rs:133:19
    |
 LL |         let _f = f.clone();
    |                   ^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/redundant_clone.rs:131:18
+  --> $DIR/redundant_clone.rs:133:18
    |
 LL |         let _f = f.clone();
    |                  ^
 
 error: redundant clone
-  --> $DIR/redundant_clone.rs:143:14
+  --> $DIR/redundant_clone.rs:145:14
    |
 LL |     let y = x.clone().join("matthias");
    |              ^^^^^^^^ help: remove this
    |
 note: cloned value is neither consumed nor mutated
-  --> $DIR/redundant_clone.rs:143:13
+  --> $DIR/redundant_clone.rs:145:13
    |
 LL |     let y = x.clone().join("matthias");
    |             ^^^^^^^^^