From 2ecc2ac864739cff6aed2609021e2467dedb117a Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Fri, 21 Aug 2020 00:07:56 +0200 Subject: [PATCH 01/49] unit-arg - improve suggestion --- clippy_lints/src/types.rs | 88 +++++++++++--------- tests/ui/unit_arg.rs | 7 +- tests/ui/unit_arg.stderr | 111 ++++++++++++-------------- tests/ui/unit_arg_empty_blocks.stderr | 21 ++--- 4 files changed, 115 insertions(+), 112 deletions(-) diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 7e9190bef5e..3f5b3a5bcd5 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -11,8 +11,8 @@ use rustc_hir as hir; use rustc_hir::intravisit::{walk_body, walk_expr, walk_ty, FnKind, NestedVisitorMap, Visitor}; use rustc_hir::{ BinOpKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericParamKind, HirId, ImplItem, - ImplItemKind, Item, ItemKind, Lifetime, Local, MatchSource, MutTy, Mutability, QPath, Stmt, StmtKind, TraitFn, - TraitItem, TraitItemKind, TyKind, UnOp, + ImplItemKind, Item, ItemKind, Lifetime, Local, MatchSource, MutTy, Mutability, Node, QPath, Stmt, StmtKind, + TraitFn, TraitItem, TraitItemKind, TyKind, UnOp, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; @@ -29,10 +29,10 @@ use rustc_typeck::hir_ty_to_ty; use crate::consts::{constant, Constant}; use crate::utils::paths; use crate::utils::{ - clip, comparisons, differing_macro_contexts, higher, in_constant, indent_of, int_bits, is_type_diagnostic_item, + clip, comparisons, differing_macro_contexts, higher, in_constant, int_bits, is_type_diagnostic_item, last_path_segment, match_def_path, match_path, method_chain_args, multispan_sugg, numeric_literal::NumericLiteral, - qpath_res, sext, snippet, snippet_block_with_applicability, snippet_opt, snippet_with_applicability, - snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, unsext, + qpath_res, sext, snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite, span_lint, + span_lint_and_help, span_lint_and_sugg, span_lint_and_then, unsext, }; declare_clippy_lint! { @@ -844,43 +844,54 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp Applicability::MaybeIncorrect, ); or = "or "; + applicability = Applicability::MaybeIncorrect; }); - let sugg = args_to_recover + + let arg_snippets: Vec = args_to_recover + .iter() + .filter_map(|arg| snippet_opt(cx, arg.span)) + .collect(); + let arg_snippets_without_empty_blocks: Vec = args_to_recover .iter() .filter(|arg| !is_empty_block(arg)) - .enumerate() - .map(|(i, arg)| { - let indent = if i == 0 { - 0 - } else { - indent_of(cx, expr.span).unwrap_or(0) - }; - format!( - "{}{};", - " ".repeat(indent), - snippet_block_with_applicability(cx, arg.span, "..", Some(expr.span), &mut applicability) - ) - }) - .collect::>(); - let mut and = ""; - if !sugg.is_empty() { - let plural = if sugg.len() > 1 { "s" } else { "" }; - db.span_suggestion( - expr.span.with_hi(expr.span.lo()), - &format!("{}move the expression{} in front of the call...", or, plural), - format!("{}\n", sugg.join("\n")), - applicability, - ); - and = "...and " + .filter_map(|arg| snippet_opt(cx, arg.span)) + .collect(); + + if let Some(mut sugg) = snippet_opt(cx, expr.span) { + arg_snippets.iter().for_each(|arg| { + sugg = sugg.replacen(arg, "()", 1); + }); + sugg = format!("{}{}{}", arg_snippets_without_empty_blocks.join("; "), "; ", sugg); + let parent_node = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(expr.hir_id)); + if !matches!(parent_node, Some(Node::Block(_))) && !matches!(parent_node, Some(Node::Stmt(_))) { + // expr is not in a block statement or result expression position, wrap in a block + sugg = format!("{{ {} }}", sugg); + } + + if arg_snippets_without_empty_blocks.is_empty() { + db.multipart_suggestion( + &format!("use {}unit literal{} instead", singular, plural), + args_to_recover + .iter() + .map(|arg| (arg.span, "()".to_string())) + .collect::>(), + applicability, + ); + } else { + let plural = arg_snippets_without_empty_blocks.len() > 1; + let empty_or_s = if plural { "s" } else { "" }; + let it_or_them = if plural { "them" } else { "it" }; + db.span_suggestion( + expr.span, + &format!( + "{}move the expression{} in front of the call and replace {} with the unit literal `()`", + or, empty_or_s, it_or_them + ), + sugg, + applicability, + ); + } } - db.multipart_suggestion( - &format!("{}use {}unit literal{} instead", and, singular, plural), - args_to_recover - .iter() - .map(|arg| (arg.span, "()".to_string())) - .collect::>(), - applicability, - ); }, ); } @@ -2055,6 +2066,7 @@ impl PartialOrd for FullInt { }) } } + impl Ord for FullInt { #[must_use] fn cmp(&self, other: &Self) -> Ordering { diff --git a/tests/ui/unit_arg.rs b/tests/ui/unit_arg.rs index 2992abae775..2e2bd054e42 100644 --- a/tests/ui/unit_arg.rs +++ b/tests/ui/unit_arg.rs @@ -1,5 +1,5 @@ #![warn(clippy::unit_arg)] -#![allow(clippy::no_effect, unused_must_use, unused_variables)] +#![allow(clippy::no_effect, unused_must_use, unused_variables, clippy::unused_unit)] use std::fmt::Debug; @@ -47,6 +47,11 @@ fn bad() { foo(3); }, ); + // here Some(foo(2)) isn't the top level statement expression, wrap the suggestion in a block + None.or(Some(foo(2))); + // in this case, the suggestion can be inlined, no need for a surrounding block + // foo(()); foo(()) instead of { foo(()); foo(()) } + foo(foo(())) } fn ok() { diff --git a/tests/ui/unit_arg.stderr b/tests/ui/unit_arg.stderr index 56f6a855dfa..2a0cc1f18e2 100644 --- a/tests/ui/unit_arg.stderr +++ b/tests/ui/unit_arg.stderr @@ -11,16 +11,12 @@ help: remove the semicolon from the last statement in the block | LL | 1 | -help: or move the expression in front of the call... +help: or move the expression in front of the call and replace it with the unit literal `()` | LL | { LL | 1; -LL | }; +LL | }; foo(()); | -help: ...and use a unit literal instead - | -LL | foo(()); - | ^^ error: passing a unit value to a function --> $DIR/unit_arg.rs:26:5 @@ -28,14 +24,10 @@ error: passing a unit value to a function LL | foo(foo(1)); | ^^^^^^^^^^^ | -help: move the expression in front of the call... +help: move the expression in front of the call and replace it with the unit literal `()` | -LL | foo(1); - | -help: ...and use a unit literal instead - | -LL | foo(()); - | ^^ +LL | foo(1); foo(()); + | ^^^^^^^^^^^^^^^ error: passing a unit value to a function --> $DIR/unit_arg.rs:27:5 @@ -50,17 +42,13 @@ help: remove the semicolon from the last statement in the block | LL | foo(2) | -help: or move the expression in front of the call... +help: or move the expression in front of the call and replace it with the unit literal `()` | LL | { LL | foo(1); LL | foo(2); -LL | }; +LL | }; foo(()); | -help: ...and use a unit literal instead - | -LL | foo(()); - | ^^ error: passing a unit value to a function --> $DIR/unit_arg.rs:32:5 @@ -74,16 +62,12 @@ help: remove the semicolon from the last statement in the block | LL | 1 | -help: or move the expression in front of the call... +help: or move the expression in front of the call and replace it with the unit literal `()` | LL | { LL | 1; -LL | }; +LL | }; b.bar(()); | -help: ...and use a unit literal instead - | -LL | b.bar(()); - | ^^ error: passing unit values to a function --> $DIR/unit_arg.rs:35:5 @@ -91,15 +75,10 @@ error: passing unit values to a function LL | taking_multiple_units(foo(0), foo(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: move the expressions in front of the call... +help: move the expressions in front of the call and replace them with the unit literal `()` | -LL | foo(0); -LL | foo(1); - | -help: ...and use unit literals instead - | -LL | taking_multiple_units((), ()); - | ^^ ^^ +LL | foo(0); foo(1); taking_multiple_units((), ()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: passing unit values to a function --> $DIR/unit_arg.rs:36:5 @@ -114,18 +93,13 @@ help: remove the semicolon from the last statement in the block | LL | foo(2) | -help: or move the expressions in front of the call... +help: or move the expressions in front of the call and replace them with the unit literal `()` | -LL | foo(0); -LL | { +LL | foo(0); { LL | foo(1); LL | foo(2); -LL | }; +LL | }; taking_multiple_units((), ()); | -help: ...and use unit literals instead - | -LL | taking_multiple_units((), ()); - | ^^ ^^ error: passing unit values to a function --> $DIR/unit_arg.rs:40:5 @@ -147,35 +121,56 @@ help: remove the semicolon from the last statement in the block | LL | foo(3) | -help: or move the expressions in front of the call... +help: or move the expressions in front of the call and replace them with the unit literal `()` | LL | { -LL | foo(0); -LL | foo(1); -LL | }; -LL | { -LL | foo(2); +LL | foo(0); +LL | foo(1); +LL | }; { +LL | foo(2); +LL | foo(3); ... -help: ...and use unit literals instead + +error: use of `or` followed by a function call + --> $DIR/unit_arg.rs:51:10 | -LL | (), -LL | (), +LL | None.or(Some(foo(2))); + | ^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some(foo(2)))` + | + = note: `-D clippy::or-fun-call` implied by `-D warnings` + +error: passing a unit value to a function + --> $DIR/unit_arg.rs:51:13 + | +LL | None.or(Some(foo(2))); + | ^^^^^^^^^^^^ + | +help: move the expression in front of the call and replace it with the unit literal `()` + | +LL | None.or({ foo(2); Some(()) }); + | ^^^^^^^^^^^^^^^^^^^^ + +error: passing a unit value to a function + --> $DIR/unit_arg.rs:54:5 + | +LL | foo(foo(())) + | ^^^^^^^^^^^^ + | +help: move the expression in front of the call and replace it with the unit literal `()` + | +LL | foo(()); foo(()) | error: passing a unit value to a function - --> $DIR/unit_arg.rs:82:5 + --> $DIR/unit_arg.rs:87:5 | LL | Some(foo(1)) | ^^^^^^^^^^^^ | -help: move the expression in front of the call... +help: move the expression in front of the call and replace it with the unit literal `()` | -LL | foo(1); +LL | foo(1); Some(()) | -help: ...and use a unit literal instead - | -LL | Some(()) - | ^^ -error: aborting due to 8 previous errors +error: aborting due to 11 previous errors diff --git a/tests/ui/unit_arg_empty_blocks.stderr b/tests/ui/unit_arg_empty_blocks.stderr index bb58483584b..4cbbc8b8cd4 100644 --- a/tests/ui/unit_arg_empty_blocks.stderr +++ b/tests/ui/unit_arg_empty_blocks.stderr @@ -22,14 +22,10 @@ error: passing unit values to a function LL | taking_two_units({}, foo(0)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: move the expression in front of the call... +help: move the expression in front of the call and replace it with the unit literal `()` | -LL | foo(0); - | -help: ...and use unit literals instead - | -LL | taking_two_units((), ()); - | ^^ ^^ +LL | foo(0); taking_two_units((), ()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: passing unit values to a function --> $DIR/unit_arg_empty_blocks.rs:18:5 @@ -37,15 +33,10 @@ error: passing unit values to a function LL | taking_three_units({}, foo(0), foo(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: move the expressions in front of the call... +help: move the expressions in front of the call and replace them with the unit literal `()` | -LL | foo(0); -LL | foo(1); - | -help: ...and use unit literals instead - | -LL | taking_three_units((), (), ()); - | ^^ ^^ ^^ +LL | foo(0); foo(1); taking_three_units((), (), ()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 4 previous errors From baf62e7a38854ff6a0039ddccb124ff329a32143 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Thu, 27 Aug 2020 14:39:09 +0200 Subject: [PATCH 02/49] Update changelog to beta 1.47 --- CHANGELOG.md | 108 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 105 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 137b561028a..34d48821023 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,113 @@ document. ## Unreleased / In Rust Nightly -[c2c07fa...master](https://github.com/rust-lang/rust-clippy/compare/c2c07fa...master) +[09bd400...master](https://github.com/rust-lang/rust-clippy/compare/09bd400...master) + +## Rust 1.47 + +Current beta, release 2020-10-08 + +[c2c07fa...09bd400](https://github.com/rust-lang/rust-clippy/compare/c2c07fa...09bd400) + +### New lints + +* [`derive_ord_xor_partial_ord`] [#5848](https://github.com/rust-lang/rust-clippy/pull/5848) +* [`trait_duplication_in_bounds`] [#5852](https://github.com/rust-lang/rust-clippy/pull/5852) +* [`map_identity`] [#5694](https://github.com/rust-lang/rust-clippy/pull/5694) +* [`unit_return_expecting_ord`] [#5737](https://github.com/rust-lang/rust-clippy/pull/5737) +* [`pattern_type_mismatch`] [#4841](https://github.com/rust-lang/rust-clippy/pull/4841) +* [`repeat_once`] [#5773](https://github.com/rust-lang/rust-clippy/pull/5773) +* [`same_item_push`] [#5825](https://github.com/rust-lang/rust-clippy/pull/5825) +* [`needless_arbitrary_self_type`] [#5869](https://github.com/rust-lang/rust-clippy/pull/5869) +* [`match_like_matches_macro`] [#5769](https://github.com/rust-lang/rust-clippy/pull/5769) +* [`stable_sort_primitive`] [#5809](https://github.com/rust-lang/rust-clippy/pull/5809) +* [`blanket_clippy_restriction_lints`] [#5750](https://github.com/rust-lang/rust-clippy/pull/5750) +* [`option_if_let_else`] [#5301](https://github.com/rust-lang/rust-clippy/pull/5301) + +### Moves and Deprecations + +* Deprecate [`regex_macro`] lint + [#5760](https://github.com/rust-lang/rust-clippy/pull/5760) +* Move [`range_minus_one`] to `pedantic` + [#5752](https://github.com/rust-lang/rust-clippy/pull/5752) + +### Enhancements + +* Improve [`needless_collect`] by catching `collect` calls followed by `iter` or `into_iter` calls + [#5837](https://github.com/rust-lang/rust-clippy/pull/5837) +* [`panic`], [`todo`], [`unimplemented`] and [`unreachable`] now detect calls with formatting + [#5811](https://github.com/rust-lang/rust-clippy/pull/5811) +* Detect more cases of [`suboptimal_flops`] and [`imprecise_flops`] + [#5443](https://github.com/rust-lang/rust-clippy/pull/5443) +* Handle asymmetrical implementations of `PartialEq` in [`cmp_owned`] + [#5701](https://github.com/rust-lang/rust-clippy/pull/5701) +* Make it possible to allow [`unsafe_derive_deserialize`] + [#5870](https://github.com/rust-lang/rust-clippy/pull/5870) +* Catch `ord.min(a).max(b)` where a < b in [`min_max`] + [#5871](https://github.com/rust-lang/rust-clippy/pull/5871) +* Make [`clone_on_copy`] suggestion machine applicable + [#5745](https://github.com/rust-lang/rust-clippy/pull/5745) +* Enable [`len_zero`] on ranges now that `is_empty` is stable on them + [#5961](https://github.com/rust-lang/rust-clippy/pull/5961) + +### False Positive Fixes + +* Avoid triggering [`or_fun_call`] with const fns that take no arguments + [#5889](https://github.com/rust-lang/rust-clippy/pull/5889) +* Fix [`redundant_closure_call`] false positive for closures that have multiple calls + [#5800](https://github.com/rust-lang/rust-clippy/pull/5800) +* Don't lint cases involving `ManuallyDrop` in [`redundant_clone`] + [#5824](https://github.com/rust-lang/rust-clippy/pull/5824) +* Treat a single expression the same as a single statement in the 2nd arm of a match in [`single_match_else`] + [#5771](https://github.com/rust-lang/rust-clippy/pull/5771) +* Don't trigger [`unnested_or_patterns`] if the feature `or_patterns` is not enabled + [#5758](https://github.com/rust-lang/rust-clippy/pull/5758) +* Avoid linting if key borrows in [`unnecessary_sort_by`] + [#5756](https://github.com/rust-lang/rust-clippy/pull/5756) +* Consider `Try` impl for `Poll` when generating suggestions in [`try_err`] + [#5857](https://github.com/rust-lang/rust-clippy/pull/5857) +* Take input lifetimes into account in `manual_async_fn` + [#5859](https://github.com/rust-lang/rust-clippy/pull/5859) +* Fix multiple false positives in [`type_repetition_in_bounds`] and add a configuration option + [#5761](https://github.com/rust-lang/rust-clippy/pull/5761) +* Limit the [`suspicious_arithmetic_impl`] lint to one binary operation + [#5820](https://github.com/rust-lang/rust-clippy/pull/5820) + +### Suggestion Fixes/Improvements + +* Improve readability of [`shadow_unrelated`] suggestion by truncating the RHS snippet + [#5788](https://github.com/rust-lang/rust-clippy/pull/5788) +* Suggest `filter_map` instead of `flat_map` when mapping to `Option` in [`map_flatten`] + [#5846](https://github.com/rust-lang/rust-clippy/pull/5846) +* Ensure suggestion is shown correctly for long method call chains in [`iter_nth_zero`] + [#5793](https://github.com/rust-lang/rust-clippy/pull/5793) +* Drop borrow operator in suggestions of [`redundant_pattern_matching`] + [#5815](https://github.com/rust-lang/rust-clippy/pull/5815) +* Add suggestion for [`iter_skip_next`] + [#5843](https://github.com/rust-lang/rust-clippy/pull/5843) +* Improve [`collapsible_if`] fix suggestion + [#5732](https://github.com/rust-lang/rust-clippy/pull/5732) + +### ICE Fixes + +* Fix ICE caused by [`needless_collect`] + [#5877](https://github.com/rust-lang/rust-clippy/pull/5877) +* Fix ICE caused by [`unnested_or_patterns`] + [#5784](https://github.com/rust-lang/rust-clippy/pull/5784) + +### Documentation Improvements + +* Fix grammar of [`await_holding_lock`] documentation + [#5748](https://github.com/rust-lang/rust-clippy/pull/5748) + +### Others + +* Make lints adhere to the rustc dev guide + [#5888](https://github.com/rust-lang/rust-clippy/pull/5888) ## Rust 1.46 -Current beta, release 2020-08-27 +Current stable, released 2020-08-27 [7ea7cd1...c2c07fa](https://github.com/rust-lang/rust-clippy/compare/7ea7cd1...c2c07fa) @@ -72,7 +174,7 @@ Current beta, release 2020-08-27 ## Rust 1.45 -Current stable, released 2020-07-16 +Released 2020-07-16 [891e1a8...7ea7cd1](https://github.com/rust-lang/rust-clippy/compare/891e1a8...7ea7cd1) From f3ccbef2af24d5d83f82f1fb50bd97a9b75e609f Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Thu, 27 Aug 2020 01:40:02 +0200 Subject: [PATCH 03/49] unit-arg - pr comments --- clippy_lints/src/types.rs | 41 ++++++++++---- clippy_lints/src/utils/mod.rs | 2 +- tests/ui/unit_arg.rs | 8 ++- tests/ui/unit_arg.stderr | 79 ++++++++++++++------------- tests/ui/unit_arg_empty_blocks.stderr | 11 ++-- 5 files changed, 88 insertions(+), 53 deletions(-) diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 3f5b3a5bcd5..16e48d91916 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -29,10 +29,10 @@ use rustc_typeck::hir_ty_to_ty; use crate::consts::{constant, Constant}; use crate::utils::paths; use crate::utils::{ - clip, comparisons, differing_macro_contexts, higher, in_constant, int_bits, is_type_diagnostic_item, + clip, comparisons, differing_macro_contexts, higher, in_constant, indent_of, int_bits, is_type_diagnostic_item, last_path_segment, match_def_path, match_path, method_chain_args, multispan_sugg, numeric_literal::NumericLiteral, qpath_res, sext, snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite, span_lint, - span_lint_and_help, span_lint_and_sugg, span_lint_and_then, unsext, + span_lint_and_help, span_lint_and_sugg, span_lint_and_then, trim_multiline, unsext, }; declare_clippy_lint! { @@ -802,6 +802,7 @@ impl<'tcx> LateLintPass<'tcx> for UnitArg { } } +#[allow(clippy::too_many_lines)] fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Expr<'_>]) { let mut applicability = Applicability::MachineApplicable; let (singular, plural) = if args_to_recover.len() > 1 { @@ -856,18 +857,38 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp .filter(|arg| !is_empty_block(arg)) .filter_map(|arg| snippet_opt(cx, arg.span)) .collect(); + let indent = indent_of(cx, expr.span).unwrap_or(0); - if let Some(mut sugg) = snippet_opt(cx, expr.span) { - arg_snippets.iter().for_each(|arg| { - sugg = sugg.replacen(arg, "()", 1); - }); - sugg = format!("{}{}{}", arg_snippets_without_empty_blocks.join("; "), "; ", sugg); + if let Some(expr_str) = snippet_opt(cx, expr.span) { + let expr_with_replacements = arg_snippets + .iter() + .fold(expr_str, |acc, arg| acc.replacen(arg, "()", 1)); + + // expr is not in a block statement or result expression position, wrap in a block let parent_node = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(expr.hir_id)); - if !matches!(parent_node, Some(Node::Block(_))) && !matches!(parent_node, Some(Node::Stmt(_))) { - // expr is not in a block statement or result expression position, wrap in a block - sugg = format!("{{ {} }}", sugg); + let wrap_in_block = + !matches!(parent_node, Some(Node::Block(_))) && !matches!(parent_node, Some(Node::Stmt(_))); + + let stmts_indent = if wrap_in_block { indent + 4 } else { indent }; + let mut stmts_and_call = arg_snippets_without_empty_blocks.clone(); + stmts_and_call.push(expr_with_replacements); + let mut stmts_and_call_str = stmts_and_call + .into_iter() + .enumerate() + .map(|(i, v)| { + let with_indent_prefix = if i > 0 { " ".repeat(stmts_indent) + &v } else { v }; + trim_multiline(with_indent_prefix.into(), true, Some(stmts_indent)).into_owned() + }) + .collect::>() + .join(";\n"); + + if wrap_in_block { + stmts_and_call_str = " ".repeat(stmts_indent) + &stmts_and_call_str; + stmts_and_call_str = format!("{{\n{}\n{}}}", &stmts_and_call_str, " ".repeat(indent)); } + let sugg = stmts_and_call_str; + if arg_snippets_without_empty_blocks.is_empty() { db.multipart_suggestion( &format!("use {}unit literal{} instead", singular, plural), diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 2aef995cec4..d20b33c4a1d 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -662,7 +662,7 @@ pub fn expr_block<'a, T: LintContext>( /// Trim indentation from a multiline string with possibility of ignoring the /// first line. -fn trim_multiline(s: Cow<'_, str>, ignore_first: bool, indent: Option) -> Cow<'_, str> { +pub fn trim_multiline(s: Cow<'_, str>, ignore_first: bool, indent: Option) -> Cow<'_, str> { let s_space = trim_multiline_inner(s, ignore_first, indent, ' '); let s_tab = trim_multiline_inner(s_space, ignore_first, indent, '\t'); trim_multiline_inner(s_tab, ignore_first, indent, ' ') diff --git a/tests/ui/unit_arg.rs b/tests/ui/unit_arg.rs index 2e2bd054e42..fec115ff29d 100644 --- a/tests/ui/unit_arg.rs +++ b/tests/ui/unit_arg.rs @@ -1,5 +1,11 @@ #![warn(clippy::unit_arg)] -#![allow(clippy::no_effect, unused_must_use, unused_variables, clippy::unused_unit)] +#![allow( + clippy::no_effect, + unused_must_use, + unused_variables, + clippy::unused_unit, + clippy::or_fun_call +)] use std::fmt::Debug; diff --git a/tests/ui/unit_arg.stderr b/tests/ui/unit_arg.stderr index 2a0cc1f18e2..90fee3aab23 100644 --- a/tests/ui/unit_arg.stderr +++ b/tests/ui/unit_arg.stderr @@ -1,5 +1,5 @@ error: passing a unit value to a function - --> $DIR/unit_arg.rs:23:5 + --> $DIR/unit_arg.rs:29:5 | LL | / foo({ LL | | 1; @@ -15,22 +15,24 @@ help: or move the expression in front of the call and replace it with the unit l | LL | { LL | 1; -LL | }; foo(()); +LL | }; +LL | foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:26:5 + --> $DIR/unit_arg.rs:32:5 | LL | foo(foo(1)); | ^^^^^^^^^^^ | help: move the expression in front of the call and replace it with the unit literal `()` | -LL | foo(1); foo(()); - | ^^^^^^^^^^^^^^^ +LL | foo(1); +LL | foo(()); + | error: passing a unit value to a function - --> $DIR/unit_arg.rs:27:5 + --> $DIR/unit_arg.rs:33:5 | LL | / foo({ LL | | foo(1); @@ -47,11 +49,12 @@ help: or move the expression in front of the call and replace it with the unit l LL | { LL | foo(1); LL | foo(2); -LL | }; foo(()); +LL | }; +LL | foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:32:5 + --> $DIR/unit_arg.rs:38:5 | LL | / b.bar({ LL | | 1; @@ -66,22 +69,25 @@ help: or move the expression in front of the call and replace it with the unit l | LL | { LL | 1; -LL | }; b.bar(()); +LL | }; +LL | b.bar(()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:35:5 + --> $DIR/unit_arg.rs:41:5 | LL | taking_multiple_units(foo(0), foo(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: move the expressions in front of the call and replace them with the unit literal `()` | -LL | foo(0); foo(1); taking_multiple_units((), ()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | foo(0); +LL | foo(1); +LL | taking_multiple_units((), ()); + | error: passing unit values to a function - --> $DIR/unit_arg.rs:36:5 + --> $DIR/unit_arg.rs:42:5 | LL | / taking_multiple_units(foo(0), { LL | | foo(1); @@ -95,14 +101,16 @@ LL | foo(2) | help: or move the expressions in front of the call and replace them with the unit literal `()` | -LL | foo(0); { +LL | foo(0); +LL | { LL | foo(1); LL | foo(2); -LL | }; taking_multiple_units((), ()); +LL | }; +LL | taking_multiple_units((), ()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:40:5 + --> $DIR/unit_arg.rs:46:5 | LL | / taking_multiple_units( LL | | { @@ -124,53 +132,50 @@ LL | foo(3) help: or move the expressions in front of the call and replace them with the unit literal `()` | LL | { -LL | foo(0); -LL | foo(1); -LL | }; { -LL | foo(2); -LL | foo(3); +LL | foo(0); +LL | foo(1); +LL | }; +LL | { +LL | foo(2); ... -error: use of `or` followed by a function call - --> $DIR/unit_arg.rs:51:10 - | -LL | None.or(Some(foo(2))); - | ^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some(foo(2)))` - | - = note: `-D clippy::or-fun-call` implied by `-D warnings` - error: passing a unit value to a function - --> $DIR/unit_arg.rs:51:13 + --> $DIR/unit_arg.rs:57:13 | LL | None.or(Some(foo(2))); | ^^^^^^^^^^^^ | help: move the expression in front of the call and replace it with the unit literal `()` | -LL | None.or({ foo(2); Some(()) }); - | ^^^^^^^^^^^^^^^^^^^^ +LL | None.or({ +LL | foo(2); +LL | Some(()) +LL | }); + | error: passing a unit value to a function - --> $DIR/unit_arg.rs:54:5 + --> $DIR/unit_arg.rs:60:5 | LL | foo(foo(())) | ^^^^^^^^^^^^ | help: move the expression in front of the call and replace it with the unit literal `()` | -LL | foo(()); foo(()) +LL | foo(()); +LL | foo(()) | error: passing a unit value to a function - --> $DIR/unit_arg.rs:87:5 + --> $DIR/unit_arg.rs:93:5 | LL | Some(foo(1)) | ^^^^^^^^^^^^ | help: move the expression in front of the call and replace it with the unit literal `()` | -LL | foo(1); Some(()) +LL | foo(1); +LL | Some(()) | -error: aborting due to 11 previous errors +error: aborting due to 10 previous errors diff --git a/tests/ui/unit_arg_empty_blocks.stderr b/tests/ui/unit_arg_empty_blocks.stderr index 4cbbc8b8cd4..456b12a2c6b 100644 --- a/tests/ui/unit_arg_empty_blocks.stderr +++ b/tests/ui/unit_arg_empty_blocks.stderr @@ -24,8 +24,9 @@ LL | taking_two_units({}, foo(0)); | help: move the expression in front of the call and replace it with the unit literal `()` | -LL | foo(0); taking_two_units((), ()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | foo(0); +LL | taking_two_units((), ()); + | error: passing unit values to a function --> $DIR/unit_arg_empty_blocks.rs:18:5 @@ -35,8 +36,10 @@ LL | taking_three_units({}, foo(0), foo(1)); | help: move the expressions in front of the call and replace them with the unit literal `()` | -LL | foo(0); foo(1); taking_three_units((), (), ()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | foo(0); +LL | foo(1); +LL | taking_three_units((), (), ()); + | error: aborting due to 4 previous errors From 5574182b4d2d08c848a88a1ac5633fc194e0465e Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Fri, 28 Aug 2020 18:40:22 +0900 Subject: [PATCH 04/49] Add a new lint to prevent `create_dir` from being used --- CHANGELOG.md | 1 + clippy_lints/src/create_dir.rs | 50 ++++++++++++++++++++++++++++++++++ clippy_lints/src/lib.rs | 4 +++ src/lintlist/mod.rs | 7 +++++ tests/ui/create_dir.fixed | 13 +++++++++ tests/ui/create_dir.rs | 13 +++++++++ tests/ui/create_dir.stderr | 16 +++++++++++ 7 files changed, 104 insertions(+) create mode 100644 clippy_lints/src/create_dir.rs create mode 100644 tests/ui/create_dir.fixed create mode 100644 tests/ui/create_dir.rs create mode 100644 tests/ui/create_dir.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 137b561028a..b37273af44d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1444,6 +1444,7 @@ Released 2018-09-13 [`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if [`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain [`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator +[`create_dir`]: https://rust-lang.github.io/rust-clippy/master/index.html#create_dir [`crosspointer_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#crosspointer_transmute [`dbg_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro [`debug_assert_with_mut_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#debug_assert_with_mut_call diff --git a/clippy_lints/src/create_dir.rs b/clippy_lints/src/create_dir.rs new file mode 100644 index 00000000000..229536bd673 --- /dev/null +++ b/clippy_lints/src/create_dir.rs @@ -0,0 +1,50 @@ +use crate::utils::{match_qpath, snippet, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::*; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks usage of `std::fs::create_dir` and suggest using `std::fs::create_dir_all` instead. + /// + /// **Why is this bad?** Sometimes `std::fs::crate_dir` is mistakenly chosen over `std::fs::create_dir_all`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// std::fs::create_dir("foo") + /// ``` + /// Use instead: + /// ```rust + /// std::fs::create_dir_all("foo") + /// ``` + pub CREATE_DIR, + restriction, + "calling `std::fs::create_dir` instead of `std::fs::create_dir_all`" +} + +declare_lint_pass!(CreateDir => [CREATE_DIR]); + +impl LateLintPass<'_> for CreateDir { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + if_chain! { + if let ExprKind::Call(ref func, ref args) = expr.kind; + if let ExprKind::Path(ref path) = func.kind; + if match_qpath(path, &["std", "fs", "create_dir"]); + then { + span_lint_and_sugg( + cx, + CREATE_DIR, + expr.span, + "calling `std::fs::create_dir` where there may be a better way", + "consider calling `std::fs::create_dir_all` instead", + format!("std::fs::create_dir_all({})", snippet(cx, args[0].span, "..")), + Applicability::MachineApplicable, + ) + } + } + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 577ce6523b4..7943be34c62 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -169,6 +169,7 @@ mod collapsible_if; mod comparison_chain; mod copies; mod copy_iterator; +mod create_dir; mod dbg_macro; mod default_trait_access; mod dereference; @@ -511,6 +512,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &copies::MATCH_SAME_ARMS, &copies::SAME_FUNCTIONS_IN_IF_CONDITION, ©_iterator::COPY_ITERATOR, + &create_dir::CREATE_DIR, &dbg_macro::DBG_MACRO, &default_trait_access::DEFAULT_TRAIT_ACCESS, &dereference::EXPLICIT_DEREF_METHODS, @@ -1042,6 +1044,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box items_after_statements::ItemsAfterStatements); store.register_early_pass(|| box precedence::Precedence); store.register_early_pass(|| box needless_continue::NeedlessContinue); + store.register_late_pass(|| box create_dir::CreateDir); store.register_early_pass(|| box needless_arbitrary_self_type::NeedlessArbitrarySelfType); store.register_early_pass(|| box redundant_static_lifetimes::RedundantStaticLifetimes); store.register_late_pass(|| box cargo_common_metadata::CargoCommonMetadata); @@ -1104,6 +1107,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&arithmetic::FLOAT_ARITHMETIC), LintId::of(&arithmetic::INTEGER_ARITHMETIC), LintId::of(&as_conversions::AS_CONVERSIONS), + LintId::of(&create_dir::CREATE_DIR), LintId::of(&dbg_macro::DBG_MACRO), LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE), LintId::of(&exit::EXIT), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 687fac7baa8..e5134510922 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -290,6 +290,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "copy_iterator", }, + Lint { + name: "create_dir", + group: "restriction", + desc: "calling `std::fs::create_dir` instead of `std::fs::create_dir_all`", + deprecation: None, + module: "create_dir", + }, Lint { name: "crosspointer_transmute", group: "complexity", diff --git a/tests/ui/create_dir.fixed b/tests/ui/create_dir.fixed new file mode 100644 index 00000000000..50f31f0c9c5 --- /dev/null +++ b/tests/ui/create_dir.fixed @@ -0,0 +1,13 @@ +// run-rustfix +#![allow(unused_must_use)] +#![warn(clippy::create_dir)] + +fn not_create_dir() {} + +fn main() { + std::fs::create_dir_all("foo"); + std::fs::create_dir_all("bar").unwrap(); + + not_create_dir(); + std::fs::create_dir_all("foobar"); +} diff --git a/tests/ui/create_dir.rs b/tests/ui/create_dir.rs new file mode 100644 index 00000000000..00ef37f413f --- /dev/null +++ b/tests/ui/create_dir.rs @@ -0,0 +1,13 @@ +// run-rustfix +#![allow(unused_must_use)] +#![warn(clippy::create_dir)] + +fn not_create_dir() {} + +fn main() { + std::fs::create_dir("foo"); + std::fs::create_dir("bar").unwrap(); + + not_create_dir(); + std::fs::create_dir_all("foobar"); +} diff --git a/tests/ui/create_dir.stderr b/tests/ui/create_dir.stderr new file mode 100644 index 00000000000..3ae4680d929 --- /dev/null +++ b/tests/ui/create_dir.stderr @@ -0,0 +1,16 @@ +error: calling `std::fs::create_dir` where there may be a better way + --> $DIR/create_dir.rs:8:5 + | +LL | std::fs::create_dir("foo"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `std::fs::create_dir_all` instead: `std::fs::create_dir_all("foo")` + | + = note: `-D clippy::create-dir` implied by `-D warnings` + +error: calling `std::fs::create_dir` where there may be a better way + --> $DIR/create_dir.rs:9:5 + | +LL | std::fs::create_dir("bar").unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `std::fs::create_dir_all` instead: `std::fs::create_dir_all("bar")` + +error: aborting due to 2 previous errors + From 607905d126c55422668007737c22d7a7a89c0d57 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Fri, 28 Aug 2020 18:53:15 +0900 Subject: [PATCH 05/49] Add STD_FS_CREATE_DIR into paths --- clippy_lints/src/create_dir.rs | 4 ++-- clippy_lints/src/utils/paths.rs | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/create_dir.rs b/clippy_lints/src/create_dir.rs index 229536bd673..7ba6facda6a 100644 --- a/clippy_lints/src/create_dir.rs +++ b/clippy_lints/src/create_dir.rs @@ -1,4 +1,4 @@ -use crate::utils::{match_qpath, snippet, span_lint_and_sugg}; +use crate::utils::{match_qpath, paths, snippet, span_lint_and_sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::*; @@ -33,7 +33,7 @@ impl LateLintPass<'_> for CreateDir { if_chain! { if let ExprKind::Call(ref func, ref args) = expr.kind; if let ExprKind::Path(ref path) = func.kind; - if match_qpath(path, &["std", "fs", "create_dir"]); + if match_qpath(path, &paths::STD_FS_CREATE_DIR); then { span_lint_and_sugg( cx, diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index d44854aefe9..65320d6a0e0 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -110,6 +110,7 @@ pub const SLICE_ITER: [&str; 3] = ["core", "slice", "Iter"]; pub const STDERR: [&str; 4] = ["std", "io", "stdio", "stderr"]; pub const STDOUT: [&str; 4] = ["std", "io", "stdio", "stdout"]; pub const STD_CONVERT_IDENTITY: [&str; 3] = ["std", "convert", "identity"]; +pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"]; pub const STD_MEM_TRANSMUTE: [&str; 3] = ["std", "mem", "transmute"]; pub const STD_PTR_NULL: [&str; 3] = ["std", "ptr", "null"]; pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"]; From 34e302e67c08c9b97d58d062ea83cc1fd860c56e Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Fri, 28 Aug 2020 19:35:04 +0900 Subject: [PATCH 06/49] Fix clippy error --- clippy_lints/src/create_dir.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/create_dir.rs b/clippy_lints/src/create_dir.rs index 7ba6facda6a..4fede8857c7 100644 --- a/clippy_lints/src/create_dir.rs +++ b/clippy_lints/src/create_dir.rs @@ -1,7 +1,7 @@ use crate::utils::{match_qpath, paths, snippet, span_lint_and_sugg}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::*; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; From eebd2483654456e332d7cf53218b56b9cbd6f2f5 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Fri, 28 Aug 2020 19:56:03 +0900 Subject: [PATCH 07/49] Fix errors --- clippy_lints/src/create_dir.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/create_dir.rs b/clippy_lints/src/create_dir.rs index 4fede8857c7..bf47c1f84a2 100644 --- a/clippy_lints/src/create_dir.rs +++ b/clippy_lints/src/create_dir.rs @@ -15,11 +15,11 @@ declare_clippy_lint! { /// **Example:** /// /// ```rust - /// std::fs::create_dir("foo") + /// std::fs::create_dir("foo"); /// ``` /// Use instead: /// ```rust - /// std::fs::create_dir_all("foo") + /// std::fs::create_dir_all("foo"); /// ``` pub CREATE_DIR, restriction, From 282c59820b8e1d8c76f440484b81a190c576f91b Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 28 Aug 2020 16:10:16 +0200 Subject: [PATCH 08/49] Merge commit '3d0b0e66afdfaa519d8855b338b35b4605775945' into clippyup --- .cargo/config | 2 +- .github/workflows/clippy.yml | 7 + CHANGELOG.md | 6 + CONTRIBUTING.md | 58 ++- Cargo.toml | 4 +- clippy_dev/src/new_lint.rs | 2 +- clippy_lints/Cargo.toml | 4 +- clippy_lints/src/assign_ops.rs | 14 +- clippy_lints/src/attrs.rs | 2 +- clippy_lints/src/booleans.rs | 10 +- clippy_lints/src/copies.rs | 7 +- clippy_lints/src/doc.rs | 75 ++- clippy_lints/src/double_comparison.rs | 5 +- clippy_lints/src/duration_subsec.rs | 2 +- clippy_lints/src/enum_clike.rs | 2 +- clippy_lints/src/enum_variants.rs | 6 +- clippy_lints/src/eq_op.rs | 4 +- .../src/float_equality_without_abs.rs | 110 +++++ clippy_lints/src/floating_point_arithmetic.rs | 24 +- clippy_lints/src/if_let_some_result.rs | 4 +- clippy_lints/src/if_not_else.rs | 4 +- clippy_lints/src/implicit_saturating_sub.rs | 4 +- clippy_lints/src/inherent_impl.rs | 4 +- clippy_lints/src/int_plus_one.rs | 6 +- clippy_lints/src/len_zero.rs | 17 +- clippy_lints/src/let_and_return.rs | 124 ----- clippy_lints/src/lib.rs | 45 +- clippy_lints/src/loops.rs | 30 +- clippy_lints/src/map_clone.rs | 12 +- clippy_lints/src/match_on_vec_items.rs | 2 +- clippy_lints/src/methods/mod.rs | 403 ++++++++++++---- .../src/methods/unnecessary_lazy_eval.rs | 111 +++++ clippy_lints/src/misc.rs | 3 +- clippy_lints/src/mut_reference.rs | 17 +- clippy_lints/src/mutex_atomic.rs | 4 +- clippy_lints/src/non_copy_const.rs | 15 +- clippy_lints/src/precedence.rs | 59 +-- clippy_lints/src/ptr.rs | 17 +- clippy_lints/src/question_mark.rs | 6 +- clippy_lints/src/redundant_closure_call.rs | 19 +- clippy_lints/src/repeat_once.rs | 14 +- clippy_lints/src/returns.rs | 440 +++++++++--------- clippy_lints/src/self_assignment.rs | 51 ++ clippy_lints/src/stable_sort_primitive.rs | 10 +- clippy_lints/src/suspicious_trait_impl.rs | 10 +- clippy_lints/src/swap.rs | 14 +- clippy_lints/src/to_string_in_display.rs | 122 +++++ clippy_lints/src/transmute.rs | 10 +- .../src/trivially_copy_pass_by_ref.rs | 9 +- clippy_lints/src/try_err.rs | 6 +- clippy_lints/src/types.rs | 15 +- clippy_lints/src/unnested_or_patterns.rs | 4 +- clippy_lints/src/unused_unit.rs | 144 ++++++ clippy_lints/src/unwrap_in_result.rs | 140 ++++++ clippy_lints/src/use_self.rs | 2 +- clippy_lints/src/useless_conversion.rs | 5 +- clippy_lints/src/utils/ast_utils.rs | 2 +- clippy_lints/src/utils/author.rs | 21 +- clippy_lints/src/utils/conf.rs | 2 +- clippy_lints/src/utils/higher.rs | 76 +-- clippy_lints/src/utils/hir_utils.rs | 45 +- clippy_lints/src/utils/internal_lints.rs | 5 +- clippy_lints/src/utils/mod.rs | 44 +- clippy_lints/src/utils/paths.rs | 3 + clippy_lints/src/vec.rs | 97 ++-- clippy_lints/src/wildcard_imports.rs | 7 +- clippy_lints/src/write.rs | 31 +- doc/adding_lints.md | 6 + src/lintlist/mod.rs | 48 +- tests/fmt.rs | 9 +- tests/ui/auxiliary/wildcard_imports_helper.rs | 6 + tests/ui/borrow_interior_mutable_const.rs | 35 +- tests/ui/borrow_interior_mutable_const.stderr | 32 +- tests/ui/crashes/ice-5944.rs | 13 + tests/ui/duration_subsec.stderr | 10 +- tests/ui/enum_clike_unportable_variant.stderr | 18 +- tests/ui/enum_variants.stderr | 20 +- tests/ui/float_equality_without_abs.rs | 31 ++ tests/ui/float_equality_without_abs.stderr | 92 ++++ tests/ui/if_let_some_result.stderr | 8 +- tests/ui/if_not_else.stderr | 4 +- tests/ui/impl.stderr | 8 +- tests/ui/implicit_saturating_sub.stderr | 46 +- tests/ui/int_plus_one.stderr | 8 +- tests/ui/iter_next_slice.stderr | 8 +- tests/ui/len_zero.fixed | 8 - tests/ui/len_zero.rs | 8 - tests/ui/len_zero_ranges.fixed | 10 +- tests/ui/len_zero_ranges.rs | 10 +- tests/ui/len_zero_ranges.stderr | 10 +- tests/ui/let_and_return.rs | 21 + tests/ui/let_and_return.stderr | 16 +- tests/ui/map_clone.stderr | 24 +- tests/ui/methods.rs | 68 +-- tests/ui/methods.stderr | 36 +- tests/ui/mut_reference.stderr | 6 +- tests/ui/mutex_atomic.stderr | 14 +- tests/ui/needless_doc_main.rs | 68 ++- tests/ui/needless_doc_main.stderr | 12 +- tests/ui/needless_return.fixed | 17 + tests/ui/needless_return.rs | 17 + tests/ui/needless_return.stderr | 14 +- tests/ui/new_ret_no_self.rs | 134 +++++- tests/ui/new_ret_no_self.stderr | 30 +- tests/ui/option_as_ref_deref.fixed | 3 + tests/ui/option_as_ref_deref.rs | 3 + tests/ui/option_as_ref_deref.stderr | 8 +- tests/ui/precedence.fixed | 8 + tests/ui/precedence.rs | 8 + tests/ui/precedence.stderr | 20 +- tests/ui/redundant_allocation.fixed | 2 +- tests/ui/redundant_allocation.stderr | 2 +- tests/ui/redundant_closure_call_early.stderr | 4 +- .../ui/redundant_closure_call_fixable.stderr | 2 +- tests/ui/redundant_closure_call_late.rs | 12 + tests/ui/same_item_push.rs | 8 + tests/ui/self_assignment.rs | 67 +++ tests/ui/self_assignment.stderr | 70 +++ tests/ui/should_impl_trait/corner_cases.rs | 83 ++++ tests/ui/should_impl_trait/method_list_1.rs | 87 ++++ .../ui/should_impl_trait/method_list_1.stderr | 143 ++++++ tests/ui/should_impl_trait/method_list_2.rs | 88 ++++ .../ui/should_impl_trait/method_list_2.stderr | 153 ++++++ tests/ui/single_char_push_str.fixed | 15 + tests/ui/single_char_push_str.rs | 15 + tests/ui/single_char_push_str.stderr | 34 ++ tests/ui/stable_sort_primitive.stderr | 14 +- tests/ui/suspicious_arithmetic_impl.rs | 52 ++- tests/ui/suspicious_arithmetic_impl.stderr | 44 +- tests/ui/to_string_in_display.rs | 69 +++ tests/ui/to_string_in_display.stderr | 10 + tests/ui/transmute.rs | 24 +- tests/ui/transmute.stderr | 66 +-- tests/ui/transmute_float_to_int.rs | 16 +- tests/ui/transmute_float_to_int.stderr | 12 +- tests/ui/trivially_copy_pass_by_ref.rs | 18 + tests/ui/trivially_copy_pass_by_ref.stderr | 14 +- tests/ui/unnecessary_clone.rs | 18 + tests/ui/unnecessary_clone.stderr | 8 +- tests/ui/unnecessary_lazy_eval.fixed | 117 +++++ tests/ui/unnecessary_lazy_eval.rs | 117 +++++ tests/ui/unnecessary_lazy_eval.stderr | 148 ++++++ tests/ui/unwrap_in_result.rs | 44 ++ tests/ui/unwrap_in_result.stderr | 41 ++ tests/ui/useless_conversion.fixed | 5 + tests/ui/useless_conversion.rs | 5 + tests/ui/useless_conversion.stderr | 8 +- tests/ui/vec.fixed | 7 + tests/ui/vec.rs | 7 + tests/ui/wildcard_imports.fixed | 2 + tests/ui/wildcard_imports.rs | 2 + tests/ui/wildcard_imports.stderr | 28 +- tests/ui/wrong_self_convention.rs | 13 + tests/ui/wrong_self_convention.stderr | 24 +- 154 files changed, 4170 insertions(+), 1102 deletions(-) create mode 100644 clippy_lints/src/float_equality_without_abs.rs delete mode 100644 clippy_lints/src/let_and_return.rs create mode 100644 clippy_lints/src/methods/unnecessary_lazy_eval.rs create mode 100644 clippy_lints/src/self_assignment.rs create mode 100644 clippy_lints/src/to_string_in_display.rs create mode 100644 clippy_lints/src/unused_unit.rs create mode 100644 clippy_lints/src/unwrap_in_result.rs create mode 100644 tests/ui/crashes/ice-5944.rs create mode 100644 tests/ui/float_equality_without_abs.rs create mode 100644 tests/ui/float_equality_without_abs.stderr create mode 100644 tests/ui/self_assignment.rs create mode 100644 tests/ui/self_assignment.stderr create mode 100644 tests/ui/should_impl_trait/corner_cases.rs create mode 100644 tests/ui/should_impl_trait/method_list_1.rs create mode 100644 tests/ui/should_impl_trait/method_list_1.stderr create mode 100644 tests/ui/should_impl_trait/method_list_2.rs create mode 100644 tests/ui/should_impl_trait/method_list_2.stderr create mode 100644 tests/ui/single_char_push_str.fixed create mode 100644 tests/ui/single_char_push_str.rs create mode 100644 tests/ui/single_char_push_str.stderr create mode 100644 tests/ui/to_string_in_display.rs create mode 100644 tests/ui/to_string_in_display.stderr create mode 100644 tests/ui/unnecessary_lazy_eval.fixed create mode 100644 tests/ui/unnecessary_lazy_eval.rs create mode 100644 tests/ui/unnecessary_lazy_eval.stderr create mode 100644 tests/ui/unwrap_in_result.rs create mode 100644 tests/ui/unwrap_in_result.stderr diff --git a/.cargo/config b/.cargo/config index 2bad3b9c57f..e70da43ab47 100644 --- a/.cargo/config +++ b/.cargo/config @@ -1,6 +1,6 @@ [alias] uitest = "test --test compile-test" -dev = "run --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --" +dev = "run --target-dir clippy_dev/target --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --" [build] rustflags = ["-Zunstable-options"] diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 5fa8009a8b4..99e371631b1 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -92,6 +92,13 @@ jobs: env: OS: ${{ runner.os }} + - name: Test cargo dev new lint + run: | + cargo dev new_lint --name new_early_pass --pass early + cargo dev new_lint --name new_late_pass --pass late + cargo check + git reset --hard HEAD + # Cleanup - name: Run cargo-cache --autoclean run: | diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e9ed54c848..137b561028a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1498,6 +1498,7 @@ Released 2018-09-13 [`float_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_arithmetic [`float_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp [`float_cmp_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp_const +[`float_equality_without_abs`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_equality_without_abs [`fn_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_address_comparisons [`fn_params_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_params_excessive_bools [`fn_to_numeric_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast @@ -1690,6 +1691,7 @@ Released 2018-09-13 [`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition [`same_item_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push [`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some +[`self_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_assignment [`serde_api_misuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#serde_api_misuse [`shadow_reuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_reuse [`shadow_same`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_same @@ -1699,6 +1701,7 @@ Released 2018-09-13 [`should_implement_trait`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_implement_trait [`similar_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#similar_names [`single_char_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern +[`single_char_push_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_push_str [`single_component_path_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_component_path_imports [`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match [`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else @@ -1723,6 +1726,7 @@ Released 2018-09-13 [`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment [`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr [`to_digit_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some +[`to_string_in_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_display [`todo`]: https://rust-lang.github.io/rust-clippy/master/index.html#todo [`too_many_arguments`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments [`too_many_lines`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines @@ -1752,6 +1756,7 @@ Released 2018-09-13 [`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast [`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map [`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold +[`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations [`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed [`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation [`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by @@ -1773,6 +1778,7 @@ Released 2018-09-13 [`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label [`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self [`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit +[`unwrap_in_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_in_result [`unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used [`use_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_debug [`use_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_self diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5f7b1e85ee9..54777810abb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -189,6 +189,35 @@ Clippy in the `rust-lang/rust` repository. For general information about `subtree`s in the Rust repository see [Rust's `CONTRIBUTING.md`][subtree]. +### Patching git-subtree to work with big repos + +Currently there's a bug in `git-subtree` that prevents it from working properly +with the [`rust-lang/rust`] repo. There's an open PR to fix that, but it's stale. +Before continuing with the following steps, we need to manually apply that fix to +our local copy of `git-subtree`. + +You can get the patched version of `git-subtree` from [here][gitgitgadget-pr]. +Put this file under `/usr/lib/git-core` (taking a backup of the previous file) +and make sure it has the proper permissions: + +```bash +sudo cp --backup /path/to/patched/git-subtree.sh /usr/lib/git-core/git-subtree +sudo chmod --reference=/usr/lib/git-core/git-subtree~ /usr/lib/git-core/git-subtree +sudo chown --reference=/usr/lib/git-core/git-subtree~ /usr/lib/git-core/git-subtree +``` + +_Note:_ The first time running `git subtree push` a cache has to be built. This +involves going through the complete Clippy history once. For this you have to +increase the stack limit though, which you can do with `ulimit -s 60000`. +Make sure to run the `ulimit` command from the same session you call git subtree. + +_Note:_ If you are a Debian user, `dash` is the shell used by default for scripts instead of `sh`. +This shell has a hardcoded recursion limit set to 1000. In order to make this process work, +you need to force the script to run `bash` instead. You can do this by editing the first +line of the `git-subtree` script and changing `sh` to `bash`. + +### Performing the sync + Here is a TL;DR version of the sync process (all of the following commands have to be run inside the `rust` directory): @@ -198,6 +227,7 @@ to be run inside the `rust` directory): # Make sure to change `your-github-name` to your github name in the following command git subtree push -P src/tools/clippy git@github.com:your-github-name/rust-clippy sync-from-rust ``` + _Note:_ This will directly push to the remote repository. You can also push to your local copy by replacing the remote address with `/path/to/rust-clippy` directory. @@ -213,14 +243,30 @@ to be run inside the `rust` directory): 3. Open a PR to `rust-lang/rust-clippy` and wait for it to get merged (to accelerate the process ping the `@rust-lang/clippy` team in your PR and/or ~~annoy~~ ask them in the [Discord] channel.) -4. Sync the `rust-lang/rust-clippy` master to the rust-copy of Clippy: + +### Syncing back changes in Clippy to [`rust-lang/rust`] + +To avoid flooding the [`rust-lang/rust`] PR queue, changes in Clippy's repo are synced back +in a bi-weekly basis if there's no urgent changes. This is done starting on the day of +the Rust stable release and then every other week. That way we guarantee that +every feature in Clippy is available for 2 weeks in nightly, before it can get to beta. +For reference, the first sync following this cadence was performed the 2020-08-27. + +All of the following commands have to be run inside the `rust` directory. + +1. Make sure Clippy itself is up-to-date by following the steps outlined in the previous +section if necessary. + +2. Sync the `rust-lang/rust-clippy` master to the rust-copy of Clippy: ```bash git checkout -b sync-from-clippy git subtree pull -P src/tools/clippy https://github.com/rust-lang/rust-clippy master ``` -5. Open a PR to [`rust-lang/rust`] +3. Open a PR to [`rust-lang/rust`] -Also, you may want to define remotes, so you don't have to type out the remote +### Defining remotes + +You may want to define remotes, so you don't have to type out the remote addresses on every sync. You can do this with the following commands (these commands still have to be run inside the `rust` directory): @@ -241,12 +287,6 @@ You can then sync with the remote names from above, e.g.: $ git subtree push -P src/tools/clippy clippy-local sync-from-rust ``` -_Note:_ The first time running `git subtree push` a cache has to be built. This -involves going through the complete Clippy history once. For this you have to -increase the stack limit though, which you can do with `ulimit -s 60000`. For -this to work, you will need the fix of `git subtree` available -[here][gitgitgadget-pr]. - [gitgitgadget-pr]: https://github.com/gitgitgadget/git/pull/493 [subtree]: https://rustc-dev-guide.rust-lang.org/contributing.html#external-dependencies-subtree [`rust-lang/rust`]: https://github.com/rust-lang/rust diff --git a/Cargo.toml b/Cargo.toml index 836897927b0..c7a3099b8ab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,13 +31,13 @@ path = "src/driver.rs" # begin automatic update clippy_lints = { version = "0.0.212", path = "clippy_lints" } # end automatic update -semver = "0.9" +semver = "0.10" rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util"} tempfile = { version = "3.1.0", optional = true } lazy_static = "1.0" [dev-dependencies] -cargo_metadata = "0.9.1" +cargo_metadata = "0.11.1" compiletest_rs = { version = "0.5.0", features = ["tmp"] } tester = "0.7" lazy_static = "1.0" diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index 1e032a7bc20..d951ca0e630 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -47,7 +47,7 @@ pub fn create(pass: Option<&str>, lint_name: Option<&str>, category: Option<&str fn create_lint(lint: &LintData) -> io::Result<()> { let (pass_type, pass_lifetimes, pass_import, context_import) = match lint.pass { "early" => ("EarlyLintPass", "", "use rustc_ast::ast::*;", "EarlyContext"), - "late" => ("LateLintPass", "<'_, '_>", "use rustc_hir::*;", "LateContext"), + "late" => ("LateLintPass", "<'_>", "use rustc_hir::*;", "LateContext"), _ => { unreachable!("`pass_type` should only ever be `early` or `late`!"); }, diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index e959c1a6511..cc7d3a04f00 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -17,7 +17,7 @@ keywords = ["clippy", "lint", "plugin"] edition = "2018" [dependencies] -cargo_metadata = "0.9.1" +cargo_metadata = "0.11.1" if_chain = "1.0.0" itertools = "0.9" lazy_static = "1.0.2" @@ -28,7 +28,7 @@ serde = { version = "1.0", features = ["derive"] } smallvec = { version = "1", features = ["union"] } toml = "0.5.3" unicode-normalization = "0.1" -semver = "0.9.0" +semver = "0.10.0" # NOTE: cargo requires serde feat in its url dep # see url = { version = "2.1.0", features = ["serde"] } diff --git a/clippy_lints/src/assign_ops.rs b/clippy_lints/src/assign_ops.rs index dab1e96e282..b3185b88840 100644 --- a/clippy_lints/src/assign_ops.rs +++ b/clippy_lints/src/assign_ops.rs @@ -1,5 +1,5 @@ use crate::utils::{ - get_trait_def_id, implements_trait, snippet_opt, span_lint_and_then, trait_ref_of_method, SpanlessEq, + eq_expr_value, get_trait_def_id, implements_trait, snippet_opt, span_lint_and_then, trait_ref_of_method, }; use crate::utils::{higher, sugg}; use if_chain::if_chain; @@ -70,11 +70,11 @@ impl<'tcx> LateLintPass<'tcx> for AssignOps { return; } // lhs op= l op r - if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, l) { + if eq_expr_value(cx, lhs, l) { lint_misrefactored_assign_op(cx, expr, *op, rhs, lhs, r); } // lhs op= l commutative_op r - if is_commutative(op.node) && SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, r) { + if is_commutative(op.node) && eq_expr_value(cx, lhs, r) { lint_misrefactored_assign_op(cx, expr, *op, rhs, lhs, l); } } @@ -161,14 +161,12 @@ impl<'tcx> LateLintPass<'tcx> for AssignOps { if visitor.counter == 1 { // a = a op b - if SpanlessEq::new(cx).ignore_fn().eq_expr(assignee, l) { + if eq_expr_value(cx, assignee, l) { lint(assignee, r); } // a = b commutative_op a // Limited to primitive type as these ops are know to be commutative - if SpanlessEq::new(cx).ignore_fn().eq_expr(assignee, r) - && cx.typeck_results().expr_ty(assignee).is_primitive_ty() - { + if eq_expr_value(cx, assignee, r) && cx.typeck_results().expr_ty(assignee).is_primitive_ty() { match op.node { hir::BinOpKind::Add | hir::BinOpKind::Mul @@ -253,7 +251,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ExprVisitor<'a, 'tcx> { type Map = Map<'tcx>; fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { - if SpanlessEq::new(self.cx).ignore_fn().eq_expr(self.assignee, expr) { + if eq_expr_value(self.cx, self.assignee, expr) { self.counter += 1; } diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 76ccf2e4bb9..cfcc1b3c5f3 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -5,8 +5,8 @@ use crate::utils::{ span_lint_and_sugg, span_lint_and_then, without_block_comments, }; use if_chain::if_chain; -use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem}; use rustc_ast::util::lev_distance::find_best_match_for_name; +use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem}; use rustc_errors::Applicability; use rustc_hir::{ Block, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, StmtKind, TraitFn, TraitItem, TraitItemKind, diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 18529f2113e..280a2c7fe67 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -1,6 +1,6 @@ use crate::utils::{ - get_trait_def_id, implements_trait, in_macro, is_type_diagnostic_item, paths, snippet_opt, span_lint_and_sugg, - span_lint_and_then, SpanlessEq, + eq_expr_value, get_trait_def_id, implements_trait, in_macro, is_type_diagnostic_item, paths, snippet_opt, + span_lint_and_sugg, span_lint_and_then, }; use if_chain::if_chain; use rustc_ast::ast::LitKind; @@ -128,7 +128,7 @@ impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> { } } for (n, expr) in self.terminals.iter().enumerate() { - if SpanlessEq::new(self.cx).ignore_fn().eq_expr(e, expr) { + if eq_expr_value(self.cx, e, expr) { #[allow(clippy::cast_possible_truncation)] return Ok(Bool::Term(n as u8)); } @@ -138,8 +138,8 @@ impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> { if implements_ord(self.cx, e_lhs); if let ExprKind::Binary(expr_binop, expr_lhs, expr_rhs) = &expr.kind; if negate(e_binop.node) == Some(expr_binop.node); - if SpanlessEq::new(self.cx).ignore_fn().eq_expr(e_lhs, expr_lhs); - if SpanlessEq::new(self.cx).ignore_fn().eq_expr(e_rhs, expr_rhs); + if eq_expr_value(self.cx, e_lhs, expr_lhs); + if eq_expr_value(self.cx, e_rhs, expr_rhs); then { #[allow(clippy::cast_possible_truncation)] return Ok(Bool::Not(Box::new(Bool::Term(n as u8)))); diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 1f8bff8d71e..10a64769585 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -1,5 +1,5 @@ +use crate::utils::{eq_expr_value, SpanlessEq, SpanlessHash}; use crate::utils::{get_parent_expr, higher, if_sequence, snippet, span_lint_and_note, span_lint_and_then}; -use crate::utils::{SpanlessEq, SpanlessHash}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::{Arm, Block, Expr, ExprKind, MatchSource, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -197,8 +197,7 @@ fn lint_same_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { h.finish() }; - let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = - &|&lhs, &rhs| -> bool { SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, rhs) }; + let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = &|&lhs, &rhs| -> bool { eq_expr_value(cx, lhs, rhs) }; for (i, j) in search_same(conds, hash, eq) { span_lint_and_note( @@ -222,7 +221,7 @@ fn lint_same_fns_in_if_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = &|&lhs, &rhs| -> bool { // Do not spawn warning if `IFS_SAME_COND` already produced it. - if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, rhs) { + if eq_expr_value(cx, lhs, rhs) { return false; } SpanlessEq::new(cx).eq_expr(lhs, rhs) diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 6ce36fd2360..9555459e240 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -1,16 +1,22 @@ use crate::utils::{implements_trait, is_entrypoint_fn, is_type_diagnostic_item, return_ty, span_lint}; use if_chain::if_chain; use itertools::Itertools; -use rustc_ast::ast::{AttrKind, Attribute}; +use rustc_ast::ast::{Async, AttrKind, Attribute, FnRetTy, ItemKind}; use rustc_ast::token::CommentKind; use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::sync::Lrc; +use rustc_errors::emitter::EmitterWriter; +use rustc_errors::Handler; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty; +use rustc_parse::maybe_new_parser_from_source_str; +use rustc_session::parse::ParseSess; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::source_map::{BytePos, MultiSpan, Span}; -use rustc_span::Pos; +use rustc_span::source_map::{BytePos, FilePathMapping, MultiSpan, SourceMap, Span}; +use rustc_span::{FileName, Pos}; +use std::io; use std::ops::Range; use url::Url; @@ -431,10 +437,67 @@ fn check_doc<'a, Events: Iterator, Range, text: &str, span: Span) { - if text.contains("fn main() {") && !LEAVE_MAIN_PATTERNS.iter().any(|p| text.contains(p)) { + fn has_needless_main(code: &str) -> bool { + 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()) { + Ok(p) => p, + Err(errs) => { + for mut err in errs { + err.cancel(); + } + return false; + }, + }; + + let mut relevant_main_found = false; + loop { + match parser.parse_item() { + Ok(Some(item)) => match &item.kind { + // Tests with one of these items are ignored + ItemKind::Static(..) + | ItemKind::Const(..) + | ItemKind::ExternCrate(..) + | ItemKind::ForeignMod(..) => return false, + // We found a main function ... + ItemKind::Fn(_, sig, _, Some(block)) if item.ident.name == sym!(main) => { + let is_async = matches!(sig.header.asyncness, Async::Yes{..}); + let returns_nothing = match &sig.decl.output { + FnRetTy::Default(..) => true, + FnRetTy::Ty(ty) if ty.kind.is_unit() => true, + _ => false, + }; + + if returns_nothing && !is_async && !block.stmts.is_empty() { + // This main function should be linted, but only if there are no other functions + relevant_main_found = true; + } else { + // This main function should not be linted, we're done + return false; + } + }, + // Another function was found; this case is ignored too + ItemKind::Fn(..) => return false, + _ => {}, + }, + Ok(None) => break, + Err(mut e) => { + e.cancel(); + return false; + }, + } + } + + relevant_main_found + } + + if has_needless_main(text) { span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest"); } } diff --git a/clippy_lints/src/double_comparison.rs b/clippy_lints/src/double_comparison.rs index bae7c4647d4..19f56195ec1 100644 --- a/clippy_lints/src/double_comparison.rs +++ b/clippy_lints/src/double_comparison.rs @@ -6,7 +6,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use crate::utils::{snippet_with_applicability, span_lint_and_sugg, SpanlessEq}; +use crate::utils::{eq_expr_value, snippet_with_applicability, span_lint_and_sugg}; declare_clippy_lint! { /// **What it does:** Checks for double comparisons that could be simplified to a single expression. @@ -46,8 +46,7 @@ impl<'tcx> DoubleComparisons { }, _ => return, }; - let mut spanless_eq = SpanlessEq::new(cx).ignore_fn(); - if !(spanless_eq.eq_expr(&llhs, &rlhs) && spanless_eq.eq_expr(&lrhs, &rrhs)) { + if !(eq_expr_value(cx, &llhs, &rlhs) && eq_expr_value(cx, &lrhs, &rrhs)) { return; } macro_rules! lint_double_comparison { diff --git a/clippy_lints/src/duration_subsec.rs b/clippy_lints/src/duration_subsec.rs index 1dfb2eaa579..8ece44878fe 100644 --- a/clippy_lints/src/duration_subsec.rs +++ b/clippy_lints/src/duration_subsec.rs @@ -56,7 +56,7 @@ impl<'tcx> LateLintPass<'tcx> for DurationSubsec { cx, DURATION_SUBSEC, expr.span, - &format!("Calling `{}()` is more concise than this calculation", suggested_fn), + &format!("calling `{}()` is more concise than this calculation", suggested_fn), "try", format!( "{}.{}()", diff --git a/clippy_lints/src/enum_clike.rs b/clippy_lints/src/enum_clike.rs index 91214f277be..48caf48dbdb 100644 --- a/clippy_lints/src/enum_clike.rs +++ b/clippy_lints/src/enum_clike.rs @@ -72,7 +72,7 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant { cx, ENUM_CLIKE_UNPORTABLE_VARIANT, var.span, - "Clike enum variant discriminant is not portable to 32-bit targets", + "C-like enum variant discriminant is not portable to 32-bit targets", ); }; } diff --git a/clippy_lints/src/enum_variants.rs b/clippy_lints/src/enum_variants.rs index cb0fd59a2d4..a9294a87f15 100644 --- a/clippy_lints/src/enum_variants.rs +++ b/clippy_lints/src/enum_variants.rs @@ -183,10 +183,10 @@ fn check_variant( && name.chars().nth(item_name_chars).map_or(false, |c| !c.is_lowercase()) && name.chars().nth(item_name_chars + 1).map_or(false, |c| !c.is_numeric()) { - span_lint(cx, lint, var.span, "Variant name starts with the enum's name"); + span_lint(cx, lint, var.span, "variant name starts with the enum's name"); } if partial_rmatch(item_name, &name) == item_name_chars { - span_lint(cx, lint, var.span, "Variant name ends with the enum's name"); + span_lint(cx, lint, var.span, "variant name ends with the enum's name"); } } let first = &def.variants[0].ident.name.as_str(); @@ -227,7 +227,7 @@ fn check_variant( cx, lint, span, - &format!("All variants have the same {}fix: `{}`", what, value), + &format!("all variants have the same {}fix: `{}`", what, value), None, &format!( "remove the {}fixes and use full paths to \ diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index 140cd21c34e..e16ec783fab 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -1,5 +1,5 @@ use crate::utils::{ - implements_trait, in_macro, is_copy, multispan_sugg, snippet, span_lint, span_lint_and_then, SpanlessEq, + eq_expr_value, implements_trait, in_macro, is_copy, multispan_sugg, snippet, span_lint, span_lint_and_then, }; use rustc_errors::Applicability; use rustc_hir::{BinOp, BinOpKind, BorrowKind, Expr, ExprKind}; @@ -69,7 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for EqOp { if macro_with_not_op(&left.kind) || macro_with_not_op(&right.kind) { return; } - if is_valid_operator(op) && SpanlessEq::new(cx).ignore_fn().eq_expr(left, right) { + if is_valid_operator(op) && eq_expr_value(cx, left, right) { span_lint( cx, EQ_OP, diff --git a/clippy_lints/src/float_equality_without_abs.rs b/clippy_lints/src/float_equality_without_abs.rs new file mode 100644 index 00000000000..9ac5a45eb45 --- /dev/null +++ b/clippy_lints/src/float_equality_without_abs.rs @@ -0,0 +1,110 @@ +use crate::utils::{match_qpath, paths, span_lint_and_then, sugg}; +use if_chain::if_chain; +use rustc_ast::util::parser::AssocOp; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Spanned; + +declare_clippy_lint! { + /// **What it does:** Checks for statements of the form `(a - b) < f32::EPSILON` or + /// `(a - b) < f64::EPSILON`. Notes the missing `.abs()`. + /// + /// **Why is this bad?** The code without `.abs()` is more likely to have a bug. + /// + /// **Known problems:** If the user can ensure that b is larger than a, the `.abs()` is + /// technically unneccessary. However, it will make the code more robust and doesn't have any + /// large performance implications. If the abs call was deliberately left out for performance + /// reasons, it is probably better to state this explicitly in the code, which then can be done + /// with an allow. + /// + /// **Example:** + /// + /// ```rust + /// pub fn is_roughly_equal(a: f32, b: f32) -> bool { + /// (a - b) < f32::EPSILON + /// } + /// ``` + /// Use instead: + /// ```rust + /// pub fn is_roughly_equal(a: f32, b: f32) -> bool { + /// (a - b).abs() < f32::EPSILON + /// } + /// ``` + pub FLOAT_EQUALITY_WITHOUT_ABS, + correctness, + "float equality check without `.abs()`" +} + +declare_lint_pass!(FloatEqualityWithoutAbs => [FLOAT_EQUALITY_WITHOUT_ABS]); + +impl<'tcx> LateLintPass<'tcx> for FloatEqualityWithoutAbs { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + let lhs; + let rhs; + + // check if expr is a binary expression with a lt or gt operator + if let ExprKind::Binary(op, ref left, ref right) = expr.kind { + match op.node { + BinOpKind::Lt => { + lhs = left; + rhs = right; + }, + BinOpKind::Gt => { + lhs = right; + rhs = left; + }, + _ => return, + }; + } else { + return; + } + + if_chain! { + + // left hand side is a substraction + if let ExprKind::Binary( + Spanned { + node: BinOpKind::Sub, + .. + }, + val_l, + val_r, + ) = lhs.kind; + + // right hand side matches either f32::EPSILON or f64::EPSILON + if let ExprKind::Path(ref epsilon_path) = rhs.kind; + if match_qpath(epsilon_path, &paths::F32_EPSILON) || match_qpath(epsilon_path, &paths::F64_EPSILON); + + // values of the substractions on the left hand side are of the type float + let t_val_l = cx.typeck_results().expr_ty(val_l); + let t_val_r = cx.typeck_results().expr_ty(val_r); + if let ty::Float(_) = t_val_l.kind; + if let ty::Float(_) = t_val_r.kind; + + then { + let sug_l = sugg::Sugg::hir(cx, &val_l, ".."); + let sug_r = sugg::Sugg::hir(cx, &val_r, ".."); + // format the suggestion + let suggestion = format!("{}.abs()", sugg::make_assoc(AssocOp::Subtract, &sug_l, &sug_r).maybe_par()); + // spans the lint + span_lint_and_then( + cx, + FLOAT_EQUALITY_WITHOUT_ABS, + expr.span, + "float equality check without `.abs()`", + | diag | { + diag.span_suggestion( + lhs.span, + "add `.abs()`", + suggestion, + Applicability::MaybeIncorrect, + ); + } + ); + } + } + } +} diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 93f6ec92ec7..1b02cee126d 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -2,7 +2,7 @@ use crate::consts::{ constant, constant_simple, Constant, Constant::{Int, F32, F64}, }; -use crate::utils::{get_parent_expr, higher, numeric_literal, span_lint_and_sugg, sugg, SpanlessEq}; +use crate::utils::{eq_expr_value, get_parent_expr, higher, numeric_literal, span_lint_and_sugg, sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp}; @@ -363,8 +363,8 @@ fn detect_hypot(cx: &LateContext<'_>, args: &[Expr<'_>]) -> Option { if_chain! { if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, ref lmul_lhs, ref lmul_rhs) = add_lhs.kind; if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, ref rmul_lhs, ref rmul_rhs) = add_rhs.kind; - if are_exprs_equal(cx, lmul_lhs, lmul_rhs); - if are_exprs_equal(cx, rmul_lhs, rmul_rhs); + if eq_expr_value(cx, lmul_lhs, lmul_rhs); + if eq_expr_value(cx, rmul_lhs, rmul_rhs); then { return Some(format!("{}.hypot({})", Sugg::hir(cx, &lmul_lhs, ".."), Sugg::hir(cx, &rmul_lhs, ".."))); } @@ -502,8 +502,8 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) { fn is_testing_positive(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool { if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind { match op { - BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, right) && are_exprs_equal(cx, left, test), - BinOpKind::Lt | BinOpKind::Le => is_zero(cx, left) && are_exprs_equal(cx, right, test), + BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, right) && eq_expr_value(cx, left, test), + BinOpKind::Lt | BinOpKind::Le => is_zero(cx, left) && eq_expr_value(cx, right, test), _ => false, } } else { @@ -515,8 +515,8 @@ fn is_testing_positive(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) - fn is_testing_negative(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool { if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind { match op { - BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, left) && are_exprs_equal(cx, right, test), - BinOpKind::Lt | BinOpKind::Le => is_zero(cx, right) && are_exprs_equal(cx, left, test), + BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, left) && eq_expr_value(cx, right, test), + BinOpKind::Lt | BinOpKind::Le => is_zero(cx, right) && eq_expr_value(cx, left, test), _ => false, } } else { @@ -524,10 +524,6 @@ fn is_testing_negative(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) - } } -fn are_exprs_equal(cx: &LateContext<'_>, expr1: &Expr<'_>, expr2: &Expr<'_>) -> bool { - SpanlessEq::new(cx).ignore_fn().eq_expr(expr1, expr2) -} - /// Returns true iff expr is some zero literal fn is_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { match constant_simple(cx, cx.typeck_results(), expr) { @@ -546,12 +542,12 @@ fn is_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { /// returns None. fn are_negated<'a>(cx: &LateContext<'_>, expr1: &'a Expr<'a>, expr2: &'a Expr<'a>) -> Option<(bool, &'a Expr<'a>)> { if let ExprKind::Unary(UnOp::UnNeg, expr1_negated) = &expr1.kind { - if are_exprs_equal(cx, expr1_negated, expr2) { + if eq_expr_value(cx, expr1_negated, expr2) { return Some((false, expr2)); } } if let ExprKind::Unary(UnOp::UnNeg, expr2_negated) = &expr2.kind { - if are_exprs_equal(cx, expr1, expr2_negated) { + if eq_expr_value(cx, expr1, expr2_negated) { return Some((true, expr1)); } } @@ -614,7 +610,7 @@ fn are_same_base_logs(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_> args_a.len() == args_b.len() && ( ["ln", "log2", "log10"].contains(&&*method_name_a.as_str()) || - method_name_a.as_str() == "log" && args_a.len() == 2 && are_exprs_equal(cx, &args_a[1], &args_b[1]) + method_name_a.as_str() == "log" && args_a.len() == 2 && eq_expr_value(cx, &args_a[1], &args_b[1]) ); } } diff --git a/clippy_lints/src/if_let_some_result.rs b/clippy_lints/src/if_let_some_result.rs index 5b22df5fe49..28b20cdeac3 100644 --- a/clippy_lints/src/if_let_some_result.rs +++ b/clippy_lints/src/if_let_some_result.rs @@ -61,8 +61,8 @@ impl<'tcx> LateLintPass<'tcx> for OkIfLet { cx, IF_LET_SOME_RESULT, expr.span.with_hi(op.span.hi()), - "Matching on `Some` with `ok()` is redundant", - &format!("Consider matching on `Ok({})` and removing the call to `ok` instead", some_expr_string), + "matching on `Some` with `ok()` is redundant", + &format!("consider matching on `Ok({})` and removing the call to `ok` instead", some_expr_string), sugg, applicability, ); diff --git a/clippy_lints/src/if_not_else.rs b/clippy_lints/src/if_not_else.rs index c11e291f98e..b86d2e76656 100644 --- a/clippy_lints/src/if_not_else.rs +++ b/clippy_lints/src/if_not_else.rs @@ -60,7 +60,7 @@ impl EarlyLintPass for IfNotElse { cx, IF_NOT_ELSE, item.span, - "Unnecessary boolean `not` operation", + "unnecessary boolean `not` operation", None, "remove the `!` and swap the blocks of the `if`/`else`", ); @@ -70,7 +70,7 @@ impl EarlyLintPass for IfNotElse { cx, IF_NOT_ELSE, item.span, - "Unnecessary `!=` operation", + "unnecessary `!=` operation", None, "change to `==` and swap the blocks of the `if`/`else`", ); diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index 5f931a0adde..b57fe8dc426 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -158,9 +158,9 @@ fn print_lint_and_sugg(cx: &LateContext<'_>, var_name: &str, expr: &Expr<'_>) { cx, IMPLICIT_SATURATING_SUB, expr.span, - "Implicitly performing saturating subtraction", + "implicitly performing saturating subtraction", "try", - format!("{} = {}.saturating_sub({});", var_name, var_name, 1.to_string()), + format!("{} = {}.saturating_sub({});", var_name, var_name, '1'), Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/inherent_impl.rs b/clippy_lints/src/inherent_impl.rs index 9fb10c7f627..4e6bb604785 100644 --- a/clippy_lints/src/inherent_impl.rs +++ b/clippy_lints/src/inherent_impl.rs @@ -81,9 +81,9 @@ impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl { cx, MULTIPLE_INHERENT_IMPL, *additional_span, - "Multiple implementations of this structure", + "multiple implementations of this structure", |diag| { - diag.span_note(*initial_span, "First implementation here"); + diag.span_note(*initial_span, "first implementation here"); }, ) }) diff --git a/clippy_lints/src/int_plus_one.rs b/clippy_lints/src/int_plus_one.rs index e91fb0c2f27..c629ee05ab9 100644 --- a/clippy_lints/src/int_plus_one.rs +++ b/clippy_lints/src/int_plus_one.rs @@ -152,7 +152,7 @@ impl IntPlusOne { cx, INT_PLUS_ONE, block.span, - "Unnecessary `>= y + 1` or `x - 1 >=`", + "unnecessary `>= y + 1` or `x - 1 >=`", "change it to", recommendation, Applicability::MachineApplicable, // snippet @@ -163,8 +163,8 @@ impl IntPlusOne { impl EarlyLintPass for IntPlusOne { fn check_expr(&mut self, cx: &EarlyContext<'_>, item: &Expr) { if let ExprKind::Binary(ref kind, ref lhs, ref rhs) = item.kind { - if let Some(ref rec) = Self::check_binop(cx, kind.node, lhs, rhs) { - Self::emit_warning(cx, item, rec.clone()); + if let Some(rec) = Self::check_binop(cx, kind.node, lhs, rhs) { + Self::emit_warning(cx, item, rec); } } } diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index e5daa30f8ca..b691d363d2f 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -1,4 +1,4 @@ -use crate::utils::{get_item_name, higher, snippet_with_applicability, span_lint, span_lint_and_sugg, walk_ptrs_ty}; +use crate::utils::{get_item_name, snippet_with_applicability, span_lint, span_lint_and_sugg, walk_ptrs_ty}; use rustc_ast::ast::LitKind; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -260,17 +260,6 @@ fn check_len( /// Checks if this type has an `is_empty` method. fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - /// Special case ranges until `range_is_empty` is stabilized. See issue 3807. - fn should_skip_range(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - higher::range(expr).map_or(false, |_| { - !cx.tcx - .features() - .declared_lib_features - .iter() - .any(|(name, _)| name.as_str() == "range_is_empty") - }) - } - /// Gets an `AssocItem` and return true if it matches `is_empty(self)`. fn is_is_empty(cx: &LateContext<'_>, item: &ty::AssocItem) -> bool { if let ty::AssocKind::Fn = item.kind { @@ -296,10 +285,6 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { }) } - if should_skip_range(cx, expr) { - return false; - } - let ty = &walk_ptrs_ty(cx.typeck_results().expr_ty(expr)); match ty.kind { ty::Dynamic(ref tt, ..) => tt.principal().map_or(false, |principal| { diff --git a/clippy_lints/src/let_and_return.rs b/clippy_lints/src/let_and_return.rs deleted file mode 100644 index fa560ffb980..00000000000 --- a/clippy_lints/src/let_and_return.rs +++ /dev/null @@ -1,124 +0,0 @@ -use if_chain::if_chain; -use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; -use rustc_hir::{Block, Expr, ExprKind, PatKind, StmtKind}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::hir::map::Map; -use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::subst::GenericArgKind; -use rustc_session::{declare_lint_pass, declare_tool_lint}; - -use crate::utils::{fn_def_id, in_macro, match_qpath, snippet_opt, span_lint_and_then}; - -declare_clippy_lint! { - /// **What it does:** Checks for `let`-bindings, which are subsequently - /// returned. - /// - /// **Why is this bad?** It is just extraneous code. Remove it to make your code - /// more rusty. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// fn foo() -> String { - /// let x = String::new(); - /// x - /// } - /// ``` - /// instead, use - /// ``` - /// fn foo() -> String { - /// String::new() - /// } - /// ``` - pub LET_AND_RETURN, - style, - "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block" -} - -declare_lint_pass!(LetReturn => [LET_AND_RETURN]); - -impl<'tcx> LateLintPass<'tcx> for LetReturn { - fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) { - // we need both a let-binding stmt and an expr - if_chain! { - if let Some(retexpr) = block.expr; - if let Some(stmt) = block.stmts.iter().last(); - if let StmtKind::Local(local) = &stmt.kind; - if local.ty.is_none(); - if local.attrs.is_empty(); - if let Some(initexpr) = &local.init; - if let PatKind::Binding(.., ident, _) = local.pat.kind; - if let ExprKind::Path(qpath) = &retexpr.kind; - if match_qpath(qpath, &[&*ident.name.as_str()]); - if !last_statement_borrows(cx, initexpr); - if !in_external_macro(cx.sess(), initexpr.span); - if !in_external_macro(cx.sess(), retexpr.span); - if !in_external_macro(cx.sess(), local.span); - if !in_macro(local.span); - then { - span_lint_and_then( - cx, - LET_AND_RETURN, - retexpr.span, - "returning the result of a `let` binding from a block", - |err| { - err.span_label(local.span, "unnecessary `let` binding"); - - if let Some(snippet) = snippet_opt(cx, initexpr.span) { - err.multipart_suggestion( - "return the expression directly", - vec![ - (local.span, String::new()), - (retexpr.span, snippet), - ], - Applicability::MachineApplicable, - ); - } else { - err.span_help(initexpr.span, "this expression can be directly returned"); - } - }, - ); - } - } - } -} - -fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { - let mut visitor = BorrowVisitor { cx, borrows: false }; - walk_expr(&mut visitor, expr); - visitor.borrows -} - -struct BorrowVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - borrows: bool, -} - -impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> { - type Map = Map<'tcx>; - - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if self.borrows { - return; - } - - if let Some(def_id) = fn_def_id(self.cx, expr) { - self.borrows = self - .cx - .tcx - .fn_sig(def_id) - .output() - .skip_binder() - .walk() - .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_))); - } - - walk_expr(self, expr); - } - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } -} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index aa100263640..577ce6523b4 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -193,6 +193,7 @@ mod excessive_bools; mod exit; mod explicit_write; mod fallible_impl_from; +mod float_equality_without_abs; mod float_literal; mod floating_point_arithmetic; mod format; @@ -218,7 +219,6 @@ mod large_const_arrays; mod large_enum_variant; mod large_stack_arrays; mod len_zero; -mod let_and_return; mod let_if_seq; mod let_underscore; mod lifetimes; @@ -285,6 +285,7 @@ mod reference; mod regex; mod repeat_once; mod returns; +mod self_assignment; mod serde_api; mod shadow; mod single_component_path_imports; @@ -296,6 +297,7 @@ mod swap; mod tabs_in_doc_comments; mod temporary_assignment; mod to_digit_is_some; +mod to_string_in_display; mod trait_bounds; mod transmute; mod transmuting_null; @@ -310,7 +312,9 @@ mod unnested_or_patterns; mod unsafe_removed_from_name; mod unused_io_amount; mod unused_self; +mod unused_unit; mod unwrap; +mod unwrap_in_result; mod use_self; mod useless_conversion; mod vec; @@ -547,6 +551,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &exit::EXIT, &explicit_write::EXPLICIT_WRITE, &fallible_impl_from::FALLIBLE_IMPL_FROM, + &float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS, &float_literal::EXCESSIVE_PRECISION, &float_literal::LOSSY_FLOAT_LITERAL, &floating_point_arithmetic::IMPRECISE_FLOPS, @@ -586,7 +591,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &large_stack_arrays::LARGE_STACK_ARRAYS, &len_zero::LEN_WITHOUT_IS_EMPTY, &len_zero::LEN_ZERO, - &let_and_return::LET_AND_RETURN, &let_if_seq::USELESS_LET_IF_SEQ, &let_underscore::LET_UNDERSCORE_LOCK, &let_underscore::LET_UNDERSCORE_MUST_USE, @@ -677,6 +681,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::SEARCH_IS_SOME, &methods::SHOULD_IMPLEMENT_TRAIT, &methods::SINGLE_CHAR_PATTERN, + &methods::SINGLE_CHAR_PUSH_STR, &methods::SKIP_WHILE_NEXT, &methods::STRING_EXTEND_CHARS, &methods::SUSPICIOUS_MAP, @@ -684,6 +689,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::UNINIT_ASSUMED_INIT, &methods::UNNECESSARY_FILTER_MAP, &methods::UNNECESSARY_FOLD, + &methods::UNNECESSARY_LAZY_EVALUATIONS, &methods::UNWRAP_USED, &methods::USELESS_ASREF, &methods::WRONG_PUB_SELF_CONVENTION, @@ -769,8 +775,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ®ex::INVALID_REGEX, ®ex::TRIVIAL_REGEX, &repeat_once::REPEAT_ONCE, + &returns::LET_AND_RETURN, &returns::NEEDLESS_RETURN, - &returns::UNUSED_UNIT, + &self_assignment::SELF_ASSIGNMENT, &serde_api::SERDE_API_MISUSE, &shadow::SHADOW_REUSE, &shadow::SHADOW_SAME, @@ -788,6 +795,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &tabs_in_doc_comments::TABS_IN_DOC_COMMENTS, &temporary_assignment::TEMPORARY_ASSIGNMENT, &to_digit_is_some::TO_DIGIT_IS_SOME, + &to_string_in_display::TO_STRING_IN_DISPLAY, &trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS, &trait_bounds::TYPE_REPETITION_IN_BOUNDS, &transmute::CROSSPOINTER_TRANSMUTE, @@ -840,8 +848,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME, &unused_io_amount::UNUSED_IO_AMOUNT, &unused_self::UNUSED_SELF, + &unused_unit::UNUSED_UNIT, &unwrap::PANICKING_UNWRAP, &unwrap::UNNECESSARY_UNWRAP, + &unwrap_in_result::UNWRAP_IN_RESULT, &use_self::USE_SELF, &useless_conversion::USELESS_CONVERSION, &utils::internal_lints::CLIPPY_LINTS_INTERNAL, @@ -930,11 +940,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box cognitive_complexity::CognitiveComplexity::new(cognitive_complexity_threshold)); let too_large_for_stack = conf.too_large_for_stack; store.register_late_pass(move || box escape::BoxedLocal{too_large_for_stack}); + store.register_late_pass(move || box vec::UselessVec{too_large_for_stack}); store.register_late_pass(|| box panic_unimplemented::PanicUnimplemented); store.register_late_pass(|| box strings::StringLitAsBytes); store.register_late_pass(|| box derive::Derive); store.register_late_pass(|| box types::CharLitAsU8); - store.register_late_pass(|| box vec::UselessVec); store.register_late_pass(|| box drop_bounds::DropBounds); store.register_late_pass(|| box get_last_with_len::GetLastWithLen); store.register_late_pass(|| box drop_forget_ref::DropForgetRef); @@ -1017,6 +1027,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box reference::DerefAddrOf); store.register_early_pass(|| box reference::RefInDeref); store.register_early_pass(|| box double_parens::DoubleParens); + store.register_late_pass(|| box to_string_in_display::ToStringInDisplay::new()); store.register_early_pass(|| box unsafe_removed_from_name::UnsafeNameRemoval); store.register_early_pass(|| box if_not_else::IfNotElse); store.register_early_pass(|| box else_if_without_else::ElseIfWithoutElse); @@ -1025,8 +1036,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box misc_early::MiscEarlyLints); store.register_early_pass(|| box redundant_closure_call::RedundantClosureCall); store.register_late_pass(|| box redundant_closure_call::RedundantClosureCall); - store.register_early_pass(|| box returns::Return); - store.register_late_pass(|| box let_and_return::LetReturn); + store.register_early_pass(|| box unused_unit::UnusedUnit); + store.register_late_pass(|| box returns::Return); store.register_early_pass(|| box collapsible_if::CollapsibleIf); store.register_early_pass(|| box items_after_statements::ItemsAfterStatements); store.register_early_pass(|| box precedence::Precedence); @@ -1085,6 +1096,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box pattern_type_mismatch::PatternTypeMismatch); store.register_late_pass(|| box stable_sort_primitive::StableSortPrimitive); store.register_late_pass(|| box repeat_once::RepeatOnce); + store.register_late_pass(|| box unwrap_in_result::UnwrapInResult); + store.register_late_pass(|| box self_assignment::SelfAssignment); + store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1122,6 +1136,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&shadow::SHADOW_REUSE), LintId::of(&shadow::SHADOW_SAME), LintId::of(&strings::STRING_ADD), + LintId::of(&unwrap_in_result::UNWRAP_IN_RESULT), LintId::of(&verbose_file_reads::VERBOSE_FILE_READS), LintId::of(&write::PRINT_STDOUT), LintId::of(&write::USE_DEBUG), @@ -1260,6 +1275,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&eval_order_dependence::DIVERGING_SUB_EXPRESSION), LintId::of(&eval_order_dependence::EVAL_ORDER_DEPENDENCE), LintId::of(&explicit_write::EXPLICIT_WRITE), + LintId::of(&float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS), LintId::of(&float_literal::EXCESSIVE_PRECISION), LintId::of(&format::USELESS_FORMAT), LintId::of(&formatting::POSSIBLE_MISSING_COMMA), @@ -1284,7 +1300,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&large_enum_variant::LARGE_ENUM_VARIANT), LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY), LintId::of(&len_zero::LEN_ZERO), - LintId::of(&let_and_return::LET_AND_RETURN), LintId::of(&let_underscore::LET_UNDERSCORE_LOCK), LintId::of(&lifetimes::EXTRA_UNUSED_LIFETIMES), LintId::of(&lifetimes::NEEDLESS_LIFETIMES), @@ -1349,6 +1364,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::SEARCH_IS_SOME), LintId::of(&methods::SHOULD_IMPLEMENT_TRAIT), LintId::of(&methods::SINGLE_CHAR_PATTERN), + LintId::of(&methods::SINGLE_CHAR_PUSH_STR), LintId::of(&methods::SKIP_WHILE_NEXT), LintId::of(&methods::STRING_EXTEND_CHARS), LintId::of(&methods::SUSPICIOUS_MAP), @@ -1356,6 +1372,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::UNINIT_ASSUMED_INIT), LintId::of(&methods::UNNECESSARY_FILTER_MAP), LintId::of(&methods::UNNECESSARY_FOLD), + LintId::of(&methods::UNNECESSARY_LAZY_EVALUATIONS), LintId::of(&methods::USELESS_ASREF), LintId::of(&methods::WRONG_SELF_CONVENTION), LintId::of(&methods::ZST_OFFSET), @@ -1413,8 +1430,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(®ex::INVALID_REGEX), LintId::of(®ex::TRIVIAL_REGEX), LintId::of(&repeat_once::REPEAT_ONCE), + LintId::of(&returns::LET_AND_RETURN), LintId::of(&returns::NEEDLESS_RETURN), - LintId::of(&returns::UNUSED_UNIT), + LintId::of(&self_assignment::SELF_ASSIGNMENT), LintId::of(&serde_api::SERDE_API_MISUSE), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), @@ -1427,6 +1445,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), LintId::of(&temporary_assignment::TEMPORARY_ASSIGNMENT), LintId::of(&to_digit_is_some::TO_DIGIT_IS_SOME), + LintId::of(&to_string_in_display::TO_STRING_IN_DISPLAY), LintId::of(&transmute::CROSSPOINTER_TRANSMUTE), LintId::of(&transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS), LintId::of(&transmute::TRANSMUTE_BYTES_TO_STR), @@ -1460,6 +1479,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), + LintId::of(&unused_unit::UNUSED_UNIT), LintId::of(&unwrap::PANICKING_UNWRAP), LintId::of(&unwrap::UNNECESSARY_UNWRAP), LintId::of(&useless_conversion::USELESS_CONVERSION), @@ -1500,7 +1520,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&inherent_to_string::INHERENT_TO_STRING), LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY), LintId::of(&len_zero::LEN_ZERO), - LintId::of(&let_and_return::LET_AND_RETURN), LintId::of(&literal_representation::INCONSISTENT_DIGIT_GROUPING), LintId::of(&loops::EMPTY_LOOP), LintId::of(&loops::FOR_KV_MAP), @@ -1532,8 +1551,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::OPTION_MAP_OR_NONE), LintId::of(&methods::RESULT_MAP_OR_INTO_OPTION), LintId::of(&methods::SHOULD_IMPLEMENT_TRAIT), + LintId::of(&methods::SINGLE_CHAR_PUSH_STR), LintId::of(&methods::STRING_EXTEND_CHARS), LintId::of(&methods::UNNECESSARY_FOLD), + LintId::of(&methods::UNNECESSARY_LAZY_EVALUATIONS), LintId::of(&methods::WRONG_SELF_CONVENTION), LintId::of(&misc::TOPLEVEL_REF_ARG), LintId::of(&misc::ZERO_PTR), @@ -1554,8 +1575,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES), LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), LintId::of(®ex::TRIVIAL_REGEX), + LintId::of(&returns::LET_AND_RETURN), LintId::of(&returns::NEEDLESS_RETURN), - LintId::of(&returns::UNUSED_UNIT), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&strings::STRING_LIT_AS_BYTES), LintId::of(&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), @@ -1564,6 +1585,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::FN_TO_NUMERIC_CAST), LintId::of(&types::FN_TO_NUMERIC_CAST_WITH_TRUNCATION), LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), + LintId::of(&unused_unit::UNUSED_UNIT), LintId::of(&write::PRINTLN_EMPTY_STRING), LintId::of(&write::PRINT_LITERAL), LintId::of(&write::PRINT_WITH_NEWLINE), @@ -1672,6 +1694,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT), LintId::of(&eq_op::EQ_OP), LintId::of(&erasing_op::ERASING_OP), + LintId::of(&float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS), LintId::of(&formatting::POSSIBLE_MISSING_COMMA), LintId::of(&functions::NOT_UNSAFE_PTR_ARG_DEREF), LintId::of(&if_let_mutex::IF_LET_MUTEX), @@ -1704,10 +1727,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&ptr::MUT_FROM_REF), LintId::of(&ranges::REVERSED_EMPTY_RANGES), LintId::of(®ex::INVALID_REGEX), + LintId::of(&self_assignment::SELF_ASSIGNMENT), LintId::of(&serde_api::SERDE_API_MISUSE), LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), LintId::of(&swap::ALMOST_SWAPPED), + LintId::of(&to_string_in_display::TO_STRING_IN_DISPLAY), LintId::of(&transmute::UNSOUND_COLLECTION_TRANSMUTE), LintId::of(&transmute::WRONG_TRANSMUTE), LintId::of(&transmuting_null::TRANSMUTING_NULL), diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 8ffcd417d1d..c95e43a9430 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1141,11 +1141,31 @@ fn detect_same_item_push<'tcx>( if same_item_push_visitor.should_lint { if let Some((vec, pushed_item)) = same_item_push_visitor.vec_push { // Make sure that the push does not involve possibly mutating values - if mutated_variables(pushed_item, cx).map_or(false, |mutvars| mutvars.is_empty()) { - if let PatKind::Wild = pat.kind { - let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); - let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); - + if let PatKind::Wild = pat.kind { + let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); + let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); + if let ExprKind::Path(ref qpath) = pushed_item.kind { + if_chain! { + if let Res::Local(hir_id) = qpath_res(cx, qpath, pushed_item.hir_id); + let node = cx.tcx.hir().get(hir_id); + if let Node::Binding(pat) = node; + if let PatKind::Binding(bind_ann, ..) = pat.kind; + if !matches!(bind_ann, BindingAnnotation::RefMut | BindingAnnotation::Mutable); + then { + span_lint_and_help( + cx, + SAME_ITEM_PUSH, + vec.span, + "it looks like the same item is being pushed into this Vec", + None, + &format!( + "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", + item_str, vec_str, item_str + ), + ) + } + } + } else if mutated_variables(pushed_item, cx).map_or(false, |mutvars| mutvars.is_empty()) { span_lint_and_help( cx, SAME_ITEM_PUSH, diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs index 641e6a17043..1cd5b201292 100644 --- a/clippy_lints/src/map_clone.rs +++ b/clippy_lints/src/map_clone.rs @@ -111,8 +111,8 @@ fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) { cx, MAP_CLONE, root.trim_start(receiver).unwrap(), - "You are needlessly cloning iterator elements", - "Remove the `map` call", + "you are needlessly cloning iterator elements", + "remove the `map` call", String::new(), Applicability::MachineApplicable, ) @@ -125,8 +125,8 @@ fn lint(cx: &LateContext<'_>, replace: Span, root: Span, copied: bool) { cx, MAP_CLONE, replace, - "You are using an explicit closure for copying elements", - "Consider calling the dedicated `copied` method", + "you are using an explicit closure for copying elements", + "consider calling the dedicated `copied` method", format!( "{}.copied()", snippet_with_applicability(cx, root, "..", &mut applicability) @@ -138,8 +138,8 @@ fn lint(cx: &LateContext<'_>, replace: Span, root: Span, copied: bool) { cx, MAP_CLONE, replace, - "You are using an explicit closure for cloning elements", - "Consider calling the dedicated `cloned` method", + "you are using an explicit closure for cloning elements", + "consider calling the dedicated `cloned` method", format!( "{}.cloned()", snippet_with_applicability(cx, root, "..", &mut applicability) diff --git a/clippy_lints/src/match_on_vec_items.rs b/clippy_lints/src/match_on_vec_items.rs index faa20687ef6..57966452253 100644 --- a/clippy_lints/src/match_on_vec_items.rs +++ b/clippy_lints/src/match_on_vec_items.rs @@ -1,5 +1,5 @@ -use crate::utils::{is_type_diagnostic_item, is_type_lang_item, snippet, span_lint_and_sugg}; use crate::utils::walk_ptrs_ty; +use crate::utils::{is_type_diagnostic_item, is_type_lang_item, snippet, span_lint_and_sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, MatchSource}; diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 2265a188855..9996df69470 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3,6 +3,7 @@ mod inefficient_to_string; mod manual_saturating_arithmetic; mod option_map_unwrap_or; mod unnecessary_filter_map; +mod unnecessary_lazy_eval; use std::borrow::Cow; use std::fmt; @@ -14,11 +15,11 @@ use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::{TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass, Lint, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::subst::GenericArgKind; -use rustc_middle::ty::{self, Ty, TyS}; +use rustc_middle::ty::{self, TraitRef, Ty, TyS}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::symbol::{sym, SymbolStr}; @@ -26,12 +27,12 @@ use rustc_span::symbol::{sym, SymbolStr}; use crate::consts::{constant, Constant}; use crate::utils::usage::mutated_variables; use crate::utils::{ - get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, in_macro, is_copy, - is_ctor_or_promotable_const_function, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, - match_def_path, match_qpath, match_trait_method, match_type, match_var, method_calls, method_chain_args, paths, - remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, - span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty, - walk_ptrs_ty_depth, SpanlessEq, + contains_ty, get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, in_macro, + is_copy, is_ctor_or_promotable_const_function, is_expn_of, is_type_diagnostic_item, iter_input_pats, + last_path_segment, match_def_path, match_qpath, match_trait_method, match_type, match_var, method_calls, + method_chain_args, paths, remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability, + snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, + span_lint_and_then, sugg, walk_ptrs_ty, walk_ptrs_ty_depth, SpanlessEq, }; declare_clippy_lint! { @@ -723,6 +724,7 @@ declare_clippy_lint! { /// **Known problems:** None. /// /// **Example:** + /// In an impl block: /// ```rust /// # struct Foo; /// # struct NotAFoo; @@ -735,25 +737,40 @@ declare_clippy_lint! { /// /// ```rust /// # struct Foo; - /// # struct FooError; + /// struct Bar(Foo); /// impl Foo { - /// // Good. Return type contains `Self` - /// fn new() -> Result { - /// # Ok(Foo) + /// // Bad. The type name must contain `Self` + /// fn new() -> Bar { + /// # Bar(Foo) /// } /// } /// ``` /// /// ```rust /// # struct Foo; - /// struct Bar(Foo); + /// # struct FooError; /// impl Foo { - /// // Bad. The type name must contain `Self`. - /// fn new() -> Bar { - /// # Bar(Foo) + /// // Good. Return type contains `Self` + /// fn new() -> Result { + /// # Ok(Foo) /// } /// } /// ``` + /// + /// Or in a trait definition: + /// ```rust + /// pub trait Trait { + /// // Bad. The type name must contain `Self` + /// fn new(); + /// } + /// ``` + /// + /// ```rust + /// pub trait Trait { + /// // Good. Return type contains `Self` + /// fn new() -> Self; + /// } + /// ``` pub NEW_RET_NO_SELF, style, "not returning type containing `Self` in a `new` method" @@ -799,7 +816,7 @@ declare_clippy_lint! { /// call_some_ffi_func(c_str); /// } /// ``` - /// Here `c_str` point to a freed address. The correct use would be: + /// Here `c_str` points to a freed address. The correct use would be: /// ```rust /// # use std::ffi::CString; /// # fn call_some_ffi_func(_: *const i8) {} @@ -1306,6 +1323,65 @@ declare_clippy_lint! { "using `.iter().next()` on a sliced array, which can be shortened to just `.get()`" } +declare_clippy_lint! { + /// **What it does:** Warns when using push_str with a single-character string literal, + /// and push with a char would work fine. + /// + /// **Why is this bad?** It's less clear that we are pushing a single character + /// + /// **Known problems:** None + /// + /// **Example:** + /// ``` + /// let mut string = String::new(); + /// string.push_str("R"); + /// ``` + /// Could be written as + /// ``` + /// let mut string = String::new(); + /// string.push('R'); + /// ``` + pub SINGLE_CHAR_PUSH_STR, + style, + "`push_str()` used with a single-character string literal as parameter" +} + +declare_clippy_lint! { + /// **What it does:** As the counterpart to `or_fun_call`, this lint looks for unnecessary + /// lazily evaluated closures on `Option` and `Result`. + /// + /// This lint suggests changing the following functions, when eager evaluation results in + /// simpler code: + /// - `unwrap_or_else` to `unwrap_or` + /// - `and_then` to `and` + /// - `or_else` to `or` + /// - `get_or_insert_with` to `get_or_insert` + /// - `ok_or_else` to `ok_or` + /// + /// **Why is this bad?** Using eager evaluation is shorter and simpler in some cases. + /// + /// **Known problems:** It is possible, but not recommended for `Deref` and `Index` to have + /// side effects. Eagerly evaluating them can change the semantics of the program. + /// + /// **Example:** + /// + /// ```rust + /// // example code where clippy issues a warning + /// let opt: Option = None; + /// + /// opt.unwrap_or_else(|| 42); + /// ``` + /// Use instead: + /// ```rust + /// let opt: Option = None; + /// + /// opt.unwrap_or(42); + /// ``` + pub UNNECESSARY_LAZY_EVALUATIONS, + style, + "using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation" +} + declare_lint_pass!(Methods => [ UNWRAP_USED, EXPECT_USED, @@ -1327,6 +1403,7 @@ declare_lint_pass!(Methods => [ INEFFICIENT_TO_STRING, NEW_RET_NO_SELF, SINGLE_CHAR_PATTERN, + SINGLE_CHAR_PUSH_STR, SEARCH_IS_SOME, TEMPORARY_CSTRING_AS_PTR, FILTER_NEXT, @@ -1354,6 +1431,7 @@ declare_lint_pass!(Methods => [ ZST_OFFSET, FILETYPE_IS_FILE, OPTION_AS_REF_DEREF, + UNNECESSARY_LAZY_EVALUATIONS, ]); impl<'tcx> LateLintPass<'tcx> for Methods { @@ -1374,13 +1452,19 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["expect", "ok"] => lint_ok_expect(cx, expr, arg_lists[1]), ["expect", ..] => lint_expect(cx, expr, arg_lists[0]), ["unwrap_or", "map"] => option_map_unwrap_or::lint(cx, expr, arg_lists[1], arg_lists[0], method_spans[1]), - ["unwrap_or_else", "map"] => lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]), + ["unwrap_or_else", "map"] => { + if !lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]) { + unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "unwrap_or"); + } + }, ["map_or", ..] => lint_map_or_none(cx, expr, arg_lists[0]), ["and_then", ..] => { + unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], false, "and"); bind_instead_of_map::OptionAndThenSome::lint(cx, expr, arg_lists[0]); bind_instead_of_map::ResultAndThenOk::lint(cx, expr, arg_lists[0]); }, ["or_else", ..] => { + unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], false, "or"); bind_instead_of_map::ResultOrElseErrInfo::lint(cx, expr, arg_lists[0]); }, ["next", "filter"] => lint_filter_next(cx, expr, arg_lists[1]), @@ -1424,6 +1508,9 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["is_file", ..] => lint_filetype_is_file(cx, expr, arg_lists[0]), ["map", "as_ref"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], false), ["map", "as_mut"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], true), + ["unwrap_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "unwrap_or"), + ["get_or_insert_with", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "get_or_insert"), + ["ok_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "ok_or"), _ => {}, } @@ -1441,6 +1528,12 @@ impl<'tcx> LateLintPass<'tcx> for Methods { inefficient_to_string::lint(cx, expr, &args[0], self_ty); } + if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { + if match_def_path(cx, fn_def_id, &paths::PUSH_STR) { + lint_single_char_push_string(cx, expr, args); + } + } + match self_ty.kind { ty::Ref(_, ty, _) if ty.kind == ty::Str => { for &(method, pos) in &PATTERN_METHODS { @@ -1470,6 +1563,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } } + #[allow(clippy::too_many_lines)] fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { if in_external_macro(cx.sess(), impl_item.span) { return; @@ -1495,16 +1589,31 @@ impl<'tcx> LateLintPass<'tcx> for Methods { then { if cx.access_levels.is_exported(impl_item.hir_id) { - // check missing trait implementations - for &(method_name, n_args, fn_header, self_kind, out_type, trait_name) in &TRAIT_METHODS { - if name == method_name && - sig.decl.inputs.len() == n_args && - out_type.matches(cx, &sig.decl.output) && - self_kind.matches(cx, self_ty, first_arg_ty) && - fn_header_equals(*fn_header, sig.header) { - span_lint(cx, SHOULD_IMPLEMENT_TRAIT, impl_item.span, &format!( - "defining a method called `{}` on this type; consider implementing \ - the `{}` trait or choosing a less ambiguous name", name, trait_name)); + // check missing trait implementations + for method_config in &TRAIT_METHODS { + if name == method_config.method_name && + sig.decl.inputs.len() == method_config.param_count && + method_config.output_type.matches(cx, &sig.decl.output) && + method_config.self_kind.matches(cx, self_ty, first_arg_ty) && + fn_header_equals(method_config.fn_header, sig.header) && + method_config.lifetime_param_cond(&impl_item) + { + span_lint_and_help( + cx, + SHOULD_IMPLEMENT_TRAIT, + impl_item.span, + &format!( + "method `{}` can be confused for the standard trait method `{}::{}`", + method_config.method_name, + method_config.trait_name, + method_config.method_name + ), + None, + &format!( + "consider implementing the trait `{}` or choosing a less ambiguous method name", + method_config.trait_name + ) + ); } } } @@ -1538,19 +1647,16 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } } + // if this impl block implements a trait, lint in trait definition instead + if let hir::ItemKind::Impl { of_trait: Some(_), .. } = item.kind { + return; + } + if let hir::ImplItemKind::Fn(_, _) = impl_item.kind { let ret_ty = return_ty(cx, impl_item.hir_id); - let contains_self_ty = |ty: Ty<'tcx>| { - ty.walk().any(|inner| match inner.unpack() { - GenericArgKind::Type(inner_ty) => TyS::same_type(self_ty, inner_ty), - - GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, - }) - }; - // walk the return type and check for Self (this does not check associated types) - if contains_self_ty(ret_ty) { + if contains_ty(ret_ty, self_ty) { return; } @@ -1560,7 +1666,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { for &(predicate, _span) in cx.tcx.predicates_of(def_id).predicates { if let ty::PredicateAtom::Projection(projection_predicate) = predicate.skip_binders() { // walk the associated type and check for Self - if contains_self_ty(projection_predicate.ty) { + if contains_ty(projection_predicate.ty, self_ty) { return; } } @@ -1577,6 +1683,26 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } } } + + fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { + if_chain! { + if !in_external_macro(cx.tcx.sess, item.span); + if item.ident.name == sym!(new); + if let TraitItemKind::Fn(_, _) = item.kind; + let ret_ty = return_ty(cx, item.hir_id); + let self_ty = TraitRef::identity(cx.tcx, item.hir_id.owner.to_def_id()).self_ty(); + if !contains_ty(ret_ty, self_ty); + + then { + span_lint( + cx, + NEW_RET_NO_SELF, + item.span, + "methods called `new` usually return `Self`", + ); + } + } + } } /// Checks for the `OR_FUN_CALL` lint. @@ -2057,18 +2183,15 @@ fn lint_clone_on_ref_ptr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir:: return; }; + let snippet = snippet_with_macro_callsite(cx, arg.span, "_"); + span_lint_and_sugg( cx, CLONE_ON_REF_PTR, expr.span, "using `.clone()` on a ref-counted pointer", "try this", - format!( - "{}::<{}>::clone(&{})", - caller_type, - subst.type_at(0), - snippet(cx, arg.span, "_") - ), + format!("{}::<{}>::clone(&{})", caller_type, subst.type_at(0), snippet), Applicability::Unspecified, // Sometimes unnecessary ::<_> after Rc/Arc/Weak ); } @@ -2280,7 +2403,7 @@ fn lint_iter_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, iter_ cx, ITER_NEXT_SLICE, expr.span, - "Using `.iter().next()` on a Slice without end index.", + "using `.iter().next()` on a Slice without end index", "try calling", format!("{}.get({})", snippet_with_applicability(cx, caller_var.span, "..", &mut applicability), start_idx), applicability, @@ -2299,7 +2422,7 @@ fn lint_iter_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, iter_ cx, ITER_NEXT_SLICE, expr.span, - "Using `.iter().next()` on an array", + "using `.iter().next()` on an array", "try calling", format!( "{}.get(0)", @@ -2618,12 +2741,13 @@ fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map } /// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s +/// Return true if lint triggered fn lint_map_unwrap_or_else<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map_args: &'tcx [hir::Expr<'_>], unwrap_args: &'tcx [hir::Expr<'_>], -) { +) -> bool { // lint if the caller of `map()` is an `Option` let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym!(option_type)); let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym!(result_type)); @@ -2635,10 +2759,10 @@ fn lint_map_unwrap_or_else<'tcx>( let unwrap_mutated_vars = mutated_variables(&unwrap_args[1], cx); if let (Some(map_mutated_vars), Some(unwrap_mutated_vars)) = (map_mutated_vars, unwrap_mutated_vars) { if map_mutated_vars.intersection(&unwrap_mutated_vars).next().is_some() { - return; + return false; } } else { - return; + return false; } // lint message @@ -2668,10 +2792,14 @@ fn lint_map_unwrap_or_else<'tcx>( map_snippet, unwrap_snippet, ), ); + return true; } else if same_span && multiline { span_lint(cx, MAP_UNWRAP_OR, expr.span, msg); - }; + return true; + } } + + false } /// lint use of `_.map_or(None, _)` for `Option`s and `Result`s @@ -3124,15 +3252,18 @@ fn lint_chars_last_cmp_with_unwrap<'tcx>(cx: &LateContext<'tcx>, info: &BinaryEx } } -/// lint for length-1 `str`s for methods in `PATTERN_METHODS` -fn lint_single_char_pattern<'tcx>(cx: &LateContext<'tcx>, _expr: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>) { +fn get_hint_if_single_char_arg( + cx: &LateContext<'_>, + arg: &hir::Expr<'_>, + applicability: &mut Applicability, +) -> Option { if_chain! { if let hir::ExprKind::Lit(lit) = &arg.kind; if let ast::LitKind::Str(r, style) = lit.node; - if r.as_str().len() == 1; + let string = r.as_str(); + if string.len() == 1; then { - let mut applicability = Applicability::MachineApplicable; - let snip = snippet_with_applicability(cx, arg.span, "..", &mut applicability); + let snip = snippet_with_applicability(cx, arg.span, &string, applicability); let ch = if let ast::StrStyle::Raw(nhash) = style { let nhash = nhash as usize; // for raw string: r##"a"## @@ -3142,19 +3273,47 @@ fn lint_single_char_pattern<'tcx>(cx: &LateContext<'tcx>, _expr: &'tcx hir::Expr &snip[1..(snip.len() - 1)] }; let hint = format!("'{}'", if ch == "'" { "\\'" } else { ch }); - span_lint_and_sugg( - cx, - SINGLE_CHAR_PATTERN, - arg.span, - "single-character string constant used as pattern", - "try using a `char` instead", - hint, - applicability, - ); + Some(hint) + } else { + None } } } +/// lint for length-1 `str`s for methods in `PATTERN_METHODS` +fn lint_single_char_pattern(cx: &LateContext<'_>, _expr: &hir::Expr<'_>, arg: &hir::Expr<'_>) { + let mut applicability = Applicability::MachineApplicable; + if let Some(hint) = get_hint_if_single_char_arg(cx, arg, &mut applicability) { + span_lint_and_sugg( + cx, + SINGLE_CHAR_PATTERN, + arg.span, + "single-character string constant used as pattern", + "try using a `char` instead", + hint, + applicability, + ); + } +} + +/// lint for length-1 `str`s as argument for `push_str` +fn lint_single_char_push_string(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { + let mut applicability = Applicability::MachineApplicable; + if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[1], &mut applicability) { + let base_string_snippet = snippet_with_applicability(cx, args[0].span, "_", &mut applicability); + let sugg = format!("{}.push({})", base_string_snippet, extension_string); + span_lint_and_sugg( + cx, + SINGLE_CHAR_PUSH_STR, + expr.span, + "calling `push_str()` using a single-character string literal", + "consider using `push` with a character literal", + sugg, + applicability, + ); + } +} + /// Checks for the `USELESS_ASREF` lint. fn lint_asref(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, as_ref_args: &[hir::Expr<'_>]) { // when we get here, we've already checked that the call name is "as_ref" or "as_mut" @@ -3292,7 +3451,12 @@ fn lint_option_as_ref_deref<'tcx>( ]; let is_deref = match map_args[1].kind { - hir::ExprKind::Path(ref expr_qpath) => deref_aliases.iter().any(|path| match_qpath(expr_qpath, path)), + hir::ExprKind::Path(ref expr_qpath) => cx + .qpath_res(expr_qpath, map_args[1].hir_id) + .opt_def_id() + .map_or(false, |fun_def_id| { + deref_aliases.iter().any(|path| match_def_path(cx, fun_def_id, path)) + }), hir::ExprKind::Closure(_, _, body_id, _, _) => { let closure_body = cx.tcx.hir().body(body_id); let closure_expr = remove_blocks(&closure_body.value); @@ -3403,38 +3567,85 @@ const FN_HEADER: hir::FnHeader = hir::FnHeader { abi: rustc_target::spec::abi::Abi::Rust, }; +struct ShouldImplTraitCase { + trait_name: &'static str, + method_name: &'static str, + param_count: usize, + fn_header: hir::FnHeader, + // implicit self kind expected (none, self, &self, ...) + self_kind: SelfKind, + // checks against the output type + output_type: OutType, + // certain methods with explicit lifetimes can't implement the equivalent trait method + lint_explicit_lifetime: bool, +} +impl ShouldImplTraitCase { + const fn new( + trait_name: &'static str, + method_name: &'static str, + param_count: usize, + fn_header: hir::FnHeader, + self_kind: SelfKind, + output_type: OutType, + lint_explicit_lifetime: bool, + ) -> ShouldImplTraitCase { + ShouldImplTraitCase { + trait_name, + method_name, + param_count, + fn_header, + self_kind, + output_type, + lint_explicit_lifetime, + } + } + + fn lifetime_param_cond(&self, impl_item: &hir::ImplItem<'_>) -> bool { + self.lint_explicit_lifetime + || !impl_item.generics.params.iter().any(|p| { + matches!( + p.kind, + hir::GenericParamKind::Lifetime { + kind: hir::LifetimeParamKind::Explicit + } + ) + }) + } +} + #[rustfmt::skip] -const TRAIT_METHODS: [(&str, usize, &hir::FnHeader, SelfKind, OutType, &str); 30] = [ - ("add", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Add"), - ("as_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, "std::convert::AsMut"), - ("as_ref", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, "std::convert::AsRef"), - ("bitand", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::BitAnd"), - ("bitor", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::BitOr"), - ("bitxor", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::BitXor"), - ("borrow", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, "std::borrow::Borrow"), - ("borrow_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, "std::borrow::BorrowMut"), - ("clone", 1, &FN_HEADER, SelfKind::Ref, OutType::Any, "std::clone::Clone"), - ("cmp", 2, &FN_HEADER, SelfKind::Ref, OutType::Any, "std::cmp::Ord"), - ("default", 0, &FN_HEADER, SelfKind::No, OutType::Any, "std::default::Default"), - ("deref", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, "std::ops::Deref"), - ("deref_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, "std::ops::DerefMut"), - ("div", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Div"), - ("drop", 1, &FN_HEADER, SelfKind::RefMut, OutType::Unit, "std::ops::Drop"), - ("eq", 2, &FN_HEADER, SelfKind::Ref, OutType::Bool, "std::cmp::PartialEq"), - ("from_iter", 1, &FN_HEADER, SelfKind::No, OutType::Any, "std::iter::FromIterator"), - ("from_str", 1, &FN_HEADER, SelfKind::No, OutType::Any, "std::str::FromStr"), - ("hash", 2, &FN_HEADER, SelfKind::Ref, OutType::Unit, "std::hash::Hash"), - ("index", 2, &FN_HEADER, SelfKind::Ref, OutType::Ref, "std::ops::Index"), - ("index_mut", 2, &FN_HEADER, SelfKind::RefMut, OutType::Ref, "std::ops::IndexMut"), - ("into_iter", 1, &FN_HEADER, SelfKind::Value, OutType::Any, "std::iter::IntoIterator"), - ("mul", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Mul"), - ("neg", 1, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Neg"), - ("next", 1, &FN_HEADER, SelfKind::RefMut, OutType::Any, "std::iter::Iterator"), - ("not", 1, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Not"), - ("rem", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Rem"), - ("shl", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Shl"), - ("shr", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Shr"), - ("sub", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Sub"), +const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [ + ShouldImplTraitCase::new("std::ops::Add", "add", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::convert::AsMut", "as_mut", 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), + ShouldImplTraitCase::new("std::convert::AsRef", "as_ref", 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true), + ShouldImplTraitCase::new("std::ops::BitAnd", "bitand", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::BitOr", "bitor", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::BitXor", "bitxor", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::borrow::Borrow", "borrow", 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true), + ShouldImplTraitCase::new("std::borrow::BorrowMut", "borrow_mut", 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), + ShouldImplTraitCase::new("std::clone::Clone", "clone", 1, FN_HEADER, SelfKind::Ref, OutType::Any, true), + ShouldImplTraitCase::new("std::cmp::Ord", "cmp", 2, FN_HEADER, SelfKind::Ref, OutType::Any, true), + // FIXME: default doesn't work + ShouldImplTraitCase::new("std::default::Default", "default", 0, FN_HEADER, SelfKind::No, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Deref", "deref", 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true), + ShouldImplTraitCase::new("std::ops::DerefMut", "deref_mut", 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), + ShouldImplTraitCase::new("std::ops::Div", "div", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Drop", "drop", 1, FN_HEADER, SelfKind::RefMut, OutType::Unit, true), + ShouldImplTraitCase::new("std::cmp::PartialEq", "eq", 2, FN_HEADER, SelfKind::Ref, OutType::Bool, true), + ShouldImplTraitCase::new("std::iter::FromIterator", "from_iter", 1, FN_HEADER, SelfKind::No, OutType::Any, true), + ShouldImplTraitCase::new("std::str::FromStr", "from_str", 1, FN_HEADER, SelfKind::No, OutType::Any, true), + ShouldImplTraitCase::new("std::hash::Hash", "hash", 2, FN_HEADER, SelfKind::Ref, OutType::Unit, true), + ShouldImplTraitCase::new("std::ops::Index", "index", 2, FN_HEADER, SelfKind::Ref, OutType::Ref, true), + ShouldImplTraitCase::new("std::ops::IndexMut", "index_mut", 2, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), + ShouldImplTraitCase::new("std::iter::IntoIterator", "into_iter", 1, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Mul", "mul", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Neg", "neg", 1, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::iter::Iterator", "next", 1, FN_HEADER, SelfKind::RefMut, OutType::Any, false), + ShouldImplTraitCase::new("std::ops::Not", "not", 1, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Rem", "rem", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Shl", "shl", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Shr", "shr", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Sub", "sub", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), ]; #[rustfmt::skip] diff --git a/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/clippy_lints/src/methods/unnecessary_lazy_eval.rs new file mode 100644 index 00000000000..31517659c34 --- /dev/null +++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -0,0 +1,111 @@ +use crate::utils::{is_type_diagnostic_item, match_qpath, snippet, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; + +use super::UNNECESSARY_LAZY_EVALUATIONS; + +// Return true if the expression is an accessor of any of the arguments +fn expr_uses_argument(expr: &hir::Expr<'_>, params: &[hir::Param<'_>]) -> bool { + params.iter().any(|arg| { + if_chain! { + if let hir::PatKind::Binding(_, _, ident, _) = arg.pat.kind; + if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = expr.kind; + if let [p, ..] = path.segments; + then { + ident.name == p.ident.name + } else { + false + } + } + }) +} + +fn match_any_qpath(path: &hir::QPath<'_>, paths: &[&[&str]]) -> bool { + paths.iter().any(|candidate| match_qpath(path, candidate)) +} + +fn can_simplify(expr: &hir::Expr<'_>, params: &[hir::Param<'_>], variant_calls: bool) -> bool { + match expr.kind { + // Closures returning literals can be unconditionally simplified + hir::ExprKind::Lit(_) => true, + + hir::ExprKind::Index(ref object, ref index) => { + // arguments are not being indexed into + if expr_uses_argument(object, params) { + false + } else { + // arguments are not used as index + !expr_uses_argument(index, params) + } + }, + + // Reading fields can be simplified if the object is not an argument of the closure + hir::ExprKind::Field(ref object, _) => !expr_uses_argument(object, params), + + // Paths can be simplified if the root is not the argument, this also covers None + hir::ExprKind::Path(_) => !expr_uses_argument(expr, params), + + // Calls to Some, Ok, Err can be considered literals if they don't derive an argument + hir::ExprKind::Call(ref func, ref args) => if_chain! { + if variant_calls; // Disable lint when rules conflict with bind_instead_of_map + if let hir::ExprKind::Path(ref path) = func.kind; + if match_any_qpath(path, &[&["Some"], &["Ok"], &["Err"]]); + then { + // Recursively check all arguments + args.iter().all(|arg| can_simplify(arg, params, variant_calls)) + } else { + false + } + }, + + // For anything more complex than the above, a closure is probably the right solution, + // or the case is handled by an other lint + _ => false, + } +} + +/// lint use of `_else(simple closure)` for `Option`s and `Result`s that can be +/// replaced with `(return value of simple closure)` +pub(super) fn lint<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'_>, + args: &'tcx [hir::Expr<'_>], + allow_variant_calls: bool, + simplify_using: &str, +) { + let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(option_type)); + let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(result_type)); + + if is_option || is_result { + if let hir::ExprKind::Closure(_, _, eid, _, _) = args[1].kind { + let body = cx.tcx.hir().body(eid); + let ex = &body.value; + let params = &body.params; + + if can_simplify(ex, params, allow_variant_calls) { + let msg = if is_option { + "unnecessary closure used to substitute value for `Option::None`" + } else { + "unnecessary closure used to substitute value for `Result::Err`" + }; + + span_lint_and_sugg( + cx, + UNNECESSARY_LAZY_EVALUATIONS, + expr.span, + msg, + &format!("Use `{}` instead", simplify_using), + format!( + "{0}.{1}({2})", + snippet(cx, args[0].span, ".."), + simplify_using, + snippet(cx, ex.span, ".."), + ), + Applicability::MachineApplicable, + ); + } + } + } +} diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 482a563572d..06f367a8b77 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -433,8 +433,7 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { return; } let binding = match expr.kind { - ExprKind::Path(hir::QPath::LangItem(..)) => None, - ExprKind::Path(ref qpath) => { + ExprKind::Path(ref qpath) if !matches!(qpath, hir::QPath::LangItem(..)) => { let binding = last_path_segment(qpath).ident.as_str(); if binding.starts_with('_') && !binding.starts_with("__") && diff --git a/clippy_lints/src/mut_reference.rs b/clippy_lints/src/mut_reference.rs index b8dc5081632..c506440ed79 100644 --- a/clippy_lints/src/mut_reference.rs +++ b/clippy_lints/src/mut_reference.rs @@ -9,8 +9,8 @@ declare_clippy_lint! { /// **What it does:** Detects passing a mutable reference to a function that only /// requires an immutable reference. /// - /// **Why is this bad?** The immutable reference rules out all other references - /// to the value. Also the code misleads about the intent of the call site. + /// **Why is this bad?** The mutable reference rules out all other references to + /// the value. Also the code misleads about the intent of the call site. /// /// **Known problems:** None. /// @@ -39,6 +39,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMutPassed { arguments, cx.typeck_results().expr_ty(fn_expr), &rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false)), + "function", ); } }, @@ -46,14 +47,20 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMutPassed { let def_id = cx.typeck_results().type_dependent_def_id(e.hir_id).unwrap(); let substs = cx.typeck_results().node_substs(e.hir_id); let method_type = cx.tcx.type_of(def_id).subst(cx.tcx, substs); - check_arguments(cx, arguments, method_type, &path.ident.as_str()) + check_arguments(cx, arguments, method_type, &path.ident.as_str(), "method") }, _ => (), } } } -fn check_arguments<'tcx>(cx: &LateContext<'tcx>, arguments: &[Expr<'_>], type_definition: Ty<'tcx>, name: &str) { +fn check_arguments<'tcx>( + cx: &LateContext<'tcx>, + arguments: &[Expr<'_>], + type_definition: Ty<'tcx>, + name: &str, + fn_kind: &str, +) { match type_definition.kind { ty::FnDef(..) | ty::FnPtr(_) => { let parameters = type_definition.fn_sig(cx.tcx).skip_binder().inputs(); @@ -68,7 +75,7 @@ fn check_arguments<'tcx>(cx: &LateContext<'tcx>, arguments: &[Expr<'_>], type_de cx, UNNECESSARY_MUT_PASSED, argument.span, - &format!("The function/method `{}` doesn't need a mutable reference", name), + &format!("the {} `{}` doesn't need a mutable reference", fn_kind, name), ); } }, diff --git a/clippy_lints/src/mutex_atomic.rs b/clippy_lints/src/mutex_atomic.rs index 568898aa5c9..21efee71269 100644 --- a/clippy_lints/src/mutex_atomic.rs +++ b/clippy_lints/src/mutex_atomic.rs @@ -72,8 +72,8 @@ impl<'tcx> LateLintPass<'tcx> for Mutex { let mutex_param = subst.type_at(0); if let Some(atomic_name) = get_atomic_name(mutex_param) { let msg = format!( - "Consider using an `{}` instead of a `Mutex` here. If you just want the locking \ - behavior and not the internal type, consider using `Mutex<()>`.", + "consider using an `{}` instead of a `Mutex` here; if you just want the locking \ + behavior and not the internal type, consider using `Mutex<()>`", atomic_name ); match mutex_param.kind { diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 031d69e86a1..f1df634701d 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -211,8 +211,21 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { needs_check_adjustment = false; }, ExprKind::Field(..) => { - dereferenced_expr = parent_expr; needs_check_adjustment = true; + + // Check whether implicit dereferences happened; + // if so, no need to go further up + // because of the same reason as the `ExprKind::Unary` case. + if cx + .typeck_results() + .expr_adjustments(dereferenced_expr) + .iter() + .any(|adj| matches!(adj.kind, Adjust::Deref(_))) + { + break; + } + + dereferenced_expr = parent_expr; }, ExprKind::Index(e, _) if ptr::eq(&**e, cur_expr) => { // `e[i]` => desugared to `*Index::index(&e, i)`, diff --git a/clippy_lints/src/precedence.rs b/clippy_lints/src/precedence.rs index 4797771e7bd..c9d18c3cb72 100644 --- a/clippy_lints/src/precedence.rs +++ b/clippy_lints/src/precedence.rs @@ -1,4 +1,5 @@ use crate::utils::{snippet_with_applicability, span_lint_and_sugg}; +use if_chain::if_chain; use rustc_ast::ast::{BinOpKind, Expr, ExprKind, LitKind, UnOp}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; @@ -102,36 +103,36 @@ impl EarlyLintPass for Precedence { } } - if let ExprKind::Unary(UnOp::Neg, ref rhs) = expr.kind { - if let ExprKind::MethodCall(ref path_segment, ref args, _) = rhs.kind { + if let ExprKind::Unary(UnOp::Neg, operand) = &expr.kind { + let mut arg = operand; + + let mut all_odd = true; + while let ExprKind::MethodCall(path_segment, args, _) = &arg.kind { let path_segment_str = path_segment.ident.name.as_str(); - if let Some(slf) = args.first() { - if let ExprKind::Lit(ref lit) = slf.kind { - match lit.kind { - LitKind::Int(..) | LitKind::Float(..) => { - if ALLOWED_ODD_FUNCTIONS - .iter() - .any(|odd_function| **odd_function == *path_segment_str) - { - return; - } - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - PRECEDENCE, - expr.span, - "unary minus has lower precedence than method call", - "consider adding parentheses to clarify your intent", - format!( - "-({})", - snippet_with_applicability(cx, rhs.span, "..", &mut applicability) - ), - applicability, - ); - }, - _ => (), - } - } + all_odd &= ALLOWED_ODD_FUNCTIONS + .iter() + .any(|odd_function| **odd_function == *path_segment_str); + arg = args.first().expect("A method always has a receiver."); + } + + if_chain! { + if !all_odd; + if let ExprKind::Lit(lit) = &arg.kind; + if let LitKind::Int(..) | LitKind::Float(..) = &lit.kind; + then { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + PRECEDENCE, + expr.span, + "unary minus has lower precedence than method call", + "consider adding parentheses to clarify your intent", + format!( + "-({})", + snippet_with_applicability(cx, operand.span, "..", &mut applicability) + ), + applicability, + ); } } } diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 460d631fab0..7dafb1555dc 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -36,14 +36,27 @@ declare_clippy_lint! { /// argument may also fail to compile if you change the argument. Applying /// this lint on them will fix the problem, but they may be in other crates. /// + /// One notable example of a function that may cause issues, and which cannot + /// easily be changed due to being in the standard library is `Vec::contains`. + /// when called on a `Vec>`. If a `&Vec` is passed to that method then + /// it will compile, but if a `&[T]` is passed then it will not compile. + /// + /// ```ignore + /// fn cannot_take_a_slice(v: &Vec) -> bool { + /// let vec_of_vecs: Vec> = some_other_fn(); + /// + /// vec_of_vecs.contains(v) + /// } + /// ``` + /// /// Also there may be `fn(&Vec)`-typed references pointing to your function. /// If you have them, you will get a compiler error after applying this lint's /// suggestions. You then have the choice to undo your changes or change the /// type of the reference. /// /// Note that if the function is part of your public interface, there may be - /// other crates referencing it you may not be aware. Carefully deprecate the - /// function before applying the lint suggestions in this case. + /// other crates referencing it, of which you may not be aware. Carefully + /// deprecate the function before applying the lint suggestions in this case. /// /// **Example:** /// ```ignore diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index fb12c565afd..dbc676ae224 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -7,8 +7,8 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use crate::utils::sugg::Sugg; use crate::utils::{ - higher, is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet_with_applicability, - span_lint_and_sugg, SpanlessEq, + eq_expr_value, higher, is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet_with_applicability, + span_lint_and_sugg, }; declare_clippy_lint! { @@ -65,7 +65,7 @@ impl QuestionMark { if let ExprKind::Block(block, None) = &else_.kind; if block.stmts.is_empty(); if let Some(block_expr) = &block.expr; - if SpanlessEq::new(cx).ignore_fn().eq_expr(subject, block_expr); + if eq_expr_value(cx, subject, block_expr); then { replacement = Some(format!("Some({}?)", receiver_str)); } diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index 8aa478ea2d6..49cb2ffc4e3 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -77,7 +77,7 @@ impl EarlyLintPass for RedundantClosureCall { cx, REDUNDANT_CLOSURE_CALL, expr.span, - "try not to call a closure in the expression where it is declared.", + "try not to call a closure in the expression where it is declared", |diag| { if decl.inputs.is_empty() { let mut app = Applicability::MachineApplicable; @@ -95,12 +95,17 @@ impl EarlyLintPass for RedundantClosureCall { impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) { - fn count_closure_usage<'tcx>(block: &'tcx hir::Block<'_>, path: &'tcx hir::Path<'tcx>) -> usize { - struct ClosureUsageCount<'tcx> { + fn count_closure_usage<'a, 'tcx>( + cx: &'a LateContext<'tcx>, + block: &'tcx hir::Block<'_>, + path: &'tcx hir::Path<'tcx>, + ) -> usize { + struct ClosureUsageCount<'a, 'tcx> { + cx: &'a LateContext<'tcx>, path: &'tcx hir::Path<'tcx>, count: usize, }; - impl<'tcx> hir_visit::Visitor<'tcx> for ClosureUsageCount<'tcx> { + impl<'a, 'tcx> hir_visit::Visitor<'tcx> for ClosureUsageCount<'a, 'tcx> { type Map = Map<'tcx>; fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { @@ -117,10 +122,10 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { } fn nested_visit_map(&mut self) -> hir_visit::NestedVisitorMap { - hir_visit::NestedVisitorMap::None + hir_visit::NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) } }; - let mut closure_usage_count = ClosureUsageCount { path, count: 0 }; + let mut closure_usage_count = ClosureUsageCount { cx, path, count: 0 }; closure_usage_count.visit_block(block); closure_usage_count.count } @@ -136,7 +141,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { if let hir::ExprKind::Call(ref closure, _) = call.kind; if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = closure.kind; if ident == path.segments[0].ident; - if count_closure_usage(block, path) == 1; + if count_closure_usage(cx, block, path) == 1; then { span_lint( cx, diff --git a/clippy_lints/src/repeat_once.rs b/clippy_lints/src/repeat_once.rs index 77c206002ea..c0890018d46 100644 --- a/clippy_lints/src/repeat_once.rs +++ b/clippy_lints/src/repeat_once.rs @@ -39,12 +39,12 @@ declare_lint_pass!(RepeatOnce => [REPEAT_ONCE]); impl<'tcx> LateLintPass<'tcx> for RepeatOnce { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) { if_chain! { - if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind; + if let ExprKind::MethodCall(path, _, [receiver, count], _) = &expr.kind; if path.ident.name == sym!(repeat); - if let Some(Constant::Int(1)) = constant_context(cx, cx.typeck_results()).expr(&args[1]); - if !in_macro(args[0].span); + if let Some(Constant::Int(1)) = constant_context(cx, cx.typeck_results()).expr(&count); + if !in_macro(receiver.span); then { - let ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&args[0])); + let ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&receiver)); if ty.is_str() { span_lint_and_sugg( cx, @@ -52,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for RepeatOnce { expr.span, "calling `repeat(1)` on str", "consider using `.to_string()` instead", - format!("{}.to_string()", snippet(cx, args[0].span, r#""...""#)), + format!("{}.to_string()", snippet(cx, receiver.span, r#""...""#)), Applicability::MachineApplicable, ); } else if ty.builtin_index().is_some() { @@ -62,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for RepeatOnce { expr.span, "calling `repeat(1)` on slice", "consider using `.to_vec()` instead", - format!("{}.to_vec()", snippet(cx, args[0].span, r#""...""#)), + format!("{}.to_vec()", snippet(cx, receiver.span, r#""...""#)), Applicability::MachineApplicable, ); } else if is_type_diagnostic_item(cx, ty, sym!(string_type)) { @@ -72,7 +72,7 @@ impl<'tcx> LateLintPass<'tcx> for RepeatOnce { expr.span, "calling `repeat(1)` on a string literal", "consider using `.clone()` instead", - format!("{}.clone()", snippet(cx, args[0].span, r#""...""#)), + format!("{}.clone()", snippet(cx, receiver.span, r#""...""#)), Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 8ed20995a70..a6e4252a0c8 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -1,14 +1,43 @@ use if_chain::if_chain; -use rustc_ast::ast; -use rustc_ast::visit::FnKind; +use rustc_ast::ast::Attribute; use rustc_errors::Applicability; -use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_hir::intravisit::{walk_expr, FnKind, NestedVisitorMap, Visitor}; +use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, HirId, MatchSource, PatKind, StmtKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::subst::GenericArgKind; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use rustc_span::BytePos; -use crate::utils::{snippet_opt, span_lint_and_sugg, span_lint_and_then}; +use crate::utils::{fn_def_id, in_macro, match_qpath, snippet_opt, span_lint_and_sugg, span_lint_and_then}; + +declare_clippy_lint! { + /// **What it does:** Checks for `let`-bindings, which are subsequently + /// returned. + /// + /// **Why is this bad?** It is just extraneous code. Remove it to make your code + /// more rusty. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// fn foo() -> String { + /// let x = String::new(); + /// x + /// } + /// ``` + /// instead, use + /// ``` + /// fn foo() -> String { + /// String::new() + /// } + /// ``` + pub LET_AND_RETURN, + style, + "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block" +} declare_clippy_lint! { /// **What it does:** Checks for return statements at the end of a block. @@ -16,8 +45,7 @@ declare_clippy_lint! { /// **Why is this bad?** Removing the `return` and semicolon will make the code /// more rusty. /// - /// **Known problems:** If the computation returning the value borrows a local - /// variable, removing the `return` may run afoul of the borrow checker. + /// **Known problems:** None. /// /// **Example:** /// ```rust @@ -36,248 +64,226 @@ declare_clippy_lint! { "using a return statement like `return expr;` where an expression would suffice" } -declare_clippy_lint! { - /// **What it does:** Checks for unit (`()`) expressions that can be removed. - /// - /// **Why is this bad?** Such expressions add no value, but can make the code - /// less readable. Depending on formatting they can make a `break` or `return` - /// statement look like a function call. - /// - /// **Known problems:** The lint currently misses unit return types in types, - /// e.g., the `F` in `fn generic_unit ()>(f: F) { .. }`. - /// - /// **Example:** - /// ```rust - /// fn return_unit() -> () { - /// () - /// } - /// ``` - pub UNUSED_UNIT, - style, - "needless unit expression" -} - #[derive(PartialEq, Eq, Copy, Clone)] enum RetReplacement { Empty, Block, } -declare_lint_pass!(Return => [NEEDLESS_RETURN, UNUSED_UNIT]); +declare_lint_pass!(Return => [LET_AND_RETURN, NEEDLESS_RETURN]); -impl Return { - // Check the final stmt or expr in a block for unnecessary return. - fn check_block_return(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) { - if let Some(stmt) = block.stmts.last() { - match stmt.kind { - ast::StmtKind::Expr(ref expr) | ast::StmtKind::Semi(ref expr) => { - self.check_final_expr(cx, expr, Some(stmt.span), RetReplacement::Empty); - }, - _ => (), +impl<'tcx> LateLintPass<'tcx> for Return { + fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) { + // we need both a let-binding stmt and an expr + if_chain! { + if let Some(retexpr) = block.expr; + if let Some(stmt) = block.stmts.iter().last(); + if let StmtKind::Local(local) = &stmt.kind; + if local.ty.is_none(); + if local.attrs.is_empty(); + if let Some(initexpr) = &local.init; + if let PatKind::Binding(.., ident, _) = local.pat.kind; + if let ExprKind::Path(qpath) = &retexpr.kind; + if match_qpath(qpath, &[&*ident.name.as_str()]); + if !last_statement_borrows(cx, initexpr); + if !in_external_macro(cx.sess(), initexpr.span); + if !in_external_macro(cx.sess(), retexpr.span); + if !in_external_macro(cx.sess(), local.span); + if !in_macro(local.span); + then { + span_lint_and_then( + cx, + LET_AND_RETURN, + retexpr.span, + "returning the result of a `let` binding from a block", + |err| { + err.span_label(local.span, "unnecessary `let` binding"); + + if let Some(mut snippet) = snippet_opt(cx, initexpr.span) { + if !cx.typeck_results().expr_adjustments(&retexpr).is_empty() { + snippet.push_str(" as _"); + } + err.multipart_suggestion( + "return the expression directly", + vec![ + (local.span, String::new()), + (retexpr.span, snippet), + ], + Applicability::MachineApplicable, + ); + } else { + err.span_help(initexpr.span, "this expression can be directly returned"); + } + }, + ); } } } - // Check the final expression in a block if it's a return. - fn check_final_expr( + fn check_fn( &mut self, - cx: &EarlyContext<'_>, - expr: &ast::Expr, - span: Option, - replacement: RetReplacement, + cx: &LateContext<'tcx>, + kind: FnKind<'tcx>, + _: &'tcx FnDecl<'tcx>, + body: &'tcx Body<'tcx>, + _: Span, + _: HirId, ) { - match expr.kind { - // simple return is always "bad" - ast::ExprKind::Ret(ref inner) => { - // allow `#[cfg(a)] return a; #[cfg(b)] return b;` - if !expr.attrs.iter().any(attr_is_cfg) { - Self::emit_return_lint( + match kind { + FnKind::Closure(_) => check_final_expr(cx, &body.value, Some(body.value.span), RetReplacement::Empty), + FnKind::ItemFn(..) | FnKind::Method(..) => { + if let ExprKind::Block(ref block, _) = body.value.kind { + check_block_return(cx, block); + } + }, + } + } +} + +fn attr_is_cfg(attr: &Attribute) -> bool { + attr.meta_item_list().is_some() && attr.has_name(sym!(cfg)) +} + +fn check_block_return<'tcx>(cx: &LateContext<'tcx>, block: &Block<'tcx>) { + if let Some(expr) = block.expr { + check_final_expr(cx, expr, Some(expr.span), RetReplacement::Empty); + } else if let Some(stmt) = block.stmts.iter().last() { + match stmt.kind { + StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => { + check_final_expr(cx, expr, Some(stmt.span), RetReplacement::Empty); + }, + _ => (), + } + } +} + +fn check_final_expr<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, + span: Option, + replacement: RetReplacement, +) { + match expr.kind { + // simple return is always "bad" + ExprKind::Ret(ref inner) => { + // allow `#[cfg(a)] return a; #[cfg(b)] return b;` + if !expr.attrs.iter().any(attr_is_cfg) { + let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner)); + if !borrows { + emit_return_lint( cx, span.expect("`else return` is not possible"), inner.as_ref().map(|i| i.span), replacement, ); } - }, - // a whole block? check it! - ast::ExprKind::Block(ref block, _) => { - self.check_block_return(cx, block); - }, - // an if/if let expr, check both exprs - // note, if without else is going to be a type checking error anyways - // (except for unit type functions) so we don't match it - ast::ExprKind::If(_, ref ifblock, Some(ref elsexpr)) => { - self.check_block_return(cx, ifblock); - self.check_final_expr(cx, elsexpr, None, RetReplacement::Empty); - }, - // a match expr, check all arms - ast::ExprKind::Match(_, ref arms) => { - for arm in arms { - self.check_final_expr(cx, &arm.body, Some(arm.body.span), RetReplacement::Block); + } + }, + // a whole block? check it! + ExprKind::Block(ref block, _) => { + check_block_return(cx, block); + }, + // a match expr, check all arms + // an if/if let expr, check both exprs + // note, if without else is going to be a type checking error anyways + // (except for unit type functions) so we don't match it + ExprKind::Match(_, ref arms, source) => match source { + MatchSource::Normal => { + for arm in arms.iter() { + check_final_expr(cx, &arm.body, Some(arm.body.span), RetReplacement::Block); } }, + MatchSource::IfDesugar { + contains_else_clause: true, + } + | MatchSource::IfLetDesugar { + contains_else_clause: true, + } => { + if let ExprKind::Block(ref ifblock, _) = arms[0].body.kind { + check_block_return(cx, ifblock); + } + check_final_expr(cx, arms[1].body, None, RetReplacement::Empty); + }, _ => (), - } - } - - fn emit_return_lint(cx: &EarlyContext<'_>, ret_span: Span, inner_span: Option, replacement: RetReplacement) { - match inner_span { - Some(inner_span) => { - if in_external_macro(cx.sess(), inner_span) || inner_span.from_expansion() { - return; - } - - span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| { - if let Some(snippet) = snippet_opt(cx, inner_span) { - diag.span_suggestion(ret_span, "remove `return`", snippet, Applicability::MachineApplicable); - } - }) - }, - None => match replacement { - RetReplacement::Empty => { - span_lint_and_sugg( - cx, - NEEDLESS_RETURN, - ret_span, - "unneeded `return` statement", - "remove `return`", - String::new(), - Applicability::MachineApplicable, - ); - }, - RetReplacement::Block => { - span_lint_and_sugg( - cx, - NEEDLESS_RETURN, - ret_span, - "unneeded `return` statement", - "replace `return` with an empty block", - "{}".to_string(), - Applicability::MachineApplicable, - ); - }, - }, - } + }, + _ => (), } } -impl EarlyLintPass for Return { - fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, span: Span, _: ast::NodeId) { - match kind { - FnKind::Fn(.., Some(block)) => self.check_block_return(cx, block), - FnKind::Closure(_, body) => self.check_final_expr(cx, body, Some(body.span), RetReplacement::Empty), - FnKind::Fn(.., None) => {}, - } - if_chain! { - if let ast::FnRetTy::Ty(ref ty) = kind.decl().output; - if let ast::TyKind::Tup(ref vals) = ty.kind; - if vals.is_empty() && !ty.span.from_expansion() && get_def(span) == get_def(ty.span); - then { - lint_unneeded_unit_return(cx, ty, span); +fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, inner_span: Option, replacement: RetReplacement) { + match inner_span { + Some(inner_span) => { + if in_external_macro(cx.tcx.sess, inner_span) || inner_span.from_expansion() { + return; } - } - } - fn check_block(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) { - if_chain! { - if let Some(ref stmt) = block.stmts.last(); - if let ast::StmtKind::Expr(ref expr) = stmt.kind; - if is_unit_expr(expr) && !stmt.span.from_expansion(); - then { - let sp = expr.span; + span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| { + if let Some(snippet) = snippet_opt(cx, inner_span) { + diag.span_suggestion(ret_span, "remove `return`", snippet, Applicability::MachineApplicable); + } + }) + }, + None => match replacement { + RetReplacement::Empty => { span_lint_and_sugg( cx, - UNUSED_UNIT, - sp, - "unneeded unit expression", - "remove the final `()`", + NEEDLESS_RETURN, + ret_span, + "unneeded `return` statement", + "remove `return`", String::new(), Applicability::MachineApplicable, ); - } - } - } - - fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { - match e.kind { - ast::ExprKind::Ret(Some(ref expr)) | ast::ExprKind::Break(_, Some(ref expr)) => { - if is_unit_expr(expr) && !expr.span.from_expansion() { - span_lint_and_sugg( - cx, - UNUSED_UNIT, - expr.span, - "unneeded `()`", - "remove the `()`", - String::new(), - Applicability::MachineApplicable, - ); - } }, - _ => (), - } - } - - fn check_poly_trait_ref(&mut self, cx: &EarlyContext<'_>, poly: &ast::PolyTraitRef, _: &ast::TraitBoundModifier) { - let segments = &poly.trait_ref.path.segments; - - if_chain! { - if segments.len() == 1; - if ["Fn", "FnMut", "FnOnce"].contains(&&*segments[0].ident.name.as_str()); - if let Some(args) = &segments[0].args; - if let ast::GenericArgs::Parenthesized(generic_args) = &**args; - if let ast::FnRetTy::Ty(ty) = &generic_args.output; - if ty.kind.is_unit(); - then { - lint_unneeded_unit_return(cx, ty, generic_args.span); - } - } - } -} - -fn attr_is_cfg(attr: &ast::Attribute) -> bool { - attr.meta_item_list().is_some() && attr.has_name(sym!(cfg)) -} - -// get the def site -#[must_use] -fn get_def(span: Span) -> Option { - if span.from_expansion() { - Some(span.ctxt().outer_expn_data().def_site) - } else { - None - } -} - -// is this expr a `()` unit? -fn is_unit_expr(expr: &ast::Expr) -> bool { - if let ast::ExprKind::Tup(ref vals) = expr.kind { - vals.is_empty() - } else { - false - } -} - -fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) { - let (ret_span, appl) = if let Ok(fn_source) = cx.sess().source_map().span_to_snippet(span.with_hi(ty.span.hi())) { - fn_source - .rfind("->") - .map_or((ty.span, Applicability::MaybeIncorrect), |rpos| { - ( - #[allow(clippy::cast_possible_truncation)] - ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), + RetReplacement::Block => { + span_lint_and_sugg( + cx, + NEEDLESS_RETURN, + ret_span, + "unneeded `return` statement", + "replace `return` with an empty block", + "{}".to_string(), Applicability::MachineApplicable, - ) - }) - } else { - (ty.span, Applicability::MaybeIncorrect) - }; - span_lint_and_sugg( - cx, - UNUSED_UNIT, - ret_span, - "unneeded unit return type", - "remove the `-> ()`", - String::new(), - appl, - ); + ); + }, + }, + } +} + +fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + let mut visitor = BorrowVisitor { cx, borrows: false }; + walk_expr(&mut visitor, expr); + visitor.borrows +} + +struct BorrowVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + borrows: bool, +} + +impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + if self.borrows { + return; + } + + if let Some(def_id) = fn_def_id(self.cx, expr) { + self.borrows = self + .cx + .tcx + .fn_sig(def_id) + .output() + .skip_binder() + .walk() + .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_))); + } + + walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } } diff --git a/clippy_lints/src/self_assignment.rs b/clippy_lints/src/self_assignment.rs new file mode 100644 index 00000000000..e096c9aebc1 --- /dev/null +++ b/clippy_lints/src/self_assignment.rs @@ -0,0 +1,51 @@ +use crate::utils::{eq_expr_value, snippet, span_lint}; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for explicit self-assignments. + /// + /// **Why is this bad?** Self-assignments are redundant and unlikely to be + /// intentional. + /// + /// **Known problems:** If expression contains any deref coercions or + /// indexing operations they are assumed not to have any side effects. + /// + /// **Example:** + /// + /// ```rust + /// struct Event { + /// id: usize, + /// x: i32, + /// y: i32, + /// } + /// + /// fn copy_position(a: &mut Event, b: &Event) { + /// a.x = b.x; + /// a.y = a.y; + /// } + /// ``` + pub SELF_ASSIGNMENT, + correctness, + "explicit self-assignment" +} + +declare_lint_pass!(SelfAssignment => [SELF_ASSIGNMENT]); + +impl<'tcx> LateLintPass<'tcx> for SelfAssignment { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if let ExprKind::Assign(lhs, rhs, _) = &expr.kind { + if eq_expr_value(cx, lhs, rhs) { + let lhs = snippet(cx, lhs.span, ""); + let rhs = snippet(cx, rhs.span, ""); + span_lint( + cx, + SELF_ASSIGNMENT, + expr.span, + &format!("self-assignment of `{}` to `{}`", rhs, lhs), + ); + } + } + } +} diff --git a/clippy_lints/src/stable_sort_primitive.rs b/clippy_lints/src/stable_sort_primitive.rs index cd7056620a2..99e4b293ac6 100644 --- a/clippy_lints/src/stable_sort_primitive.rs +++ b/clippy_lints/src/stable_sort_primitive.rs @@ -86,6 +86,7 @@ struct LintDetection { slice_name: String, method: SortingKind, method_args: String, + slice_type: String, } fn detect_stable_sort_primitive(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { @@ -93,10 +94,10 @@ fn detect_stable_sort_primitive(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option if let ExprKind::MethodCall(method_name, _, args, _) = &expr.kind; if let Some(slice) = &args.get(0); if let Some(method) = SortingKind::from_stable_name(&method_name.ident.name.as_str()); - if is_slice_of_primitives(cx, slice); + if let Some(slice_type) = is_slice_of_primitives(cx, slice); then { let args_str = args.iter().skip(1).map(|arg| Sugg::hir(cx, arg, "..").to_string()).collect::>().join(", "); - Some(LintDetection { slice_name: Sugg::hir(cx, slice, "..").to_string(), method, method_args: args_str }) + Some(LintDetection { slice_name: Sugg::hir(cx, slice, "..").to_string(), method, method_args: args_str, slice_type }) } else { None } @@ -111,9 +112,10 @@ impl LateLintPass<'_> for StableSortPrimitive { STABLE_SORT_PRIMITIVE, expr.span, format!( - "Use {} instead of {}", + "used {} instead of {} to sort primitive type `{}`", + detection.method.stable_name(), detection.method.unstable_name(), - detection.method.stable_name() + detection.slice_type, ) .as_str(), "try", diff --git a/clippy_lints/src/suspicious_trait_impl.rs b/clippy_lints/src/suspicious_trait_impl.rs index 4e335a0222f..3a688a7bbef 100644 --- a/clippy_lints/src/suspicious_trait_impl.rs +++ b/clippy_lints/src/suspicious_trait_impl.rs @@ -86,12 +86,20 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl { cx, expr, binop.node, - &["Add", "Sub", "Mul", "Div"], + &[ + "Add", "Sub", "Mul", "Div", "Rem", "BitAnd", "BitOr", "BitXor", "Shl", "Shr", + ], &[ hir::BinOpKind::Add, hir::BinOpKind::Sub, hir::BinOpKind::Mul, hir::BinOpKind::Div, + hir::BinOpKind::Rem, + hir::BinOpKind::BitAnd, + hir::BinOpKind::BitOr, + hir::BinOpKind::BitXor, + hir::BinOpKind::Shl, + hir::BinOpKind::Shr, ], ) { span_lint( diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index 754f87e6b55..cc39f060fc7 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -1,7 +1,7 @@ use crate::utils::sugg::Sugg; use crate::utils::{ - differing_macro_contexts, is_type_diagnostic_item, snippet_with_applicability, span_lint_and_then, walk_ptrs_ty, - SpanlessEq, + differing_macro_contexts, eq_expr_value, is_type_diagnostic_item, snippet_with_applicability, span_lint_and_then, + walk_ptrs_ty, }; use if_chain::if_chain; use rustc_errors::Applicability; @@ -92,8 +92,8 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) { if rhs2.segments.len() == 1; if ident.as_str() == rhs2.segments[0].ident.as_str(); - if SpanlessEq::new(cx).ignore_fn().eq_expr(tmp_init, lhs1); - if SpanlessEq::new(cx).ignore_fn().eq_expr(rhs1, lhs2); + if eq_expr_value(cx, tmp_init, lhs1); + if eq_expr_value(cx, rhs1, lhs2); then { if let ExprKind::Field(ref lhs1, _) = lhs1.kind { if let ExprKind::Field(ref lhs2, _) = lhs2.kind { @@ -193,7 +193,7 @@ enum Slice<'a> { fn check_for_slice<'a>(cx: &LateContext<'_>, lhs1: &'a Expr<'_>, lhs2: &'a Expr<'_>) -> Slice<'a> { if let ExprKind::Index(ref lhs1, ref idx1) = lhs1.kind { if let ExprKind::Index(ref lhs2, ref idx2) = lhs2.kind { - if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs1, lhs2) { + if eq_expr_value(cx, lhs1, lhs2) { let ty = walk_ptrs_ty(cx.typeck_results().expr_ty(lhs1)); if matches!(ty.kind, ty::Slice(_)) @@ -221,8 +221,8 @@ fn check_suspicious_swap(cx: &LateContext<'_>, block: &Block<'_>) { if !differing_macro_contexts(first.span, second.span); if let ExprKind::Assign(ref lhs0, ref rhs0, _) = first.kind; if let ExprKind::Assign(ref lhs1, ref rhs1, _) = second.kind; - if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs0, rhs1); - if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs1, rhs0); + if eq_expr_value(cx, lhs0, rhs1); + if eq_expr_value(cx, lhs1, rhs0); then { let lhs0 = Sugg::hir_opt(cx, lhs0); let rhs0 = Sugg::hir_opt(cx, rhs0); diff --git a/clippy_lints/src/to_string_in_display.rs b/clippy_lints/src/to_string_in_display.rs new file mode 100644 index 00000000000..006d7a3a12d --- /dev/null +++ b/clippy_lints/src/to_string_in_display.rs @@ -0,0 +1,122 @@ +use crate::utils::{match_def_path, match_trait_method, paths, qpath_res, span_lint}; +use if_chain::if_chain; +use rustc_hir::def::Res; +use rustc_hir::{Expr, ExprKind, HirId, ImplItem, ImplItemKind, Item, ItemKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; + +declare_clippy_lint! { + /// **What it does:** Checks for uses of `to_string()` in `Display` traits. + /// + /// **Why is this bad?** Usually `to_string` is implemented indirectly + /// via `Display`. Hence using it while implementing `Display` would + /// lead to infinite recursion. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// use std::fmt; + /// + /// struct Structure(i32); + /// impl fmt::Display for Structure { + /// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + /// write!(f, "{}", self.to_string()) + /// } + /// } + /// + /// ``` + /// Use instead: + /// ```rust + /// use std::fmt; + /// + /// struct Structure(i32); + /// impl fmt::Display for Structure { + /// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + /// write!(f, "{}", self.0) + /// } + /// } + /// ``` + pub TO_STRING_IN_DISPLAY, + correctness, + "`to_string` method used while implementing `Display` trait" +} + +#[derive(Default)] +pub struct ToStringInDisplay { + in_display_impl: bool, + self_hir_id: Option, +} + +impl ToStringInDisplay { + pub fn new() -> Self { + Self { + in_display_impl: false, + self_hir_id: None, + } + } +} + +impl_lint_pass!(ToStringInDisplay => [TO_STRING_IN_DISPLAY]); + +impl LateLintPass<'_> for ToStringInDisplay { + fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + if is_display_impl(cx, item) { + self.in_display_impl = true; + } + } + + fn check_item_post(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + if is_display_impl(cx, item) { + self.in_display_impl = false; + self.self_hir_id = None; + } + } + + fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &ImplItem<'_>) { + if_chain! { + if self.in_display_impl; + if let ImplItemKind::Fn(.., body_id) = &impl_item.kind; + let body = cx.tcx.hir().body(*body_id); + if !body.params.is_empty(); + then { + let self_param = &body.params[0]; + self.self_hir_id = Some(self_param.pat.hir_id); + } + } + } + + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + if_chain! { + if let ExprKind::MethodCall(ref path, _, args, _) = expr.kind; + if path.ident.name == sym!(to_string); + if match_trait_method(cx, expr, &paths::TO_STRING); + if self.in_display_impl; + if let ExprKind::Path(ref qpath) = args[0].kind; + if let Res::Local(hir_id) = qpath_res(cx, qpath, args[0].hir_id); + if let Some(self_hir_id) = self.self_hir_id; + if hir_id == self_hir_id; + then { + span_lint( + cx, + TO_STRING_IN_DISPLAY, + expr.span, + "using `to_string` in `fmt::Display` implementation might lead to infinite recursion", + ); + } + } + } +} + +fn is_display_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool { + if_chain! { + if let ItemKind::Impl { of_trait: Some(trait_ref), .. } = &item.kind; + if let Some(did) = trait_ref.trait_def_id(); + then { + match_def_path(cx, did, &paths::DISPLAY_TRAIT) + } else { + false + } + } +} diff --git a/clippy_lints/src/transmute.rs b/clippy_lints/src/transmute.rs index 28fd55f6ff0..50d9c93f9d4 100644 --- a/clippy_lints/src/transmute.rs +++ b/clippy_lints/src/transmute.rs @@ -1,5 +1,5 @@ use crate::utils::{ - is_normalizable, last_path_segment, match_def_path, paths, snippet, span_lint, span_lint_and_sugg, + in_constant, is_normalizable, last_path_segment, match_def_path, paths, snippet, span_lint, span_lint_and_sugg, span_lint_and_then, sugg, }; use if_chain::if_chain; @@ -331,6 +331,10 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id(); if match_def_path(cx, def_id, &paths::TRANSMUTE); then { + // Avoid suggesting from/to bits in const contexts. + // See https://github.com/rust-lang/rust/issues/73736 for progress on making them `const fn`. + let const_context = in_constant(cx, e.hir_id); + let from_ty = cx.typeck_results().expr_ty(&args[0]); let to_ty = cx.typeck_results().expr_ty(e); @@ -544,7 +548,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { }, ) }, - (ty::Int(_) | ty::Uint(_), ty::Float(_)) => span_lint_and_then( + (ty::Int(_) | ty::Uint(_), ty::Float(_)) if !const_context => span_lint_and_then( cx, TRANSMUTE_INT_TO_FLOAT, e.span, @@ -567,7 +571,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { ); }, ), - (ty::Float(float_ty), ty::Int(_) | ty::Uint(_)) => span_lint_and_then( + (ty::Float(float_ty), ty::Int(_) | ty::Uint(_)) if !const_context => span_lint_and_then( cx, TRANSMUTE_FLOAT_TO_INT, e.span, diff --git a/clippy_lints/src/trivially_copy_pass_by_ref.rs b/clippy_lints/src/trivially_copy_pass_by_ref.rs index 7948d99162b..92f42168a1e 100644 --- a/clippy_lints/src/trivially_copy_pass_by_ref.rs +++ b/clippy_lints/src/trivially_copy_pass_by_ref.rs @@ -2,6 +2,7 @@ use std::cmp; use crate::utils::{is_copy, is_self_ty, snippet, span_lint_and_sugg}; use if_chain::if_chain; +use rustc_ast::attr; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::intravisit::FnKind; @@ -155,8 +156,12 @@ impl<'tcx> LateLintPass<'tcx> for TriviallyCopyPassByRef { return; } for a in attrs { - if a.meta_item_list().is_some() && a.has_name(sym!(proc_macro_derive)) { - return; + if let Some(meta_items) = a.meta_item_list() { + if a.has_name(sym!(proc_macro_derive)) + || (a.has_name(sym!(inline)) && attr::list_contains_name(&meta_items, sym!(always))) + { + return; + } } } }, diff --git a/clippy_lints/src/try_err.rs b/clippy_lints/src/try_err.rs index a74104e9282..a4676e505b6 100644 --- a/clippy_lints/src/try_err.rs +++ b/clippy_lints/src/try_err.rs @@ -1,10 +1,10 @@ use crate::utils::{ - is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet, - snippet_with_macro_callsite, span_lint_and_sugg, + is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet, snippet_with_macro_callsite, + span_lint_and_sugg, }; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, QPath, LangItem, MatchSource}; +use rustc_hir::{Expr, ExprKind, LangItem, MatchSource, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, Ty}; diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 0fd70550fa0..7e9190bef5e 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -353,14 +353,25 @@ impl Types { ); return; // don't recurse into the type } - if let Some(span) = match_type_parameter(cx, qpath, &paths::BOX) { + if match_type_parameter(cx, qpath, &paths::BOX).is_some() { + let box_ty = match &last_path_segment(qpath).args.unwrap().args[0] { + GenericArg::Type(ty) => match &ty.kind { + TyKind::Path(qpath) => qpath, + _ => return, + }, + _ => return, + }; + let inner_span = match &last_path_segment(&box_ty).args.unwrap().args[0] { + GenericArg::Type(ty) => ty.span, + _ => return, + }; span_lint_and_sugg( cx, REDUNDANT_ALLOCATION, hir_ty.span, "usage of `Rc>`", "try", - snippet(cx, span, "..").to_string(), + format!("Rc<{}>", snippet(cx, inner_span, "..")), Applicability::MachineApplicable, ); return; // don't recurse into the type diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index 9fe771cef45..7f4f16f8faf 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -2,9 +2,9 @@ use crate::utils::ast_utils::{eq_field_pat, eq_id, eq_pat, eq_path}; use crate::utils::{over, span_lint_and_then}; -use rustc_ast::{self as ast, Pat, PatKind, PatKind::*, DUMMY_NODE_ID}; use rustc_ast::mut_visit::*; use rustc_ast::ptr::P; +use rustc_ast::{self as ast, Pat, PatKind, PatKind::*, DUMMY_NODE_ID}; use rustc_ast_pretty::pprust; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; @@ -340,7 +340,7 @@ fn take_pat(from: &mut Pat) -> Pat { id: DUMMY_NODE_ID, kind: Wild, span: DUMMY_SP, - tokens: None + tokens: None, }; mem::replace(from, dummy) } diff --git a/clippy_lints/src/unused_unit.rs b/clippy_lints/src/unused_unit.rs new file mode 100644 index 00000000000..7548c6afa97 --- /dev/null +++ b/clippy_lints/src/unused_unit.rs @@ -0,0 +1,144 @@ +use if_chain::if_chain; +use rustc_ast::ast; +use rustc_ast::visit::FnKind; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; +use rustc_span::BytePos; + +use crate::utils::span_lint_and_sugg; + +declare_clippy_lint! { + /// **What it does:** Checks for unit (`()`) expressions that can be removed. + /// + /// **Why is this bad?** Such expressions add no value, but can make the code + /// less readable. Depending on formatting they can make a `break` or `return` + /// statement look like a function call. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// fn return_unit() -> () { + /// () + /// } + /// ``` + pub UNUSED_UNIT, + style, + "needless unit expression" +} + +declare_lint_pass!(UnusedUnit => [UNUSED_UNIT]); + +impl EarlyLintPass for UnusedUnit { + fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, span: Span, _: ast::NodeId) { + if_chain! { + if let ast::FnRetTy::Ty(ref ty) = kind.decl().output; + if let ast::TyKind::Tup(ref vals) = ty.kind; + if vals.is_empty() && !ty.span.from_expansion() && get_def(span) == get_def(ty.span); + then { + lint_unneeded_unit_return(cx, ty, span); + } + } + } + + fn check_block(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) { + if_chain! { + if let Some(ref stmt) = block.stmts.last(); + if let ast::StmtKind::Expr(ref expr) = stmt.kind; + if is_unit_expr(expr) && !stmt.span.from_expansion(); + then { + let sp = expr.span; + span_lint_and_sugg( + cx, + UNUSED_UNIT, + sp, + "unneeded unit expression", + "remove the final `()`", + String::new(), + Applicability::MachineApplicable, + ); + } + } + } + + fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { + match e.kind { + ast::ExprKind::Ret(Some(ref expr)) | ast::ExprKind::Break(_, Some(ref expr)) => { + if is_unit_expr(expr) && !expr.span.from_expansion() { + span_lint_and_sugg( + cx, + UNUSED_UNIT, + expr.span, + "unneeded `()`", + "remove the `()`", + String::new(), + Applicability::MachineApplicable, + ); + } + }, + _ => (), + } + } + + fn check_poly_trait_ref(&mut self, cx: &EarlyContext<'_>, poly: &ast::PolyTraitRef, _: &ast::TraitBoundModifier) { + let segments = &poly.trait_ref.path.segments; + + if_chain! { + if segments.len() == 1; + if ["Fn", "FnMut", "FnOnce"].contains(&&*segments[0].ident.name.as_str()); + if let Some(args) = &segments[0].args; + if let ast::GenericArgs::Parenthesized(generic_args) = &**args; + if let ast::FnRetTy::Ty(ty) = &generic_args.output; + if ty.kind.is_unit(); + then { + lint_unneeded_unit_return(cx, ty, generic_args.span); + } + } + } +} + +// get the def site +#[must_use] +fn get_def(span: Span) -> Option { + if span.from_expansion() { + Some(span.ctxt().outer_expn_data().def_site) + } else { + None + } +} + +// is this expr a `()` unit? +fn is_unit_expr(expr: &ast::Expr) -> bool { + if let ast::ExprKind::Tup(ref vals) = expr.kind { + vals.is_empty() + } else { + false + } +} + +fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) { + let (ret_span, appl) = if let Ok(fn_source) = cx.sess().source_map().span_to_snippet(span.with_hi(ty.span.hi())) { + fn_source + .rfind("->") + .map_or((ty.span, Applicability::MaybeIncorrect), |rpos| { + ( + #[allow(clippy::cast_possible_truncation)] + ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), + Applicability::MachineApplicable, + ) + }) + } else { + (ty.span, Applicability::MaybeIncorrect) + }; + span_lint_and_sugg( + cx, + UNUSED_UNIT, + ret_span, + "unneeded unit return type", + "remove the `-> ()`", + String::new(), + appl, + ); +} diff --git a/clippy_lints/src/unwrap_in_result.rs b/clippy_lints/src/unwrap_in_result.rs new file mode 100644 index 00000000000..1c7e62ecd3d --- /dev/null +++ b/clippy_lints/src/unwrap_in_result.rs @@ -0,0 +1,140 @@ +use crate::utils::{is_type_diagnostic_item, method_chain_args, return_ty, span_lint_and_then, walk_ptrs_ty}; +use if_chain::if_chain; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for functions of type Result that contain `expect()` or `unwrap()` + /// + /// **Why is this bad?** These functions promote recoverable errors to non-recoverable errors which may be undesirable in code bases which wish to avoid panics. + /// + /// **Known problems:** This can cause false positives in functions that handle both recoverable and non recoverable errors. + /// + /// **Example:** + /// Before: + /// ```rust + /// fn divisible_by_3(i_str: String) -> Result<(), String> { + /// let i = i_str + /// .parse::() + /// .expect("cannot divide the input by three"); + /// + /// if i % 3 != 0 { + /// Err("Number is not divisible by 3")? + /// } + /// + /// Ok(()) + /// } + /// ``` + /// + /// After: + /// ```rust + /// fn divisible_by_3(i_str: String) -> Result<(), String> { + /// let i = i_str + /// .parse::() + /// .map_err(|e| format!("cannot divide the input by three: {}", e))?; + /// + /// if i % 3 != 0 { + /// Err("Number is not divisible by 3")? + /// } + /// + /// Ok(()) + /// } + /// ``` + pub UNWRAP_IN_RESULT, + restriction, + "functions of type `Result<..>` or `Option`<...> that contain `expect()` or `unwrap()`" +} + +declare_lint_pass!(UnwrapInResult=> [UNWRAP_IN_RESULT]); + +impl<'tcx> LateLintPass<'tcx> for UnwrapInResult { + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { + if_chain! { + // first check if it's a method or function + if let hir::ImplItemKind::Fn(ref _signature, _) = impl_item.kind; + // checking if its return type is `result` or `option` + if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(result_type)) + || is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(option_type)); + then { + lint_impl_body(cx, impl_item.span, impl_item); + } + } + } +} + +use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::{Expr, ImplItemKind}; + +struct FindExpectUnwrap<'a, 'tcx> { + lcx: &'a LateContext<'tcx>, + typeck_results: &'tcx ty::TypeckResults<'tcx>, + result: Vec, +} + +impl<'a, 'tcx> Visitor<'tcx> for FindExpectUnwrap<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + // check for `expect` + if let Some(arglists) = method_chain_args(expr, &["expect"]) { + let reciever_ty = walk_ptrs_ty(self.typeck_results.expr_ty(&arglists[0][0])); + if is_type_diagnostic_item(self.lcx, reciever_ty, sym!(option_type)) + || is_type_diagnostic_item(self.lcx, reciever_ty, sym!(result_type)) + { + self.result.push(expr.span); + } + } + + // check for `unwrap` + if let Some(arglists) = method_chain_args(expr, &["unwrap"]) { + let reciever_ty = walk_ptrs_ty(self.typeck_results.expr_ty(&arglists[0][0])); + if is_type_diagnostic_item(self.lcx, reciever_ty, sym!(option_type)) + || is_type_diagnostic_item(self.lcx, reciever_ty, sym!(result_type)) + { + self.result.push(expr.span); + } + } + + // and check sub-expressions + intravisit::walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tcx hir::ImplItem<'_>) { + if_chain! { + + if let ImplItemKind::Fn(_, body_id) = impl_item.kind; + then { + let body = cx.tcx.hir().body(body_id); + let impl_item_def_id = cx.tcx.hir().local_def_id(impl_item.hir_id); + let mut fpu = FindExpectUnwrap { + lcx: cx, + typeck_results: cx.tcx.typeck(impl_item_def_id), + result: Vec::new(), + }; + fpu.visit_expr(&body.value); + + // if we've found one, lint + if !fpu.result.is_empty() { + span_lint_and_then( + cx, + UNWRAP_IN_RESULT, + impl_span, + "used unwrap or expect in a function that returns result or option", + move |diag| { + diag.help( + "unwrap and expect should not be used in a function that returns result or option" ); + diag.span_note(fpu.result, "potential non-recoverable error(s)"); + }); + } + } + } +} diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 776c6bc57ca..427a1b65773 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -50,7 +50,7 @@ declare_clippy_lint! { /// ``` pub USE_SELF, nursery, - "Unnecessary structure name repetition whereas `Self` is applicable" + "unnecessary structure name repetition whereas `Self` is applicable" } declare_lint_pass!(UseSelf => [USE_SELF]); diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 1bf37632e32..4ab2b5e796d 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -1,3 +1,4 @@ +use crate::utils::sugg::Sugg; use crate::utils::{ get_parent_expr, is_type_diagnostic_item, match_def_path, match_trait_method, paths, snippet, snippet_with_macro_callsite, span_lint_and_help, span_lint_and_sugg, @@ -158,7 +159,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { if TyS::same_type(a, b); then { - let sugg = snippet(cx, args[0].span.source_callsite(), "").into_owned(); + let sugg = Sugg::hir_with_macro_callsite(cx, &args[0], "").maybe_par(); let sugg_msg = format!("consider removing `{}()`", snippet(cx, path.span, "From::from")); span_lint_and_sugg( @@ -167,7 +168,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { e.span, "useless conversion to the same type", &sugg_msg, - sugg, + sugg.to_string(), Applicability::MachineApplicable, // snippet ); } diff --git a/clippy_lints/src/utils/ast_utils.rs b/clippy_lints/src/utils/ast_utils.rs index c32c80dcd3c..7b419431c0f 100644 --- a/clippy_lints/src/utils/ast_utils.rs +++ b/clippy_lints/src/utils/ast_utils.rs @@ -5,8 +5,8 @@ #![allow(clippy::similar_names, clippy::wildcard_imports, clippy::enum_glob_use)] use crate::utils::{both, over}; -use rustc_ast::{self as ast, *}; use rustc_ast::ptr::P; +use rustc_ast::{self as ast, *}; use rustc_span::symbol::Ident; use std::mem; diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index 9b7a268c628..6eda6d1fa83 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -175,18 +175,15 @@ impl PrintVisitor { } fn print_qpath(&mut self, path: &QPath<'_>) { - match *path { - QPath::LangItem(lang_item, _) => { - println!( - " if matches!({}, QPath::LangItem(LangItem::{:?}, _));", - self.current, lang_item, - ); - }, - _ => { - print!(" if match_qpath({}, &[", self.current); - print_path(path, &mut true); - println!("]);"); - }, + if let QPath::LangItem(lang_item, _) = *path { + println!( + " if matches!({}, QPath::LangItem(LangItem::{:?}, _));", + self.current, lang_item, + ); + } else { + print!(" if match_qpath({}, &[", self.current); + print_path(path, &mut true); + println!("]);"); } } } diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index ba3492a6fff..292dbd7ad6b 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -138,7 +138,7 @@ define_Conf! { (type_complexity_threshold, "type_complexity_threshold": u64, 250), /// Lint: MANY_SINGLE_CHAR_NAMES. The maximum number of single char bindings a scope may have (single_char_binding_names_threshold, "single_char_binding_names_threshold": u64, 4), - /// Lint: BOXED_LOCAL. The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap + /// Lint: BOXED_LOCAL, USELESS_VEC. The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap (too_large_for_stack, "too_large_for_stack": u64, 200), /// Lint: ENUM_VARIANT_NAMES. The minimum number of enum variants for the lints about variant names to trigger (enum_variant_name_threshold, "enum_variant_name_threshold": u64, 3), diff --git a/clippy_lints/src/utils/higher.rs b/clippy_lints/src/utils/higher.rs index ba15456014d..8563b469a30 100644 --- a/clippy_lints/src/utils/higher.rs +++ b/clippy_lints/src/utils/higher.rs @@ -56,43 +56,45 @@ pub fn range<'a>(expr: &'a hir::Expr<'_>) -> Option> { } match expr.kind { - hir::ExprKind::Call(ref path, ref args) if matches!( - path.kind, - hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::RangeInclusiveNew, _)) - ) => Some(Range { - start: Some(&args[0]), - end: Some(&args[1]), - limits: ast::RangeLimits::Closed, - }), - hir::ExprKind::Struct(ref path, ref fields, None) => { - match path { - hir::QPath::LangItem(hir::LangItem::RangeFull, _) => Some(Range { - start: None, - end: None, - limits: ast::RangeLimits::HalfOpen, - }), - hir::QPath::LangItem(hir::LangItem::RangeFrom, _) => Some(Range { - start: Some(get_field("start", fields)?), - end: None, - limits: ast::RangeLimits::HalfOpen, - }), - hir::QPath::LangItem(hir::LangItem::Range, _) => Some(Range { - start: Some(get_field("start", fields)?), - end: Some(get_field("end", fields)?), - limits: ast::RangeLimits::HalfOpen, - }), - hir::QPath::LangItem(hir::LangItem::RangeToInclusive, _) => Some(Range { - start: None, - end: Some(get_field("end", fields)?), - limits: ast::RangeLimits::Closed, - }), - hir::QPath::LangItem(hir::LangItem::RangeTo, _) => Some(Range { - start: None, - end: Some(get_field("end", fields)?), - limits: ast::RangeLimits::HalfOpen, - }), - _ => None, - } + hir::ExprKind::Call(ref path, ref args) + if matches!( + path.kind, + hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::RangeInclusiveNew, _)) + ) => + { + Some(Range { + start: Some(&args[0]), + end: Some(&args[1]), + limits: ast::RangeLimits::Closed, + }) + }, + hir::ExprKind::Struct(ref path, ref fields, None) => match path { + hir::QPath::LangItem(hir::LangItem::RangeFull, _) => Some(Range { + start: None, + end: None, + limits: ast::RangeLimits::HalfOpen, + }), + hir::QPath::LangItem(hir::LangItem::RangeFrom, _) => Some(Range { + start: Some(get_field("start", fields)?), + end: None, + limits: ast::RangeLimits::HalfOpen, + }), + hir::QPath::LangItem(hir::LangItem::Range, _) => Some(Range { + start: Some(get_field("start", fields)?), + end: Some(get_field("end", fields)?), + limits: ast::RangeLimits::HalfOpen, + }), + hir::QPath::LangItem(hir::LangItem::RangeToInclusive, _) => Some(Range { + start: None, + end: Some(get_field("end", fields)?), + limits: ast::RangeLimits::Closed, + }), + hir::QPath::LangItem(hir::LangItem::RangeTo, _) => Some(Range { + start: None, + end: Some(get_field("end", fields)?), + limits: ast::RangeLimits::HalfOpen, + }), + _ => None, }, _ => None, } diff --git a/clippy_lints/src/utils/hir_utils.rs b/clippy_lints/src/utils/hir_utils.rs index 2eefd4a38a6..c7263f48965 100644 --- a/clippy_lints/src/utils/hir_utils.rs +++ b/clippy_lints/src/utils/hir_utils.rs @@ -3,9 +3,9 @@ use crate::utils::differing_macro_contexts; use rustc_ast::ast::InlineAsmTemplatePiece; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_hir::{ - BinOpKind, Block, BlockCheckMode, BodyId, BorrowKind, CaptureBy, Expr, ExprKind, Field, FieldPat, - FnRetTy, GenericArg, GenericArgs, Guard, InlineAsmOperand, Lifetime, LifetimeName, ParamName, - Pat, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, Ty, TyKind, TypeBinding, + BinOpKind, Block, BlockCheckMode, BodyId, BorrowKind, CaptureBy, Expr, ExprKind, Field, FieldPat, FnRetTy, + GenericArg, GenericArgs, Guard, InlineAsmOperand, Lifetime, LifetimeName, ParamName, Pat, PatKind, Path, + PathSegment, QPath, Stmt, StmtKind, Ty, TyKind, TypeBinding, }; use rustc_lint::LateContext; use rustc_middle::ich::StableHashingContextProvider; @@ -23,9 +23,7 @@ pub struct SpanlessEq<'a, 'tcx> { /// Context used to evaluate constant expressions. cx: &'a LateContext<'tcx>, maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>, - /// If is true, never consider as equal expressions containing function - /// calls. - ignore_fn: bool, + allow_side_effects: bool, } impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { @@ -33,13 +31,14 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { Self { cx, maybe_typeck_results: cx.maybe_typeck_results(), - ignore_fn: false, + allow_side_effects: true, } } - pub fn ignore_fn(self) -> Self { + /// Consider expressions containing potential side effects as not equal. + pub fn deny_side_effects(self) -> Self { Self { - ignore_fn: true, + allow_side_effects: false, ..self } } @@ -67,7 +66,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { #[allow(clippy::similar_names)] pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool { - if self.ignore_fn && differing_macro_contexts(left.span, right.span) { + if !self.allow_side_effects && differing_macro_contexts(left.span, right.span) { return false; } @@ -90,10 +89,10 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { both(&li.label, &ri.label, |l, r| l.ident.as_str() == r.ident.as_str()) }, (&ExprKind::Assign(ref ll, ref lr, _), &ExprKind::Assign(ref rl, ref rr, _)) => { - self.eq_expr(ll, rl) && self.eq_expr(lr, rr) + self.allow_side_effects && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) }, (&ExprKind::AssignOp(ref lo, ref ll, ref lr), &ExprKind::AssignOp(ref ro, ref rl, ref rr)) => { - lo.node == ro.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) + self.allow_side_effects && lo.node == ro.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) }, (&ExprKind::Block(ref l, _), &ExprKind::Block(ref r, _)) => self.eq_block(l, r), (&ExprKind::Binary(l_op, ref ll, ref lr), &ExprKind::Binary(r_op, ref rl, ref rr)) => { @@ -108,7 +107,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { }, (&ExprKind::Box(ref l), &ExprKind::Box(ref r)) => self.eq_expr(l, r), (&ExprKind::Call(l_fun, l_args), &ExprKind::Call(r_fun, r_args)) => { - !self.ignore_fn && self.eq_expr(l_fun, r_fun) && self.eq_exprs(l_args, r_args) + self.allow_side_effects && self.eq_expr(l_fun, r_fun) && self.eq_exprs(l_args, r_args) }, (&ExprKind::Cast(ref lx, ref lt), &ExprKind::Cast(ref rx, ref rt)) | (&ExprKind::Type(ref lx, ref lt), &ExprKind::Type(ref rx, ref rt)) => { @@ -134,7 +133,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { }) }, (&ExprKind::MethodCall(l_path, _, l_args, _), &ExprKind::MethodCall(r_path, _, r_args, _)) => { - !self.ignore_fn && self.eq_path_segment(l_path, r_path) && self.eq_exprs(l_args, r_args) + self.allow_side_effects && self.eq_path_segment(l_path, r_path) && self.eq_exprs(l_args, r_args) }, (&ExprKind::Repeat(ref le, ref ll_id), &ExprKind::Repeat(ref re, ref rl_id)) => { let mut celcx = constant_context(self.cx, self.cx.tcx.typeck_body(ll_id.body)); @@ -186,10 +185,8 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { } pub fn eq_fieldpat(&mut self, left: &FieldPat<'_>, right: &FieldPat<'_>) -> bool { - match (&left, &right) { - (FieldPat { ident: li, pat: lp, .. }, FieldPat { ident: ri, pat: rp, .. }) => - li.name.as_str() == ri.name.as_str() && self.eq_pat(lp, rp), - } + let (FieldPat { ident: li, pat: lp, .. }, FieldPat { ident: ri, pat: rp, .. }) = (&left, &right); + li.name.as_str() == ri.name.as_str() && self.eq_pat(lp, rp) } /// Checks whether two patterns are the same. @@ -233,8 +230,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { (&QPath::TypeRelative(ref lty, ref lseg), &QPath::TypeRelative(ref rty, ref rseg)) => { self.eq_ty(lty, rty) && self.eq_path_segment(lseg, rseg) }, - (&QPath::LangItem(llang_item, _), &QPath::LangItem(rlang_item, _)) => - llang_item == rlang_item, + (&QPath::LangItem(llang_item, _), &QPath::LangItem(rlang_item, _)) => llang_item == rlang_item, _ => false, } } @@ -352,6 +348,11 @@ pub fn over(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) - left.len() == right.len() && left.iter().zip(right).all(|(x, y)| eq_fn(x, y)) } +/// Checks if two expressions evaluate to the same value, and don't contain any side effects. +pub fn eq_expr_value(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>) -> bool { + SpanlessEq::new(cx).deny_side_effects().eq_expr(left, right) +} + /// Type used to hash an ast element. This is different from the `Hash` trait /// on ast types as this /// trait would consider IDs and spans. @@ -615,7 +616,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { }, QPath::LangItem(lang_item, ..) => { lang_item.hash_stable(&mut self.cx.tcx.get_stable_hashing_context(), &mut self.s); - } + }, } // self.maybe_typeck_results.unwrap().qpath_res(p, id).hash(&mut self.s); } @@ -727,7 +728,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { }, QPath::LangItem(lang_item, ..) => { lang_item.hash(&mut self.s); - } + }, }, TyKind::OpaqueDef(_, arg_list) => { self.hash_generic_args(arg_list); diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 6c235679914..8fa5d22210a 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -1,7 +1,6 @@ -use crate::utils::SpanlessEq; use crate::utils::{ is_expn_of, match_def_path, match_qpath, match_type, method_calls, paths, run_lints, snippet, span_lint, - span_lint_and_help, span_lint_and_sugg, walk_ptrs_ty, + span_lint_and_help, span_lint_and_sugg, walk_ptrs_ty, SpanlessEq, }; use if_chain::if_chain; use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, NodeId}; @@ -493,7 +492,7 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls { if let StmtKind::Semi(only_expr) = &stmts[0].kind; if let ExprKind::MethodCall(ref ps, _, ref span_call_args, _) = &only_expr.kind; let and_then_snippets = get_and_then_snippets(cx, and_then_args); - let mut sle = SpanlessEq::new(cx).ignore_fn(); + let mut sle = SpanlessEq::new(cx).deny_side_effects(); then { match &*ps.ident.as_str() { "span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => { diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index a56b8203513..82005257115 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -21,7 +21,7 @@ pub mod sugg; pub mod usage; pub use self::attrs::*; pub use self::diagnostics::*; -pub use self::hir_utils::{both, over, SpanlessEq, SpanlessHash}; +pub use self::hir_utils::{both, eq_expr_value, over, SpanlessEq, SpanlessHash}; use std::borrow::Cow; use std::mem; @@ -42,7 +42,8 @@ use rustc_hir::{ use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::map::Map; -use rustc_middle::ty::{self, layout::IntegerExt, subst::GenericArg, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; +use rustc_middle::ty::{self, layout::IntegerExt, Ty, TyCtxt, TypeFoldable}; use rustc_mir::const_eval; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::original_sp; @@ -574,7 +575,7 @@ pub fn snippet_block<'a, T: LintContext>( } /// Same as `snippet_block`, but adapts the applicability level by the rules of -/// `snippet_with_applicabiliy`. +/// `snippet_with_applicability`. pub fn snippet_block_with_applicability<'a, T: LintContext>( cx: &T, span: Span, @@ -866,6 +867,14 @@ pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_item: hir::HirId) -> Ty<'tcx> cx.tcx.erase_late_bound_regions(&ret_ty) } +/// Walks into `ty` and returns `true` if any inner type is the same as `other_ty` +pub fn contains_ty(ty: Ty<'_>, other_ty: Ty<'_>) -> bool { + ty.walk().any(|inner| match inner.unpack() { + GenericArgKind::Type(inner_ty) => ty::TyS::same_type(other_ty, inner_ty), + GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, + }) +} + /// Returns `true` if the given type is an `unsafe` function. pub fn type_is_unsafe_function<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { match ty.kind { @@ -1304,7 +1313,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { } } -// check if expr is calling method or function with #[must_use] attribyte +// check if expr is calling method or function with #[must_use] attribute pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let did = match expr.kind { ExprKind::Call(ref path, _) => if_chain! { @@ -1409,11 +1418,13 @@ pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool { } } -/// Returns true iff the given expression is a slice of primitives (as defined in the -/// `is_recursively_primitive_type` function). -pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { +/// Returns Option where String is a textual representation of the type encapsulated in the +/// slice iff the given expression is a slice of primitives (as defined in the +/// `is_recursively_primitive_type` function) and None otherwise. +pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { let expr_type = cx.typeck_results().expr_ty_adjusted(expr); - match expr_type.kind { + let expr_kind = &expr_type.kind; + let is_primitive = match expr_kind { ty::Slice(ref element_type) | ty::Ref( _, @@ -1424,7 +1435,24 @@ pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { _, ) => is_recursively_primitive_type(element_type), _ => false, + }; + + if is_primitive { + // if we have wrappers like Array, Slice or Tuple, print these + // and get the type enclosed in the slice ref + match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind { + ty::Slice(..) => return Some("slice".into()), + ty::Array(..) => return Some("array".into()), + ty::Tuple(..) => return Some("tuple".into()), + _ => { + // is_recursively_primitive_type() should have taken care + // of the rest and we can rely on the type that is found + let refs_peeled = expr_type.peel_refs(); + return Some(refs_peeled.walk().last().unwrap().to_string()); + }, + } } + None } #[macro_export] diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 9c28d63d414..d44854aefe9 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -35,6 +35,8 @@ pub const DROP_TRAIT: [&str; 4] = ["core", "ops", "drop", "Drop"]; pub const DURATION: [&str; 3] = ["core", "time", "Duration"]; pub const EARLY_CONTEXT: [&str; 4] = ["rustc", "lint", "context", "EarlyContext"]; pub const EXIT: [&str; 3] = ["std", "process", "exit"]; +pub const F32_EPSILON: [&str; 2] = ["f32", "EPSILON"]; +pub const F64_EPSILON: [&str; 2] = ["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"]; @@ -84,6 +86,7 @@ pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"]; pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"]; pub const PTR_NULL: [&str; 2] = ["ptr", "null"]; pub const PTR_NULL_MUT: [&str; 2] = ["ptr", "null_mut"]; +pub const PUSH_STR: [&str; 4] = ["alloc", "string", "String", "push_str"]; pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"]; pub const RC: [&str; 3] = ["alloc", "rc", "Rc"]; pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"]; diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index f2e76442a19..84e907d7125 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -1,13 +1,20 @@ -use crate::consts::constant; +use crate::consts::{constant, Constant}; +use crate::rustc_target::abi::LayoutOf; use crate::utils::{higher, is_copy, snippet_with_applicability, span_lint_and_sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; +#[allow(clippy::module_name_repetitions)] +#[derive(Copy, Clone)] +pub struct UselessVec { + pub too_large_for_stack: u64, +} + declare_clippy_lint! { /// **What it does:** Checks for usage of `&vec![..]` when using `&[..]` would /// be possible. @@ -31,7 +38,7 @@ declare_clippy_lint! { "useless `vec!`" } -declare_lint_pass!(UselessVec => [USELESS_VEC]); +impl_lint_pass!(UselessVec => [USELESS_VEC]); impl<'tcx> LateLintPass<'tcx> for UselessVec { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { @@ -42,7 +49,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { if let ExprKind::AddrOf(BorrowKind::Ref, _, ref addressee) = expr.kind; if let Some(vec_args) = higher::vec_macro(cx, addressee); then { - check_vec_macro(cx, &vec_args, expr.span); + self.check_vec_macro(cx, &vec_args, expr.span); } } @@ -60,46 +67,62 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { .ctxt() .outer_expn_data() .call_site; - check_vec_macro(cx, &vec_args, span); + self.check_vec_macro(cx, &vec_args, span); } } } } -fn check_vec_macro<'tcx>(cx: &LateContext<'tcx>, vec_args: &higher::VecArgs<'tcx>, span: Span) { - let mut applicability = Applicability::MachineApplicable; - let snippet = match *vec_args { - higher::VecArgs::Repeat(elem, len) => { - if constant(cx, cx.typeck_results(), len).is_some() { - format!( - "&[{}; {}]", - snippet_with_applicability(cx, elem.span, "elem", &mut applicability), - snippet_with_applicability(cx, len.span, "len", &mut applicability) - ) - } else { - return; - } - }, - higher::VecArgs::Vec(args) => { - if let Some(last) = args.iter().last() { - let span = args[0].span.to(last.span); +impl UselessVec { + fn check_vec_macro<'tcx>(self, cx: &LateContext<'tcx>, vec_args: &higher::VecArgs<'tcx>, span: Span) { + let mut applicability = Applicability::MachineApplicable; + let snippet = match *vec_args { + higher::VecArgs::Repeat(elem, len) => { + if let Some((Constant::Int(len_constant), _)) = constant(cx, cx.typeck_results(), len) { + #[allow(clippy::cast_possible_truncation)] + if len_constant as u64 * size_of(cx, elem) > self.too_large_for_stack { + return; + } - format!("&[{}]", snippet_with_applicability(cx, span, "..", &mut applicability)) - } else { - "&[]".into() - } - }, - }; + format!( + "&[{}; {}]", + snippet_with_applicability(cx, elem.span, "elem", &mut applicability), + snippet_with_applicability(cx, len.span, "len", &mut applicability) + ) + } else { + return; + } + }, + higher::VecArgs::Vec(args) => { + if let Some(last) = args.iter().last() { + #[allow(clippy::cast_possible_truncation)] + if args.len() as u64 * size_of(cx, last) > self.too_large_for_stack { + return; + } + let span = args[0].span.to(last.span); - span_lint_and_sugg( - cx, - USELESS_VEC, - span, - "useless use of `vec!`", - "you can use a slice directly", - snippet, - applicability, - ); + format!("&[{}]", snippet_with_applicability(cx, span, "..", &mut applicability)) + } else { + "&[]".into() + } + }, + }; + + span_lint_and_sugg( + cx, + USELESS_VEC, + span, + "useless use of `vec!`", + "you can use a slice directly", + snippet, + applicability, + ); + } +} + +fn size_of(cx: &LateContext<'_>, expr: &Expr<'_>) -> u64 { + let ty = cx.typeck_results().expr_ty_adjusted(expr); + cx.layout_of(ty).map_or(0, |l| l.size.bytes()) } /// Returns the item type of the vector (i.e., the `T` in `Vec`). diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index e7eb7c2e980..5683a71efea 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -195,13 +195,10 @@ impl WildcardImports { } } -// Allow "...prelude::*" imports. +// Allow "...prelude::..::*" imports. // Many crates have a prelude, and it is imported as a glob by design. fn is_prelude_import(segments: &[PathSegment<'_>]) -> bool { - segments - .iter() - .last() - .map_or(false, |ps| ps.ident.as_str() == "prelude") + segments.iter().any(|ps| ps.ident.as_str() == "prelude") } // Allow "super::*" imports in tests. diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 063f94582b9..e653240d049 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -237,7 +237,7 @@ impl EarlyLintPass for Write { fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &MacCall) { if mac.path == sym!(println) { span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`"); - if let (Some(fmt_str), _) = self.check_tts(cx, &mac.args.inner_tokens(), false) { + if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { if fmt_str.symbol == Symbol::intern("") { span_lint_and_sugg( cx, @@ -252,7 +252,7 @@ impl EarlyLintPass for Write { } } else if mac.path == sym!(print) { span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`"); - if let (Some(fmt_str), _) = self.check_tts(cx, &mac.args.inner_tokens(), false) { + if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { if check_newlines(&fmt_str) { span_lint_and_then( cx, @@ -273,7 +273,7 @@ impl EarlyLintPass for Write { } } } else if mac.path == sym!(write) { - if let (Some(fmt_str), _) = self.check_tts(cx, &mac.args.inner_tokens(), true) { + if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), true) { if check_newlines(&fmt_str) { span_lint_and_then( cx, @@ -294,16 +294,17 @@ impl EarlyLintPass for Write { } } } else if mac.path == sym!(writeln) { - if let (Some(fmt_str), expr) = self.check_tts(cx, &mac.args.inner_tokens(), true) { + if let (Some(fmt_str), expr) = self.check_tts(cx, mac.args.inner_tokens(), true) { if fmt_str.symbol == Symbol::intern("") { let mut applicability = Applicability::MachineApplicable; - let suggestion = expr.map_or_else( - || { - applicability = Applicability::HasPlaceholders; - Cow::Borrowed("v") - }, - |e| snippet_with_applicability(cx, e.span, "v", &mut Applicability::MachineApplicable), - ); + // FIXME: remove this `#[allow(...)]` once the issue #5822 gets fixed + #[allow(clippy::option_if_let_else)] + let suggestion = if let Some(e) = expr { + snippet_with_applicability(cx, e.span, "v", &mut applicability) + } else { + applicability = Applicability::HasPlaceholders; + Cow::Borrowed("v") + }; span_lint_and_sugg( cx, @@ -364,17 +365,11 @@ impl Write { /// (Some("string to write: {}"), Some(buf)) /// ``` #[allow(clippy::too_many_lines)] - fn check_tts<'a>( - &self, - cx: &EarlyContext<'a>, - tts: &TokenStream, - is_write: bool, - ) -> (Option, Option) { + fn check_tts<'a>(&self, cx: &EarlyContext<'a>, tts: TokenStream, is_write: bool) -> (Option, Option) { use rustc_parse_format::{ AlignUnknown, ArgumentImplicitlyIs, ArgumentIs, ArgumentNamed, CountImplied, FormatSpec, ParseMode, Parser, Piece, }; - let tts = tts.clone(); let mut parser = parser::Parser::new(&cx.sess.parse_sess, tts, false, None); let mut expr: Option = None; diff --git a/doc/adding_lints.md b/doc/adding_lints.md index 168092f7329..3c782e9b17f 100644 --- a/doc/adding_lints.md +++ b/doc/adding_lints.md @@ -295,8 +295,14 @@ impl EarlyLintPass for FooFunctions { Running our UI test should now produce output that contains the lint message. +According to [the rustc-dev-guide], the text should be matter of fact and avoid +capitalization and periods, unless multiple sentences are needed. +When code or an identifier must appear in a message or label, it should be +surrounded with single acute accents \`. + [check_fn]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html#method.check_fn [diagnostics]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/utils/diagnostics.rs +[the rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/diagnostics.html ## Adding the lint logic diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index bbb300296be..687fac7baa8 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -661,6 +661,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "misc", }, + Lint { + name: "float_equality_without_abs", + group: "correctness", + desc: "float equality check without `.abs()`", + deprecation: None, + module: "float_equality_without_abs", + }, Lint { name: "fn_address_comparisons", group: "correctness", @@ -1037,7 +1044,7 @@ pub static ref ALL_LINTS: Vec = vec![ group: "style", desc: "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block", deprecation: None, - module: "let_and_return", + module: "returns", }, Lint { name: "let_underscore_lock", @@ -1956,6 +1963,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "methods", }, + Lint { + name: "self_assignment", + group: "correctness", + desc: "explicit self-assignment", + deprecation: None, + module: "self_assignment", + }, Lint { name: "serde_api_misuse", group: "correctness", @@ -2012,6 +2026,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "methods", }, + Lint { + name: "single_char_push_str", + group: "style", + desc: "`push_str()` used with a single-character string literal as parameter", + deprecation: None, + module: "methods", + }, Lint { name: "single_component_path_imports", group: "style", @@ -2166,6 +2187,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "to_digit_is_some", }, + Lint { + name: "to_string_in_display", + group: "correctness", + desc: "`to_string` method used while implementing `Display` trait", + deprecation: None, + module: "to_string_in_display", + }, Lint { name: "todo", group: "restriction", @@ -2369,6 +2397,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "methods", }, + Lint { + name: "unnecessary_lazy_evaluations", + group: "style", + desc: "using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation", + deprecation: None, + module: "methods", + }, Lint { name: "unnecessary_mut_passed", group: "style", @@ -2479,7 +2514,14 @@ pub static ref ALL_LINTS: Vec = vec![ group: "style", desc: "needless unit expression", deprecation: None, - module: "returns", + module: "unused_unit", + }, + Lint { + name: "unwrap_in_result", + group: "restriction", + desc: "functions of type `Result<..>` or `Option`<...> that contain `expect()` or `unwrap()`", + deprecation: None, + module: "unwrap_in_result", }, Lint { name: "unwrap_used", @@ -2498,7 +2540,7 @@ pub static ref ALL_LINTS: Vec = vec![ Lint { name: "use_self", group: "nursery", - desc: "Unnecessary structure name repetition whereas `Self` is applicable", + desc: "unnecessary structure name repetition whereas `Self` is applicable", deprecation: None, module: "use_self", }, diff --git a/tests/fmt.rs b/tests/fmt.rs index 3aff8741f60..7616d8001e8 100644 --- a/tests/fmt.rs +++ b/tests/fmt.rs @@ -7,7 +7,7 @@ fn fmt() { return; } - // Skip this test if rustup nightly is unavailable + // Skip this test if nightly rustfmt is unavailable let rustup_output = Command::new("rustup") .args(&["component", "list", "--toolchain", "nightly"]) .output() @@ -19,12 +19,9 @@ fn fmt() { } let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - let dev_dir = root_dir.join("clippy_dev"); - let target_dir = root_dir.join("target"); - let target_dir = target_dir.to_str().unwrap(); let output = Command::new("cargo") - .current_dir(dev_dir) - .args(&["+nightly", "run", "--target-dir", target_dir, "--", "fmt", "--check"]) + .current_dir(root_dir) + .args(&["dev", "fmt", "--check"]) .output() .unwrap(); diff --git a/tests/ui/auxiliary/wildcard_imports_helper.rs b/tests/ui/auxiliary/wildcard_imports_helper.rs index 414477aedd7..d75cdd625f9 100644 --- a/tests/ui/auxiliary/wildcard_imports_helper.rs +++ b/tests/ui/auxiliary/wildcard_imports_helper.rs @@ -19,3 +19,9 @@ mod extern_exports { A, } } + +pub mod prelude { + pub mod v1 { + pub struct PreludeModAnywhere; + } +} diff --git a/tests/ui/borrow_interior_mutable_const.rs b/tests/ui/borrow_interior_mutable_const.rs index fef9f4f39f8..a414832bcd3 100644 --- a/tests/ui/borrow_interior_mutable_const.rs +++ b/tests/ui/borrow_interior_mutable_const.rs @@ -2,7 +2,7 @@ #![allow(clippy::declare_interior_mutable_const, clippy::ref_in_deref)] use std::borrow::Cow; -use std::cell::Cell; +use std::cell::{Cell, UnsafeCell}; use std::fmt::Display; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Once; @@ -30,6 +30,37 @@ impl Trait for u64 { const ATOMIC: AtomicUsize = AtomicUsize::new(9); } +// This is just a pointer that can be safely dereferended, +// it's semantically the same as `&'static T`; +// but it isn't allowed to make a static reference from an arbitrary integer value at the moment. +// For more information, please see the issue #5918. +pub struct StaticRef { + ptr: *const T, +} + +impl StaticRef { + /// Create a new `StaticRef` from a raw pointer + /// + /// ## Safety + /// + /// Callers must pass in a reference to statically allocated memory which + /// does not overlap with other values. + pub const unsafe fn new(ptr: *const T) -> StaticRef { + StaticRef { ptr } + } +} + +impl std::ops::Deref for StaticRef { + type Target = T; + + fn deref(&self) -> &'static T { + unsafe { &*self.ptr } + } +} + +// use a tuple to make sure referencing a field behind a pointer isn't linted. +const CELL_REF: StaticRef<(UnsafeCell,)> = unsafe { StaticRef::new(std::ptr::null()) }; + fn main() { ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutability @@ -82,4 +113,6 @@ fn main() { assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); //~ ERROR interior mutability assert_eq!(NO_ANN.to_string(), "70"); // should never lint this. + + let _ = &CELL_REF.0; } diff --git a/tests/ui/borrow_interior_mutable_const.stderr b/tests/ui/borrow_interior_mutable_const.stderr index dc738064a17..1e0b3e4d20a 100644 --- a/tests/ui/borrow_interior_mutable_const.stderr +++ b/tests/ui/borrow_interior_mutable_const.stderr @@ -1,5 +1,5 @@ error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:34:5 + --> $DIR/borrow_interior_mutable_const.rs:65:5 | LL | ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability | ^^^^^^ @@ -8,7 +8,7 @@ LL | ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:35:16 + --> $DIR/borrow_interior_mutable_const.rs:66:16 | LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutability | ^^^^^^ @@ -16,7 +16,7 @@ LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutabi = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:38:22 + --> $DIR/borrow_interior_mutable_const.rs:69:22 | LL | let _once_ref = &ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -24,7 +24,7 @@ LL | let _once_ref = &ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:39:25 + --> $DIR/borrow_interior_mutable_const.rs:70:25 | LL | let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -32,7 +32,7 @@ LL | let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:40:27 + --> $DIR/borrow_interior_mutable_const.rs:71:27 | LL | let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -40,7 +40,7 @@ LL | let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:41:26 + --> $DIR/borrow_interior_mutable_const.rs:72:26 | LL | let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -48,7 +48,7 @@ LL | let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:52:14 + --> $DIR/borrow_interior_mutable_const.rs:83:14 | LL | let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -56,7 +56,7 @@ LL | let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:53:14 + --> $DIR/borrow_interior_mutable_const.rs:84:14 | LL | let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -64,7 +64,7 @@ LL | let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:54:19 + --> $DIR/borrow_interior_mutable_const.rs:85:19 | LL | let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL | let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:55:14 + --> $DIR/borrow_interior_mutable_const.rs:86:14 | LL | let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -80,7 +80,7 @@ LL | let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:56:13 + --> $DIR/borrow_interior_mutable_const.rs:87:13 | LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -88,7 +88,7 @@ LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mu = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:62:13 + --> $DIR/borrow_interior_mutable_const.rs:93:13 | LL | let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -96,7 +96,7 @@ LL | let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:67:5 + --> $DIR/borrow_interior_mutable_const.rs:98:5 | LL | CELL.set(2); //~ ERROR interior mutability | ^^^^ @@ -104,7 +104,7 @@ LL | CELL.set(2); //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:68:16 + --> $DIR/borrow_interior_mutable_const.rs:99:16 | LL | assert_eq!(CELL.get(), 6); //~ ERROR interior mutability | ^^^^ @@ -112,7 +112,7 @@ LL | assert_eq!(CELL.get(), 6); //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:81:5 + --> $DIR/borrow_interior_mutable_const.rs:112:5 | LL | u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability | ^^^^^^^^^^^ @@ -120,7 +120,7 @@ LL | u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:82:16 + --> $DIR/borrow_interior_mutable_const.rs:113:16 | LL | assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); //~ ERROR interior mutability | ^^^^^^^^^^^ diff --git a/tests/ui/crashes/ice-5944.rs b/tests/ui/crashes/ice-5944.rs new file mode 100644 index 00000000000..5caf29c6197 --- /dev/null +++ b/tests/ui/crashes/ice-5944.rs @@ -0,0 +1,13 @@ +#![warn(clippy::repeat_once)] + +trait Repeat { + fn repeat(&self) {} +} + +impl Repeat for usize { + fn repeat(&self) {} +} + +fn main() { + let _ = 42.repeat(); +} diff --git a/tests/ui/duration_subsec.stderr b/tests/ui/duration_subsec.stderr index bd8adc2c570..cdbeff6a037 100644 --- a/tests/ui/duration_subsec.stderr +++ b/tests/ui/duration_subsec.stderr @@ -1,4 +1,4 @@ -error: Calling `subsec_millis()` is more concise than this calculation +error: calling `subsec_millis()` is more concise than this calculation --> $DIR/duration_subsec.rs:10:24 | LL | let bad_millis_1 = dur.subsec_micros() / 1_000; @@ -6,25 +6,25 @@ LL | let bad_millis_1 = dur.subsec_micros() / 1_000; | = note: `-D clippy::duration-subsec` implied by `-D warnings` -error: Calling `subsec_millis()` is more concise than this calculation +error: calling `subsec_millis()` is more concise than this calculation --> $DIR/duration_subsec.rs:11:24 | LL | let bad_millis_2 = dur.subsec_nanos() / 1_000_000; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `dur.subsec_millis()` -error: Calling `subsec_micros()` is more concise than this calculation +error: calling `subsec_micros()` is more concise than this calculation --> $DIR/duration_subsec.rs:16:22 | LL | let bad_micros = dur.subsec_nanos() / 1_000; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `dur.subsec_micros()` -error: Calling `subsec_micros()` is more concise than this calculation +error: calling `subsec_micros()` is more concise than this calculation --> $DIR/duration_subsec.rs:21:13 | LL | let _ = (&dur).subsec_nanos() / 1_000; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(&dur).subsec_micros()` -error: Calling `subsec_micros()` is more concise than this calculation +error: calling `subsec_micros()` is more concise than this calculation --> $DIR/duration_subsec.rs:25:13 | LL | let _ = dur.subsec_nanos() / NANOS_IN_MICRO; diff --git a/tests/ui/enum_clike_unportable_variant.stderr b/tests/ui/enum_clike_unportable_variant.stderr index 71f3f5e083e..5935eea5e03 100644 --- a/tests/ui/enum_clike_unportable_variant.stderr +++ b/tests/ui/enum_clike_unportable_variant.stderr @@ -1,4 +1,4 @@ -error: Clike enum variant discriminant is not portable to 32-bit targets +error: C-like enum variant discriminant is not portable to 32-bit targets --> $DIR/enum_clike_unportable_variant.rs:8:5 | LL | X = 0x1_0000_0000, @@ -6,49 +6,49 @@ LL | X = 0x1_0000_0000, | = note: `-D clippy::enum-clike-unportable-variant` implied by `-D warnings` -error: Clike enum variant discriminant is not portable to 32-bit targets +error: C-like enum variant discriminant is not portable to 32-bit targets --> $DIR/enum_clike_unportable_variant.rs:15:5 | LL | X = 0x1_0000_0000, | ^^^^^^^^^^^^^^^^^ -error: Clike enum variant discriminant is not portable to 32-bit targets +error: C-like enum variant discriminant is not portable to 32-bit targets --> $DIR/enum_clike_unportable_variant.rs:18:5 | LL | A = 0xFFFF_FFFF, | ^^^^^^^^^^^^^^^ -error: Clike enum variant discriminant is not portable to 32-bit targets +error: C-like enum variant discriminant is not portable to 32-bit targets --> $DIR/enum_clike_unportable_variant.rs:25:5 | LL | Z = 0xFFFF_FFFF, | ^^^^^^^^^^^^^^^ -error: Clike enum variant discriminant is not portable to 32-bit targets +error: C-like enum variant discriminant is not portable to 32-bit targets --> $DIR/enum_clike_unportable_variant.rs:26:5 | LL | A = 0x1_0000_0000, | ^^^^^^^^^^^^^^^^^ -error: Clike enum variant discriminant is not portable to 32-bit targets +error: C-like enum variant discriminant is not portable to 32-bit targets --> $DIR/enum_clike_unportable_variant.rs:28:5 | LL | C = (i32::MIN as isize) - 1, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: Clike enum variant discriminant is not portable to 32-bit targets +error: C-like enum variant discriminant is not portable to 32-bit targets --> $DIR/enum_clike_unportable_variant.rs:34:5 | LL | Z = 0xFFFF_FFFF, | ^^^^^^^^^^^^^^^ -error: Clike enum variant discriminant is not portable to 32-bit targets +error: C-like enum variant discriminant is not portable to 32-bit targets --> $DIR/enum_clike_unportable_variant.rs:35:5 | LL | A = 0x1_0000_0000, | ^^^^^^^^^^^^^^^^^ -error: Clike enum variant discriminant is not portable to 32-bit targets +error: C-like enum variant discriminant is not portable to 32-bit targets --> $DIR/enum_clike_unportable_variant.rs:40:5 | LL | X = ::Number, diff --git a/tests/ui/enum_variants.stderr b/tests/ui/enum_variants.stderr index 2835391de7f..b1d481190ff 100644 --- a/tests/ui/enum_variants.stderr +++ b/tests/ui/enum_variants.stderr @@ -1,4 +1,4 @@ -error: Variant name ends with the enum's name +error: variant name ends with the enum's name --> $DIR/enum_variants.rs:16:5 | LL | cFoo, @@ -6,25 +6,25 @@ LL | cFoo, | = note: `-D clippy::enum-variant-names` implied by `-D warnings` -error: Variant name starts with the enum's name +error: variant name starts with the enum's name --> $DIR/enum_variants.rs:27:5 | LL | FoodGood, | ^^^^^^^^ -error: Variant name starts with the enum's name +error: variant name starts with the enum's name --> $DIR/enum_variants.rs:28:5 | LL | FoodMiddle, | ^^^^^^^^^^ -error: Variant name starts with the enum's name +error: variant name starts with the enum's name --> $DIR/enum_variants.rs:29:5 | LL | FoodBad, | ^^^^^^^ -error: All variants have the same prefix: `Food` +error: all variants have the same prefix: `Food` --> $DIR/enum_variants.rs:26:1 | LL | / enum Food { @@ -36,7 +36,7 @@ LL | | } | = help: remove the prefixes and use full paths to the variants instead of glob imports -error: All variants have the same prefix: `CallType` +error: all variants have the same prefix: `CallType` --> $DIR/enum_variants.rs:36:1 | LL | / enum BadCallType { @@ -48,7 +48,7 @@ LL | | } | = help: remove the prefixes and use full paths to the variants instead of glob imports -error: All variants have the same prefix: `Constant` +error: all variants have the same prefix: `Constant` --> $DIR/enum_variants.rs:48:1 | LL | / enum Consts { @@ -60,7 +60,7 @@ LL | | } | = help: remove the prefixes and use full paths to the variants instead of glob imports -error: All variants have the same prefix: `With` +error: all variants have the same prefix: `With` --> $DIR/enum_variants.rs:82:1 | LL | / enum Seallll { @@ -72,7 +72,7 @@ LL | | } | = help: remove the prefixes and use full paths to the variants instead of glob imports -error: All variants have the same prefix: `Prefix` +error: all variants have the same prefix: `Prefix` --> $DIR/enum_variants.rs:88:1 | LL | / enum NonCaps { @@ -84,7 +84,7 @@ LL | | } | = help: remove the prefixes and use full paths to the variants instead of glob imports -error: All variants have the same prefix: `With` +error: all variants have the same prefix: `With` --> $DIR/enum_variants.rs:94:1 | LL | / pub enum PubSeall { diff --git a/tests/ui/float_equality_without_abs.rs b/tests/ui/float_equality_without_abs.rs new file mode 100644 index 00000000000..d40fa00c315 --- /dev/null +++ b/tests/ui/float_equality_without_abs.rs @@ -0,0 +1,31 @@ +#![warn(clippy::float_equality_without_abs)] + +pub fn is_roughly_equal(a: f32, b: f32) -> bool { + (a - b) < f32::EPSILON +} + +pub fn main() { + // all errors + is_roughly_equal(1.0, 2.0); + let a = 0.05; + let b = 0.0500001; + + let _ = (a - b) < f32::EPSILON; + let _ = a - b < f32::EPSILON; + let _ = a - b.abs() < f32::EPSILON; + let _ = (a as f64 - b as f64) < f64::EPSILON; + let _ = 1.0 - 2.0 < f32::EPSILON; + + let _ = f32::EPSILON > (a - b); + let _ = f32::EPSILON > a - b; + let _ = f32::EPSILON > a - b.abs(); + let _ = f64::EPSILON > (a as f64 - b as f64); + let _ = f32::EPSILON > 1.0 - 2.0; + + // those are correct + let _ = (a - b).abs() < f32::EPSILON; + let _ = (a as f64 - b as f64).abs() < f64::EPSILON; + + let _ = f32::EPSILON > (a - b).abs(); + let _ = f64::EPSILON > (a as f64 - b as f64).abs(); +} diff --git a/tests/ui/float_equality_without_abs.stderr b/tests/ui/float_equality_without_abs.stderr new file mode 100644 index 00000000000..b34c8159da0 --- /dev/null +++ b/tests/ui/float_equality_without_abs.stderr @@ -0,0 +1,92 @@ +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:4:5 + | +LL | (a - b) < f32::EPSILON + | -------^^^^^^^^^^^^^^^ + | | + | help: add `.abs()`: `(a - b).abs()` + | + = note: `-D clippy::float-equality-without-abs` implied by `-D warnings` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:13:13 + | +LL | let _ = (a - b) < f32::EPSILON; + | -------^^^^^^^^^^^^^^^ + | | + | help: add `.abs()`: `(a - b).abs()` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:14:13 + | +LL | let _ = a - b < f32::EPSILON; + | -----^^^^^^^^^^^^^^^ + | | + | help: add `.abs()`: `(a - b).abs()` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:15:13 + | +LL | let _ = a - b.abs() < f32::EPSILON; + | -----------^^^^^^^^^^^^^^^ + | | + | help: add `.abs()`: `(a - b.abs()).abs()` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:16:13 + | +LL | let _ = (a as f64 - b as f64) < f64::EPSILON; + | ---------------------^^^^^^^^^^^^^^^ + | | + | help: add `.abs()`: `(a as f64 - b as f64).abs()` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:17:13 + | +LL | let _ = 1.0 - 2.0 < f32::EPSILON; + | ---------^^^^^^^^^^^^^^^ + | | + | help: add `.abs()`: `(1.0 - 2.0).abs()` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:19:13 + | +LL | let _ = f32::EPSILON > (a - b); + | ^^^^^^^^^^^^^^^------- + | | + | help: add `.abs()`: `(a - b).abs()` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:20:13 + | +LL | let _ = f32::EPSILON > a - b; + | ^^^^^^^^^^^^^^^----- + | | + | help: add `.abs()`: `(a - b).abs()` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:21:13 + | +LL | let _ = f32::EPSILON > a - b.abs(); + | ^^^^^^^^^^^^^^^----------- + | | + | help: add `.abs()`: `(a - b.abs()).abs()` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:22:13 + | +LL | let _ = f64::EPSILON > (a as f64 - b as f64); + | ^^^^^^^^^^^^^^^--------------------- + | | + | help: add `.abs()`: `(a as f64 - b as f64).abs()` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:23:13 + | +LL | let _ = f32::EPSILON > 1.0 - 2.0; + | ^^^^^^^^^^^^^^^--------- + | | + | help: add `.abs()`: `(1.0 - 2.0).abs()` + +error: aborting due to 11 previous errors + diff --git a/tests/ui/if_let_some_result.stderr b/tests/ui/if_let_some_result.stderr index 334ccb01016..6afee0f36b9 100644 --- a/tests/ui/if_let_some_result.stderr +++ b/tests/ui/if_let_some_result.stderr @@ -1,22 +1,22 @@ -error: Matching on `Some` with `ok()` is redundant +error: matching on `Some` with `ok()` is redundant --> $DIR/if_let_some_result.rs:6:5 | LL | if let Some(y) = x.parse().ok() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::if-let-some-result` implied by `-D warnings` -help: Consider matching on `Ok(y)` and removing the call to `ok` instead +help: consider matching on `Ok(y)` and removing the call to `ok` instead | LL | if let Ok(y) = x.parse() { | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: Matching on `Some` with `ok()` is redundant +error: matching on `Some` with `ok()` is redundant --> $DIR/if_let_some_result.rs:24:9 | LL | if let Some(y) = x . parse() . ok () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: Consider matching on `Ok(y)` and removing the call to `ok` instead +help: consider matching on `Ok(y)` and removing the call to `ok` instead | LL | if let Ok(y) = x . parse() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/if_not_else.stderr b/tests/ui/if_not_else.stderr index 78bc4d4bd20..53d1b86d02a 100644 --- a/tests/ui/if_not_else.stderr +++ b/tests/ui/if_not_else.stderr @@ -1,4 +1,4 @@ -error: Unnecessary boolean `not` operation +error: unnecessary boolean `not` operation --> $DIR/if_not_else.rs:9:5 | LL | / if !bla() { @@ -11,7 +11,7 @@ LL | | } = note: `-D clippy::if-not-else` implied by `-D warnings` = help: remove the `!` and swap the blocks of the `if`/`else` -error: Unnecessary `!=` operation +error: unnecessary `!=` operation --> $DIR/if_not_else.rs:14:5 | LL | / if 4 != 5 { diff --git a/tests/ui/impl.stderr b/tests/ui/impl.stderr index 585d32845d2..aab688cc2d8 100644 --- a/tests/ui/impl.stderr +++ b/tests/ui/impl.stderr @@ -1,4 +1,4 @@ -error: Multiple implementations of this structure +error: multiple implementations of this structure --> $DIR/impl.rs:10:1 | LL | / impl MyStruct { @@ -7,7 +7,7 @@ LL | | } | |_^ | = note: `-D clippy::multiple-inherent-impl` implied by `-D warnings` -note: First implementation here +note: first implementation here --> $DIR/impl.rs:6:1 | LL | / impl MyStruct { @@ -15,7 +15,7 @@ LL | | fn first() {} LL | | } | |_^ -error: Multiple implementations of this structure +error: multiple implementations of this structure --> $DIR/impl.rs:24:5 | LL | / impl super::MyStruct { @@ -23,7 +23,7 @@ LL | | fn third() {} LL | | } | |_____^ | -note: First implementation here +note: first implementation here --> $DIR/impl.rs:6:1 | LL | / impl MyStruct { diff --git a/tests/ui/implicit_saturating_sub.stderr b/tests/ui/implicit_saturating_sub.stderr index 2eb2023b3b9..5bb9a606422 100644 --- a/tests/ui/implicit_saturating_sub.stderr +++ b/tests/ui/implicit_saturating_sub.stderr @@ -1,4 +1,4 @@ -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:13:5 | LL | / if u_8 > 0 { @@ -8,7 +8,7 @@ LL | | } | = note: `-D clippy::implicit-saturating-sub` implied by `-D warnings` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:20:13 | LL | / if u_8 > 0 { @@ -16,7 +16,7 @@ LL | | u_8 -= 1; LL | | } | |_____________^ help: try: `u_8 = u_8.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:34:5 | LL | / if u_16 > 0 { @@ -24,7 +24,7 @@ LL | | u_16 -= 1; LL | | } | |_____^ help: try: `u_16 = u_16.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:44:5 | LL | / if u_32 != 0 { @@ -32,7 +32,7 @@ LL | | u_32 -= 1; LL | | } | |_____^ help: try: `u_32 = u_32.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:65:5 | LL | / if u_64 > 0 { @@ -40,7 +40,7 @@ LL | | u_64 -= 1; LL | | } | |_____^ help: try: `u_64 = u_64.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:70:5 | LL | / if 0 < u_64 { @@ -48,7 +48,7 @@ LL | | u_64 -= 1; LL | | } | |_____^ help: try: `u_64 = u_64.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:75:5 | LL | / if 0 != u_64 { @@ -56,7 +56,7 @@ LL | | u_64 -= 1; LL | | } | |_____^ help: try: `u_64 = u_64.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:96:5 | LL | / if u_usize > 0 { @@ -64,7 +64,7 @@ LL | | u_usize -= 1; LL | | } | |_____^ help: try: `u_usize = u_usize.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:108:5 | LL | / if i_8 > i8::MIN { @@ -72,7 +72,7 @@ LL | | i_8 -= 1; LL | | } | |_____^ help: try: `i_8 = i_8.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:113:5 | LL | / if i_8 > i8::MIN { @@ -80,7 +80,7 @@ LL | | i_8 -= 1; LL | | } | |_____^ help: try: `i_8 = i_8.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:118:5 | LL | / if i_8 != i8::MIN { @@ -88,7 +88,7 @@ LL | | i_8 -= 1; LL | | } | |_____^ help: try: `i_8 = i_8.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:123:5 | LL | / if i_8 != i8::MIN { @@ -96,7 +96,7 @@ LL | | i_8 -= 1; LL | | } | |_____^ help: try: `i_8 = i_8.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:133:5 | LL | / if i_16 > i16::MIN { @@ -104,7 +104,7 @@ LL | | i_16 -= 1; LL | | } | |_____^ help: try: `i_16 = i_16.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:138:5 | LL | / if i_16 > i16::MIN { @@ -112,7 +112,7 @@ LL | | i_16 -= 1; LL | | } | |_____^ help: try: `i_16 = i_16.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:143:5 | LL | / if i_16 != i16::MIN { @@ -120,7 +120,7 @@ LL | | i_16 -= 1; LL | | } | |_____^ help: try: `i_16 = i_16.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:148:5 | LL | / if i_16 != i16::MIN { @@ -128,7 +128,7 @@ LL | | i_16 -= 1; LL | | } | |_____^ help: try: `i_16 = i_16.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:158:5 | LL | / if i_32 > i32::MIN { @@ -136,7 +136,7 @@ LL | | i_32 -= 1; LL | | } | |_____^ help: try: `i_32 = i_32.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:163:5 | LL | / if i_32 > i32::MIN { @@ -144,7 +144,7 @@ LL | | i_32 -= 1; LL | | } | |_____^ help: try: `i_32 = i_32.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:168:5 | LL | / if i_32 != i32::MIN { @@ -152,7 +152,7 @@ LL | | i_32 -= 1; LL | | } | |_____^ help: try: `i_32 = i_32.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:173:5 | LL | / if i_32 != i32::MIN { @@ -160,7 +160,7 @@ LL | | i_32 -= 1; LL | | } | |_____^ help: try: `i_32 = i_32.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:183:5 | LL | / if i64::MIN < i_64 { @@ -168,7 +168,7 @@ LL | | i_64 -= 1; LL | | } | |_____^ help: try: `i_64 = i_64.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:188:5 | LL | / if i64::MIN != i_64 { @@ -176,7 +176,7 @@ LL | | i_64 -= 1; LL | | } | |_____^ help: try: `i_64 = i_64.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:193:5 | LL | / if i64::MIN < i_64 { diff --git a/tests/ui/int_plus_one.stderr b/tests/ui/int_plus_one.stderr index 29a6914761c..c5b020ba8ce 100644 --- a/tests/ui/int_plus_one.stderr +++ b/tests/ui/int_plus_one.stderr @@ -1,4 +1,4 @@ -error: Unnecessary `>= y + 1` or `x - 1 >=` +error: unnecessary `>= y + 1` or `x - 1 >=` --> $DIR/int_plus_one.rs:9:13 | LL | let _ = x >= y + 1; @@ -6,19 +6,19 @@ LL | let _ = x >= y + 1; | = note: `-D clippy::int-plus-one` implied by `-D warnings` -error: Unnecessary `>= y + 1` or `x - 1 >=` +error: unnecessary `>= y + 1` or `x - 1 >=` --> $DIR/int_plus_one.rs:10:13 | LL | let _ = y + 1 <= x; | ^^^^^^^^^^ help: change it to: `y < x` -error: Unnecessary `>= y + 1` or `x - 1 >=` +error: unnecessary `>= y + 1` or `x - 1 >=` --> $DIR/int_plus_one.rs:12:13 | LL | let _ = x - 1 >= y; | ^^^^^^^^^^ help: change it to: `x > y` -error: Unnecessary `>= y + 1` or `x - 1 >=` +error: unnecessary `>= y + 1` or `x - 1 >=` --> $DIR/int_plus_one.rs:13:13 | LL | let _ = y <= x - 1; diff --git a/tests/ui/iter_next_slice.stderr b/tests/ui/iter_next_slice.stderr index bbf61df0cda..8c10a252ee0 100644 --- a/tests/ui/iter_next_slice.stderr +++ b/tests/ui/iter_next_slice.stderr @@ -1,4 +1,4 @@ -error: Using `.iter().next()` on an array +error: using `.iter().next()` on an array --> $DIR/iter_next_slice.rs:9:5 | LL | s.iter().next(); @@ -6,19 +6,19 @@ LL | s.iter().next(); | = note: `-D clippy::iter-next-slice` implied by `-D warnings` -error: Using `.iter().next()` on a Slice without end index. +error: using `.iter().next()` on a Slice without end index --> $DIR/iter_next_slice.rs:12:5 | LL | s[2..].iter().next(); | ^^^^^^^^^^^^^^^^^^^^ help: try calling: `s.get(2)` -error: Using `.iter().next()` on a Slice without end index. +error: using `.iter().next()` on a Slice without end index --> $DIR/iter_next_slice.rs:15:5 | LL | v[5..].iter().next(); | ^^^^^^^^^^^^^^^^^^^^ help: try calling: `v.get(5)` -error: Using `.iter().next()` on an array +error: using `.iter().next()` on an array --> $DIR/iter_next_slice.rs:18:5 | LL | v.iter().next(); diff --git a/tests/ui/len_zero.fixed b/tests/ui/len_zero.fixed index d81676a3d9f..1f3b8ac99b1 100644 --- a/tests/ui/len_zero.fixed +++ b/tests/ui/len_zero.fixed @@ -141,11 +141,3 @@ fn main() { fn test_slice(b: &[u8]) { if !b.is_empty() {} } - -mod issue_3807 { - // Avoid suggesting changes to ranges if the user did not enable `range_is_empty`. - // See https://github.com/rust-lang/rust/issues/48111#issuecomment-445132965 - fn no_suggestion() { - let _ = (0..42).len() == 0; - } -} diff --git a/tests/ui/len_zero.rs b/tests/ui/len_zero.rs index ecdba921a5d..dc21de0001b 100644 --- a/tests/ui/len_zero.rs +++ b/tests/ui/len_zero.rs @@ -141,11 +141,3 @@ fn main() { fn test_slice(b: &[u8]) { if b.len() != 0 {} } - -mod issue_3807 { - // Avoid suggesting changes to ranges if the user did not enable `range_is_empty`. - // See https://github.com/rust-lang/rust/issues/48111#issuecomment-445132965 - fn no_suggestion() { - let _ = (0..42).len() == 0; - } -} diff --git a/tests/ui/len_zero_ranges.fixed b/tests/ui/len_zero_ranges.fixed index eee3db9b7d4..79781766242 100644 --- a/tests/ui/len_zero_ranges.fixed +++ b/tests/ui/len_zero_ranges.fixed @@ -1,15 +1,17 @@ // run-rustfix -#![feature(range_is_empty)] #![warn(clippy::len_zero)] #![allow(unused)] -#![allow(stable_features)] // TODO: https://github.com/rust-lang/rust-clippy/issues/5956 +// Now that `Range(Inclusive)::is_empty` is stable (1.47), we can always suggest this mod issue_3807 { - // With the feature enabled, `is_empty` should be suggested - fn suggestion_is_fine() { + fn suggestion_is_fine_range() { let _ = (0..42).is_empty(); } + + fn suggestion_is_fine_range_inclusive() { + let _ = (0_u8..=42).is_empty(); + } } fn main() {} diff --git a/tests/ui/len_zero_ranges.rs b/tests/ui/len_zero_ranges.rs index be2e0f38fd1..a0eb51cc976 100644 --- a/tests/ui/len_zero_ranges.rs +++ b/tests/ui/len_zero_ranges.rs @@ -1,15 +1,17 @@ // run-rustfix -#![feature(range_is_empty)] #![warn(clippy::len_zero)] #![allow(unused)] -#![allow(stable_features)] // TODO: https://github.com/rust-lang/rust-clippy/issues/5956 +// Now that `Range(Inclusive)::is_empty` is stable (1.47), we can always suggest this mod issue_3807 { - // With the feature enabled, `is_empty` should be suggested - fn suggestion_is_fine() { + fn suggestion_is_fine_range() { let _ = (0..42).len() == 0; } + + fn suggestion_is_fine_range_inclusive() { + let _ = (0_u8..=42).len() == 0; + } } fn main() {} diff --git a/tests/ui/len_zero_ranges.stderr b/tests/ui/len_zero_ranges.stderr index acb85f7100a..d0defb5a79e 100644 --- a/tests/ui/len_zero_ranges.stderr +++ b/tests/ui/len_zero_ranges.stderr @@ -1,10 +1,16 @@ error: length comparison to zero - --> $DIR/len_zero_ranges.rs:11:17 + --> $DIR/len_zero_ranges.rs:9:17 | LL | let _ = (0..42).len() == 0; | ^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `(0..42).is_empty()` | = note: `-D clippy::len-zero` implied by `-D warnings` -error: aborting due to previous error +error: length comparison to zero + --> $DIR/len_zero_ranges.rs:13:17 + | +LL | let _ = (0_u8..=42).len() == 0; + | ^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `(0_u8..=42).is_empty()` + +error: aborting due to 2 previous errors diff --git a/tests/ui/let_and_return.rs b/tests/ui/let_and_return.rs index 09614b8c1ad..73e550b3df8 100644 --- a/tests/ui/let_and_return.rs +++ b/tests/ui/let_and_return.rs @@ -135,4 +135,25 @@ mod no_lint_if_stmt_borrows { } } +mod issue_5729 { + use std::sync::Arc; + + trait Foo {} + + trait FooStorage { + fn foo_cloned(&self) -> Arc; + } + + struct FooStorageImpl { + foo: Arc, + } + + impl FooStorage for FooStorageImpl { + fn foo_cloned(&self) -> Arc { + let clone = Arc::clone(&self.foo); + clone + } + } +} + fn main() {} diff --git a/tests/ui/let_and_return.stderr b/tests/ui/let_and_return.stderr index eacf948b392..fe878e5f206 100644 --- a/tests/ui/let_and_return.stderr +++ b/tests/ui/let_and_return.stderr @@ -27,5 +27,19 @@ LL | LL | 5 | -error: aborting due to 2 previous errors +error: returning the result of a `let` binding from a block + --> $DIR/let_and_return.rs:154:13 + | +LL | let clone = Arc::clone(&self.foo); + | ---------------------------------- unnecessary `let` binding +LL | clone + | ^^^^^ + | +help: return the expression directly + | +LL | +LL | Arc::clone(&self.foo) as _ + | + +error: aborting due to 3 previous errors diff --git a/tests/ui/map_clone.stderr b/tests/ui/map_clone.stderr index 9eec6928e8c..4f43cff5024 100644 --- a/tests/ui/map_clone.stderr +++ b/tests/ui/map_clone.stderr @@ -1,40 +1,40 @@ -error: You are using an explicit closure for copying elements +error: you are using an explicit closure for copying elements --> $DIR/map_clone.rs:10:22 | LL | let _: Vec = vec![5_i8; 6].iter().map(|x| *x).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `copied` method: `vec![5_i8; 6].iter().copied()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `vec![5_i8; 6].iter().copied()` | = note: `-D clippy::map-clone` implied by `-D warnings` -error: You are using an explicit closure for cloning elements +error: you are using an explicit closure for cloning elements --> $DIR/map_clone.rs:11:26 | LL | let _: Vec = vec![String::new()].iter().map(|x| x.clone()).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `cloned` method: `vec![String::new()].iter().cloned()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `vec![String::new()].iter().cloned()` -error: You are using an explicit closure for copying elements +error: you are using an explicit closure for copying elements --> $DIR/map_clone.rs:12:23 | LL | let _: Vec = vec![42, 43].iter().map(|&x| x).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `copied` method: `vec![42, 43].iter().copied()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `vec![42, 43].iter().copied()` -error: You are using an explicit closure for copying elements +error: you are using an explicit closure for copying elements --> $DIR/map_clone.rs:14:26 | LL | let _: Option = Some(&16).map(|b| *b); - | ^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `copied` method: `Some(&16).copied()` + | ^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `Some(&16).copied()` -error: You are using an explicit closure for copying elements +error: you are using an explicit closure for copying elements --> $DIR/map_clone.rs:15:25 | LL | let _: Option = Some(&1).map(|x| x.clone()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `copied` method: `Some(&1).copied()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `Some(&1).copied()` -error: You are needlessly cloning iterator elements +error: you are needlessly cloning iterator elements --> $DIR/map_clone.rs:26:29 | LL | let _ = std::env::args().map(|v| v.clone()); - | ^^^^^^^^^^^^^^^^^^^ help: Remove the `map` call + | ^^^^^^^^^^^^^^^^^^^ help: remove the `map` call error: aborting due to 6 previous errors diff --git a/tests/ui/methods.rs b/tests/ui/methods.rs index 7880cf36415..80dd2f744b3 100644 --- a/tests/ui/methods.rs +++ b/tests/ui/methods.rs @@ -10,6 +10,7 @@ clippy::non_ascii_literal, clippy::new_without_default, clippy::needless_pass_by_value, + clippy::needless_lifetimes, clippy::print_stdout, clippy::must_use_candidate, clippy::use_self, @@ -33,71 +34,6 @@ use std::sync::{self, Arc}; use option_helpers::IteratorFalsePositives; -pub struct T; - -impl T { - pub fn add(self, other: T) -> T { - self - } - - // no error, not public interface - pub(crate) fn drop(&mut self) {} - - // no error, private function - fn neg(self) -> Self { - self - } - - // no error, private function - fn eq(&self, other: T) -> bool { - true - } - - // No error; self is a ref. - fn sub(&self, other: T) -> &T { - self - } - - // No error; different number of arguments. - fn div(self) -> T { - self - } - - // No error; wrong return type. - fn rem(self, other: T) {} - - // Fine - fn into_u32(self) -> u32 { - 0 - } - - fn into_u16(&self) -> u16 { - 0 - } - - fn to_something(self) -> u32 { - 0 - } - - fn new(self) -> Self { - unimplemented!(); - } -} - -pub struct T1; - -impl T1 { - // Shouldn't trigger lint as it is unsafe. - pub unsafe fn add(self, rhs: T1) -> T1 { - self - } - - // Should not trigger lint since this is an async function. - pub async fn next(&mut self) -> Option { - None - } -} - struct Lt<'a> { foo: &'a u32, } @@ -171,6 +107,8 @@ impl BadNew { } } +struct T; + impl Mul for T { type Output = T; // No error, obviously. diff --git a/tests/ui/methods.stderr b/tests/ui/methods.stderr index 01cf487ac14..2a0a43e83a6 100644 --- a/tests/ui/methods.stderr +++ b/tests/ui/methods.stderr @@ -1,15 +1,5 @@ -error: defining a method called `add` on this type; consider implementing the `std::ops::Add` trait or choosing a less ambiguous name - --> $DIR/methods.rs:39:5 - | -LL | / pub fn add(self, other: T) -> T { -LL | | self -LL | | } - | |_____^ - | - = note: `-D clippy::should-implement-trait` implied by `-D warnings` - error: methods called `new` usually return `Self` - --> $DIR/methods.rs:169:5 + --> $DIR/methods.rs:105:5 | LL | / fn new() -> i32 { LL | | 0 @@ -19,7 +9,7 @@ LL | | } = note: `-D clippy::new-ret-no-self` implied by `-D warnings` error: called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(p)` instead. - --> $DIR/methods.rs:188:13 + --> $DIR/methods.rs:126:13 | LL | let _ = v.iter().filter(|&x| *x < 0).next(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -28,7 +18,7 @@ LL | let _ = v.iter().filter(|&x| *x < 0).next(); = note: replace `filter(|&x| *x < 0).next()` with `find(|&x| *x < 0)` error: called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(p)` instead. - --> $DIR/methods.rs:191:13 + --> $DIR/methods.rs:129:13 | LL | let _ = v.iter().filter(|&x| { | _____________^ @@ -38,7 +28,7 @@ LL | | ).next(); | |___________________________^ error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:208:22 + --> $DIR/methods.rs:146:22 | LL | let _ = v.iter().find(|&x| *x < 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x < 0)` @@ -46,25 +36,25 @@ LL | let _ = v.iter().find(|&x| *x < 0).is_some(); = note: `-D clippy::search-is-some` implied by `-D warnings` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:209:20 + --> $DIR/methods.rs:147:20 | LL | let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| **y == x)` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:210:20 + --> $DIR/methods.rs:148:20 | LL | let _ = (0..1).find(|x| *x == 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| x == 0)` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:211:22 + --> $DIR/methods.rs:149:22 | LL | let _ = v.iter().find(|x| **x == 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x == 0)` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:214:13 + --> $DIR/methods.rs:152:13 | LL | let _ = v.iter().find(|&x| { | _____________^ @@ -74,13 +64,13 @@ LL | | ).is_some(); | |______________________________^ error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:220:22 + --> $DIR/methods.rs:158:22 | LL | let _ = v.iter().position(|&x| x < 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)` error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:223:13 + --> $DIR/methods.rs:161:13 | LL | let _ = v.iter().position(|&x| { | _____________^ @@ -90,13 +80,13 @@ LL | | ).is_some(); | |______________________________^ error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:229:22 + --> $DIR/methods.rs:167:22 | LL | let _ = v.iter().rposition(|&x| x < 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)` error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:232:13 + --> $DIR/methods.rs:170:13 | LL | let _ = v.iter().rposition(|&x| { | _____________^ @@ -105,5 +95,5 @@ LL | | } LL | | ).is_some(); | |______________________________^ -error: aborting due to 13 previous errors +error: aborting due to 12 previous errors diff --git a/tests/ui/mut_reference.stderr b/tests/ui/mut_reference.stderr index fa8c82ae0f3..062d30b262c 100644 --- a/tests/ui/mut_reference.stderr +++ b/tests/ui/mut_reference.stderr @@ -1,4 +1,4 @@ -error: The function/method `takes_an_immutable_reference` doesn't need a mutable reference +error: the function `takes_an_immutable_reference` doesn't need a mutable reference --> $DIR/mut_reference.rs:17:34 | LL | takes_an_immutable_reference(&mut 42); @@ -6,13 +6,13 @@ LL | takes_an_immutable_reference(&mut 42); | = note: `-D clippy::unnecessary-mut-passed` implied by `-D warnings` -error: The function/method `as_ptr` doesn't need a mutable reference +error: the function `as_ptr` doesn't need a mutable reference --> $DIR/mut_reference.rs:19:12 | LL | as_ptr(&mut 42); | ^^^^^^^ -error: The function/method `takes_an_immutable_reference` doesn't need a mutable reference +error: the method `takes_an_immutable_reference` doesn't need a mutable reference --> $DIR/mut_reference.rs:23:44 | LL | my_struct.takes_an_immutable_reference(&mut 42); diff --git a/tests/ui/mutex_atomic.stderr b/tests/ui/mutex_atomic.stderr index 7dac0865855..a3511ba708a 100644 --- a/tests/ui/mutex_atomic.stderr +++ b/tests/ui/mutex_atomic.stderr @@ -1,4 +1,4 @@ -error: Consider using an `AtomicBool` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. +error: consider using an `AtomicBool` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` --> $DIR/mutex_atomic.rs:6:5 | LL | Mutex::new(true); @@ -6,31 +6,31 @@ LL | Mutex::new(true); | = note: `-D clippy::mutex-atomic` implied by `-D warnings` -error: Consider using an `AtomicUsize` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. +error: consider using an `AtomicUsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` --> $DIR/mutex_atomic.rs:7:5 | LL | Mutex::new(5usize); | ^^^^^^^^^^^^^^^^^^ -error: Consider using an `AtomicIsize` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. +error: consider using an `AtomicIsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` --> $DIR/mutex_atomic.rs:8:5 | LL | Mutex::new(9isize); | ^^^^^^^^^^^^^^^^^^ -error: Consider using an `AtomicPtr` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. +error: consider using an `AtomicPtr` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` --> $DIR/mutex_atomic.rs:10:5 | LL | Mutex::new(&x as *const u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: Consider using an `AtomicPtr` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. +error: consider using an `AtomicPtr` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` --> $DIR/mutex_atomic.rs:11:5 | LL | Mutex::new(&mut x as *mut u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: Consider using an `AtomicUsize` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. +error: consider using an `AtomicUsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` --> $DIR/mutex_atomic.rs:12:5 | LL | Mutex::new(0u32); @@ -38,7 +38,7 @@ LL | Mutex::new(0u32); | = note: `-D clippy::mutex-integer` implied by `-D warnings` -error: Consider using an `AtomicIsize` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. +error: consider using an `AtomicIsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` --> $DIR/mutex_atomic.rs:13:5 | LL | Mutex::new(0i32); diff --git a/tests/ui/needless_doc_main.rs b/tests/ui/needless_doc_main.rs index 682d7b3c4ce..883683e08a2 100644 --- a/tests/ui/needless_doc_main.rs +++ b/tests/ui/needless_doc_main.rs @@ -9,8 +9,14 @@ /// } /// ``` /// -/// This should, too. +/// With an explicit return type it should lint too +/// ``` +/// fn main() -> () { +/// unimplemented!(); +/// } +/// ``` /// +/// This should, too. /// ```rust /// fn main() { /// unimplemented!(); @@ -18,7 +24,6 @@ /// ``` /// /// This one too. -/// /// ```no_run /// fn main() { /// unimplemented!(); @@ -33,6 +38,20 @@ fn bad_doctests() {} /// fn main(){} /// ``` /// +/// This shouldn't lint either, because main is async: +/// ``` +/// async fn main() { +/// assert_eq!(42, ANSWER); +/// } +/// ``` +/// +/// Same here, because the return type is not the unit type: +/// ``` +/// fn main() -> Result<()> { +/// Ok(()) +/// } +/// ``` +/// /// This shouldn't lint either, because there's a `static`: /// ``` /// static ANSWER: i32 = 42; @@ -42,6 +61,15 @@ fn bad_doctests() {} /// } /// ``` /// +/// This shouldn't lint either, because there's a `const`: +/// ``` +/// fn main() { +/// assert_eq!(42, ANSWER); +/// } +/// +/// const ANSWER: i32 = 42; +/// ``` +/// /// Neither should this lint because of `extern crate`: /// ``` /// #![feature(test)] @@ -51,8 +79,41 @@ fn bad_doctests() {} /// } /// ``` /// -/// We should not lint ignored examples: +/// Neither should this lint because it has an extern block: +/// ``` +/// extern {} +/// fn main() { +/// unimplemented!(); +/// } +/// ``` /// +/// This should not lint because there is another function defined: +/// ``` +/// fn fun() {} +/// +/// fn main() { +/// unimplemented!(); +/// } +/// ``` +/// +/// We should not lint inside raw strings ... +/// ``` +/// let string = r#" +/// fn main() { +/// unimplemented!(); +/// } +/// "#; +/// ``` +/// +/// ... or comments +/// ``` +/// // fn main() { +/// // let _inception = 42; +/// // } +/// let _inception = 42; +/// ``` +/// +/// We should not lint ignored examples: /// ```rust,ignore /// fn main() { /// unimplemented!(); @@ -60,7 +121,6 @@ fn bad_doctests() {} /// ``` /// /// Or even non-rust examples: -/// /// ```text /// fn main() { /// is what starts the program diff --git a/tests/ui/needless_doc_main.stderr b/tests/ui/needless_doc_main.stderr index 65d40ee6832..05c7f9d33a7 100644 --- a/tests/ui/needless_doc_main.stderr +++ b/tests/ui/needless_doc_main.stderr @@ -7,16 +7,22 @@ LL | /// fn main() { = note: `-D clippy::needless-doctest-main` implied by `-D warnings` error: needless `fn main` in doctest - --> $DIR/needless_doc_main.rs:15:4 + --> $DIR/needless_doc_main.rs:14:4 + | +LL | /// fn main() -> () { + | ^^^^^^^^^^^^^^^^^^ + +error: needless `fn main` in doctest + --> $DIR/needless_doc_main.rs:21:4 | LL | /// fn main() { | ^^^^^^^^^^^^ error: needless `fn main` in doctest - --> $DIR/needless_doc_main.rs:23:4 + --> $DIR/needless_doc_main.rs:28:4 | LL | /// fn main() { | ^^^^^^^^^^^^ -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index ad20e238107..d849e093da7 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -69,6 +69,23 @@ fn test_void_match(x: u32) { } } +fn read_line() -> String { + use std::io::BufRead; + let stdin = ::std::io::stdin(); + return stdin.lock().lines().next().unwrap().unwrap(); +} + +fn borrows_but_not_last(value: bool) -> String { + if value { + use std::io::BufRead; + let stdin = ::std::io::stdin(); + let _a = stdin.lock().lines().next().unwrap().unwrap(); + String::from("test") + } else { + String::new() + } +} + fn main() { let _ = test_end_of_fn(); let _ = test_no_semicolon(); diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index af0cdfb207f..29f2bd1852a 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -69,6 +69,23 @@ fn test_void_match(x: u32) { } } +fn read_line() -> String { + use std::io::BufRead; + let stdin = ::std::io::stdin(); + return stdin.lock().lines().next().unwrap().unwrap(); +} + +fn borrows_but_not_last(value: bool) -> String { + if value { + use std::io::BufRead; + let stdin = ::std::io::stdin(); + let _a = stdin.lock().lines().next().unwrap().unwrap(); + return String::from("test"); + } else { + return String::new(); + } +} + fn main() { let _ = test_end_of_fn(); let _ = test_no_semicolon(); diff --git a/tests/ui/needless_return.stderr b/tests/ui/needless_return.stderr index c34eecbcbb6..f73c833a801 100644 --- a/tests/ui/needless_return.stderr +++ b/tests/ui/needless_return.stderr @@ -72,5 +72,17 @@ error: unneeded `return` statement LL | _ => return, | ^^^^^^ help: replace `return` with an empty block: `{}` -error: aborting due to 12 previous errors +error: unneeded `return` statement + --> $DIR/needless_return.rs:83:9 + | +LL | return String::from("test"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:85:9 + | +LL | return String::new(); + | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()` + +error: aborting due to 14 previous errors diff --git a/tests/ui/new_ret_no_self.rs b/tests/ui/new_ret_no_self.rs index 2c2d1e27589..e82873629a5 100644 --- a/tests/ui/new_ret_no_self.rs +++ b/tests/ui/new_ret_no_self.rs @@ -137,9 +137,9 @@ impl MutPointerReturnerOk { } } -struct MutPointerReturnerOk2; +struct ConstPointerReturnerOk2; -impl MutPointerReturnerOk2 { +impl ConstPointerReturnerOk2 { // should not trigger lint pub fn new() -> *const Self { unimplemented!(); @@ -210,3 +210,133 @@ impl<'a> WithLifetime<'a> { unimplemented!(); } } + +mod issue5435 { + struct V; + + pub trait TraitRetSelf { + // should not trigger lint + fn new() -> Self; + } + + pub trait TraitRet { + // should trigger lint as we are in trait definition + fn new() -> String; + } + pub struct StructRet; + impl TraitRet for StructRet { + // should not trigger lint as we are in the impl block + fn new() -> String { + unimplemented!(); + } + } + + pub trait TraitRet2 { + // should trigger lint + fn new(_: String) -> String; + } + + trait TupleReturnerOk { + // should not trigger lint + fn new() -> (Self, u32) + where + Self: Sized, + { + unimplemented!(); + } + } + + trait TupleReturnerOk2 { + // should not trigger lint (it doesn't matter which element in the tuple is Self) + fn new() -> (u32, Self) + where + Self: Sized, + { + unimplemented!(); + } + } + + trait TupleReturnerOk3 { + // should not trigger lint (tuple can contain multiple Self) + fn new() -> (Self, Self) + where + Self: Sized, + { + unimplemented!(); + } + } + + trait TupleReturnerBad { + // should trigger lint + fn new() -> (u32, u32) { + unimplemented!(); + } + } + + trait MutPointerReturnerOk { + // should not trigger lint + fn new() -> *mut Self + where + Self: Sized, + { + unimplemented!(); + } + } + + trait ConstPointerReturnerOk2 { + // should not trigger lint + fn new() -> *const Self + where + Self: Sized, + { + unimplemented!(); + } + } + + trait MutPointerReturnerBad { + // should trigger lint + fn new() -> *mut V { + unimplemented!(); + } + } + + trait GenericReturnerOk { + // should not trigger lint + fn new() -> Option + where + Self: Sized, + { + unimplemented!(); + } + } + + trait NestedReturnerOk { + // should not trigger lint + fn new() -> (Option, u32) + where + Self: Sized, + { + unimplemented!(); + } + } + + trait NestedReturnerOk2 { + // should not trigger lint + fn new() -> ((Self, u32), u32) + where + Self: Sized, + { + unimplemented!(); + } + } + + trait NestedReturnerOk3 { + // should not trigger lint + fn new() -> Option<(Self, u32)> + where + Self: Sized, + { + unimplemented!(); + } + } +} diff --git a/tests/ui/new_ret_no_self.stderr b/tests/ui/new_ret_no_self.stderr index dd5a24bcbe7..8217bc6187f 100644 --- a/tests/ui/new_ret_no_self.stderr +++ b/tests/ui/new_ret_no_self.stderr @@ -48,5 +48,33 @@ LL | | unimplemented!(); LL | | } | |_____^ -error: aborting due to 6 previous errors +error: methods called `new` usually return `Self` + --> $DIR/new_ret_no_self.rs:224:9 + | +LL | fn new() -> String; + | ^^^^^^^^^^^^^^^^^^^ + +error: methods called `new` usually return `Self` + --> $DIR/new_ret_no_self.rs:236:9 + | +LL | fn new(_: String) -> String; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: methods called `new` usually return `Self` + --> $DIR/new_ret_no_self.rs:271:9 + | +LL | / fn new() -> (u32, u32) { +LL | | unimplemented!(); +LL | | } + | |_________^ + +error: methods called `new` usually return `Self` + --> $DIR/new_ret_no_self.rs:298:9 + | +LL | / fn new() -> *mut V { +LL | | unimplemented!(); +LL | | } + | |_________^ + +error: aborting due to 10 previous errors diff --git a/tests/ui/option_as_ref_deref.fixed b/tests/ui/option_as_ref_deref.fixed index 076692e6445..07d7f0b45b0 100644 --- a/tests/ui/option_as_ref_deref.fixed +++ b/tests/ui/option_as_ref_deref.fixed @@ -38,4 +38,7 @@ fn main() { let _ = opt.as_deref(); let _ = opt.as_deref_mut(); + + // Issue #5927 + let _ = opt.as_deref(); } diff --git a/tests/ui/option_as_ref_deref.rs b/tests/ui/option_as_ref_deref.rs index 3bf5f715f83..6ae059c9425 100644 --- a/tests/ui/option_as_ref_deref.rs +++ b/tests/ui/option_as_ref_deref.rs @@ -41,4 +41,7 @@ fn main() { let _ = opt.as_ref().map(|x| &**x); let _ = opt.as_mut().map(|x| &mut **x); + + // Issue #5927 + let _ = opt.as_ref().map(std::ops::Deref::deref); } diff --git a/tests/ui/option_as_ref_deref.stderr b/tests/ui/option_as_ref_deref.stderr index a106582a633..62f28232475 100644 --- a/tests/ui/option_as_ref_deref.stderr +++ b/tests/ui/option_as_ref_deref.stderr @@ -100,5 +100,11 @@ error: called `.as_mut().map(|x| &mut **x)` on an Option value. This can be done LL | let _ = opt.as_mut().map(|x| &mut **x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` -error: aborting due to 16 previous errors +error: called `.as_ref().map(std::ops::Deref::deref)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead + --> $DIR/option_as_ref_deref.rs:46:13 + | +LL | let _ = opt.as_ref().map(std::ops::Deref::deref); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` + +error: aborting due to 17 previous errors diff --git a/tests/ui/precedence.fixed b/tests/ui/precedence.fixed index 4d284ae1319..163bd044c17 100644 --- a/tests/ui/precedence.fixed +++ b/tests/ui/precedence.fixed @@ -48,6 +48,14 @@ fn main() { let _ = -1f64.to_degrees(); let _ = -1f64.to_radians(); + // Chains containing any non-odd function should trigger (issue #5924) + let _ = -(1.0_f64.cos().cos()); + let _ = -(1.0_f64.cos().sin()); + let _ = -(1.0_f64.sin().cos()); + + // Chains of odd functions shouldn't trigger + let _ = -1f64.sin().sin(); + let b = 3; trip!(b * 8); } diff --git a/tests/ui/precedence.rs b/tests/ui/precedence.rs index 2d08e82f349..8c849e3209b 100644 --- a/tests/ui/precedence.rs +++ b/tests/ui/precedence.rs @@ -48,6 +48,14 @@ fn main() { let _ = -1f64.to_degrees(); let _ = -1f64.to_radians(); + // Chains containing any non-odd function should trigger (issue #5924) + let _ = -1.0_f64.cos().cos(); + let _ = -1.0_f64.cos().sin(); + let _ = -1.0_f64.sin().cos(); + + // Chains of odd functions shouldn't trigger + let _ = -1f64.sin().sin(); + let b = 3; trip!(b * 8); } diff --git a/tests/ui/precedence.stderr b/tests/ui/precedence.stderr index a2ed5392bfc..03d585b3975 100644 --- a/tests/ui/precedence.stderr +++ b/tests/ui/precedence.stderr @@ -54,5 +54,23 @@ error: unary minus has lower precedence than method call LL | -1f32.abs(); | ^^^^^^^^^^^ help: consider adding parentheses to clarify your intent: `-(1f32.abs())` -error: aborting due to 9 previous errors +error: unary minus has lower precedence than method call + --> $DIR/precedence.rs:52:13 + | +LL | let _ = -1.0_f64.cos().cos(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider adding parentheses to clarify your intent: `-(1.0_f64.cos().cos())` + +error: unary minus has lower precedence than method call + --> $DIR/precedence.rs:53:13 + | +LL | let _ = -1.0_f64.cos().sin(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider adding parentheses to clarify your intent: `-(1.0_f64.cos().sin())` + +error: unary minus has lower precedence than method call + --> $DIR/precedence.rs:54:13 + | +LL | let _ = -1.0_f64.sin().cos(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider adding parentheses to clarify your intent: `-(1.0_f64.sin().cos())` + +error: aborting due to 12 previous errors diff --git a/tests/ui/redundant_allocation.fixed b/tests/ui/redundant_allocation.fixed index 26635833458..6514fd6d1ac 100644 --- a/tests/ui/redundant_allocation.fixed +++ b/tests/ui/redundant_allocation.fixed @@ -33,7 +33,7 @@ pub fn test5(a: Rc) {} // Rc> -pub fn test6(a: Box) {} +pub fn test6(a: Rc) {} // Box<&T> diff --git a/tests/ui/redundant_allocation.stderr b/tests/ui/redundant_allocation.stderr index eaa57ce3024..92e4f67f5db 100644 --- a/tests/ui/redundant_allocation.stderr +++ b/tests/ui/redundant_allocation.stderr @@ -28,7 +28,7 @@ error: usage of `Rc>` --> $DIR/redundant_allocation.rs:36:17 | LL | pub fn test6(a: Rc>) {} - | ^^^^^^^^^^^^^ help: try: `Box` + | ^^^^^^^^^^^^^ help: try: `Rc` error: usage of `Box<&T>` --> $DIR/redundant_allocation.rs:40:22 diff --git a/tests/ui/redundant_closure_call_early.stderr b/tests/ui/redundant_closure_call_early.stderr index 79f27663461..2735e41738f 100644 --- a/tests/ui/redundant_closure_call_early.stderr +++ b/tests/ui/redundant_closure_call_early.stderr @@ -1,4 +1,4 @@ -error: try not to call a closure in the expression where it is declared. +error: try not to call a closure in the expression where it is declared --> $DIR/redundant_closure_call_early.rs:9:17 | LL | let mut k = (|m| m + 1)(i); @@ -6,7 +6,7 @@ LL | let mut k = (|m| m + 1)(i); | = note: `-D clippy::redundant-closure-call` implied by `-D warnings` -error: try not to call a closure in the expression where it is declared. +error: try not to call a closure in the expression where it is declared --> $DIR/redundant_closure_call_early.rs:12:9 | LL | k = (|a, b| a * b)(1, 5); diff --git a/tests/ui/redundant_closure_call_fixable.stderr b/tests/ui/redundant_closure_call_fixable.stderr index 644161d9f5d..afd704ef12a 100644 --- a/tests/ui/redundant_closure_call_fixable.stderr +++ b/tests/ui/redundant_closure_call_fixable.stderr @@ -1,4 +1,4 @@ -error: try not to call a closure in the expression where it is declared. +error: try not to call a closure in the expression where it is declared --> $DIR/redundant_closure_call_fixable.rs:7:13 | LL | let a = (|| 42)(); diff --git a/tests/ui/redundant_closure_call_late.rs b/tests/ui/redundant_closure_call_late.rs index e29a1dce0c7..1f4864b7289 100644 --- a/tests/ui/redundant_closure_call_late.rs +++ b/tests/ui/redundant_closure_call_late.rs @@ -24,4 +24,16 @@ fn main() { let shadowed_closure = || 2; i = shadowed_closure(); i = shadowed_closure(); + + // Fix FP in #5916 + let mut x; + let create = || 2 * 2; + x = create(); + fun(move || { + x = create(); + }) +} + +fn fun(mut f: T) { + f(); } diff --git a/tests/ui/same_item_push.rs b/tests/ui/same_item_push.rs index ff1088f86f6..bfe27e02044 100644 --- a/tests/ui/same_item_push.rs +++ b/tests/ui/same_item_push.rs @@ -86,4 +86,12 @@ fn main() { for a in vec_a { vec12.push(2u8.pow(a.kind)); } + + // Fix #5902 + let mut vec13: Vec = Vec::new(); + let mut item = 0; + for _ in 0..10 { + vec13.push(item); + item += 10; + } } diff --git a/tests/ui/self_assignment.rs b/tests/ui/self_assignment.rs new file mode 100644 index 00000000000..a7cbb9cd78b --- /dev/null +++ b/tests/ui/self_assignment.rs @@ -0,0 +1,67 @@ +#![warn(clippy::self_assignment)] + +pub struct S<'a> { + a: i32, + b: [i32; 10], + c: Vec>, + e: &'a mut i32, + f: &'a mut i32, +} + +pub fn positives(mut a: usize, b: &mut u32, mut s: S) { + a = a; + *b = *b; + s = s; + s.a = s.a; + s.b[10] = s.b[5 + 5]; + s.c[0][1] = s.c[0][1]; + s.b[a] = s.b[a]; + *s.e = *s.e; + s.b[a + 10] = s.b[10 + a]; + + let mut t = (0, 1); + t.1 = t.1; + t.0 = (t.0); +} + +pub fn negatives_not_equal(mut a: usize, b: &mut usize, mut s: S) { + dbg!(&a); + a = *b; + dbg!(&a); + s.b[1] += s.b[1]; + s.b[1] = s.b[2]; + s.c[1][0] = s.c[0][1]; + s.b[a] = s.b[*b]; + s.b[a + 10] = s.b[a + 11]; + *s.e = *s.f; + + let mut t = (0, 1); + t.0 = t.1; +} + +#[allow(clippy::eval_order_dependence)] +pub fn negatives_side_effects() { + let mut v = vec![1, 2, 3, 4, 5]; + let mut i = 0; + v[{ + i += 1; + i + }] = v[{ + i += 1; + i + }]; + + fn next(n: &mut usize) -> usize { + let v = *n; + *n += 1; + v + } + + let mut w = vec![1, 2, 3, 4, 5]; + let mut i = 0; + let i = &mut i; + w[next(i)] = w[next(i)]; + w[next(i)] = w[next(i)]; +} + +fn main() {} diff --git a/tests/ui/self_assignment.stderr b/tests/ui/self_assignment.stderr new file mode 100644 index 00000000000..826e0d0ba88 --- /dev/null +++ b/tests/ui/self_assignment.stderr @@ -0,0 +1,70 @@ +error: self-assignment of `a` to `a` + --> $DIR/self_assignment.rs:12:5 + | +LL | a = a; + | ^^^^^ + | + = note: `-D clippy::self-assignment` implied by `-D warnings` + +error: self-assignment of `*b` to `*b` + --> $DIR/self_assignment.rs:13:5 + | +LL | *b = *b; + | ^^^^^^^ + +error: self-assignment of `s` to `s` + --> $DIR/self_assignment.rs:14:5 + | +LL | s = s; + | ^^^^^ + +error: self-assignment of `s.a` to `s.a` + --> $DIR/self_assignment.rs:15:5 + | +LL | s.a = s.a; + | ^^^^^^^^^ + +error: self-assignment of `s.b[5 + 5]` to `s.b[10]` + --> $DIR/self_assignment.rs:16:5 + | +LL | s.b[10] = s.b[5 + 5]; + | ^^^^^^^^^^^^^^^^^^^^ + +error: self-assignment of `s.c[0][1]` to `s.c[0][1]` + --> $DIR/self_assignment.rs:17:5 + | +LL | s.c[0][1] = s.c[0][1]; + | ^^^^^^^^^^^^^^^^^^^^^ + +error: self-assignment of `s.b[a]` to `s.b[a]` + --> $DIR/self_assignment.rs:18:5 + | +LL | s.b[a] = s.b[a]; + | ^^^^^^^^^^^^^^^ + +error: self-assignment of `*s.e` to `*s.e` + --> $DIR/self_assignment.rs:19:5 + | +LL | *s.e = *s.e; + | ^^^^^^^^^^^ + +error: self-assignment of `s.b[10 + a]` to `s.b[a + 10]` + --> $DIR/self_assignment.rs:20:5 + | +LL | s.b[a + 10] = s.b[10 + a]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: self-assignment of `t.1` to `t.1` + --> $DIR/self_assignment.rs:23:5 + | +LL | t.1 = t.1; + | ^^^^^^^^^ + +error: self-assignment of `(t.0)` to `t.0` + --> $DIR/self_assignment.rs:24:5 + | +LL | t.0 = (t.0); + | ^^^^^^^^^^^ + +error: aborting due to 11 previous errors + diff --git a/tests/ui/should_impl_trait/corner_cases.rs b/tests/ui/should_impl_trait/corner_cases.rs new file mode 100644 index 00000000000..6c5ffe6aba8 --- /dev/null +++ b/tests/ui/should_impl_trait/corner_cases.rs @@ -0,0 +1,83 @@ +// edition:2018 + +#![warn(clippy::all, clippy::pedantic)] +#![allow( + clippy::missing_errors_doc, + clippy::needless_pass_by_value, + clippy::must_use_candidate, + clippy::unused_self, + clippy::needless_lifetimes, + clippy::missing_safety_doc, + clippy::wrong_self_convention +)] + +use std::ops::Mul; +use std::rc::{self, Rc}; +use std::sync::{self, Arc}; + +fn main() {} + +pub struct T1; +impl T1 { + // corner cases: should not lint + + // no error, not public interface + pub(crate) fn drop(&mut self) {} + + // no error, private function + fn neg(self) -> Self { + self + } + + // no error, private function + fn eq(&self, other: Self) -> bool { + true + } + + // No error; self is a ref. + fn sub(&self, other: Self) -> &Self { + self + } + + // No error; different number of arguments. + fn div(self) -> Self { + self + } + + // No error; wrong return type. + fn rem(self, other: Self) {} + + // Fine + fn into_u32(self) -> u32 { + 0 + } + + fn into_u16(&self) -> u16 { + 0 + } + + fn to_something(self) -> u32 { + 0 + } + + fn new(self) -> Self { + unimplemented!(); + } + + pub fn next<'b>(&'b mut self) -> Option<&'b mut T1> { + unimplemented!(); + } +} + +pub struct T2; +impl T2 { + // Shouldn't trigger lint as it is unsafe. + pub unsafe fn add(self, rhs: Self) -> Self { + self + } + + // Should not trigger lint since this is an async function. + pub async fn next(&mut self) -> Option { + None + } +} diff --git a/tests/ui/should_impl_trait/method_list_1.rs b/tests/ui/should_impl_trait/method_list_1.rs new file mode 100644 index 00000000000..f8d248fc98d --- /dev/null +++ b/tests/ui/should_impl_trait/method_list_1.rs @@ -0,0 +1,87 @@ +// edition:2018 + +#![warn(clippy::all, clippy::pedantic)] +#![allow( + clippy::missing_errors_doc, + clippy::needless_pass_by_value, + clippy::must_use_candidate, + clippy::unused_self, + clippy::needless_lifetimes, + clippy::missing_safety_doc, + clippy::wrong_self_convention +)] + +use std::ops::Mul; +use std::rc::{self, Rc}; +use std::sync::{self, Arc}; + +fn main() {} +pub struct T; + +impl T { + // ***************************************** + // trait method list part 1, should lint all + // ***************************************** + pub fn add(self, other: T) -> T { + unimplemented!() + } + + pub fn as_mut(&mut self) -> &mut T { + unimplemented!() + } + + pub fn as_ref(&self) -> &T { + unimplemented!() + } + + pub fn bitand(self, rhs: T) -> T { + unimplemented!() + } + + pub fn bitor(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn bitxor(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn borrow(&self) -> &str { + unimplemented!() + } + + pub fn borrow_mut(&mut self) -> &mut str { + unimplemented!() + } + + pub fn clone(&self) -> Self { + unimplemented!() + } + + pub fn cmp(&self, other: &Self) -> Self { + unimplemented!() + } + + pub fn default() -> Self { + unimplemented!() + } + + pub fn deref(&self) -> &Self { + unimplemented!() + } + + pub fn deref_mut(&mut self) -> &mut Self { + unimplemented!() + } + + pub fn div(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn drop(&mut self) { + unimplemented!() + } + // ********** + // part 1 end + // ********** +} diff --git a/tests/ui/should_impl_trait/method_list_1.stderr b/tests/ui/should_impl_trait/method_list_1.stderr new file mode 100644 index 00000000000..2b7d4628c3f --- /dev/null +++ b/tests/ui/should_impl_trait/method_list_1.stderr @@ -0,0 +1,143 @@ +error: method `add` can be confused for the standard trait method `std::ops::Add::add` + --> $DIR/method_list_1.rs:25:5 + | +LL | / pub fn add(self, other: T) -> T { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = note: `-D clippy::should-implement-trait` implied by `-D warnings` + = help: consider implementing the trait `std::ops::Add` or choosing a less ambiguous method name + +error: method `as_mut` can be confused for the standard trait method `std::convert::AsMut::as_mut` + --> $DIR/method_list_1.rs:29:5 + | +LL | / pub fn as_mut(&mut self) -> &mut T { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::convert::AsMut` or choosing a less ambiguous method name + +error: method `as_ref` can be confused for the standard trait method `std::convert::AsRef::as_ref` + --> $DIR/method_list_1.rs:33:5 + | +LL | / pub fn as_ref(&self) -> &T { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::convert::AsRef` or choosing a less ambiguous method name + +error: method `bitand` can be confused for the standard trait method `std::ops::BitAnd::bitand` + --> $DIR/method_list_1.rs:37:5 + | +LL | / pub fn bitand(self, rhs: T) -> T { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::BitAnd` or choosing a less ambiguous method name + +error: method `bitor` can be confused for the standard trait method `std::ops::BitOr::bitor` + --> $DIR/method_list_1.rs:41:5 + | +LL | / pub fn bitor(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::BitOr` or choosing a less ambiguous method name + +error: method `bitxor` can be confused for the standard trait method `std::ops::BitXor::bitxor` + --> $DIR/method_list_1.rs:45:5 + | +LL | / pub fn bitxor(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::BitXor` or choosing a less ambiguous method name + +error: method `borrow` can be confused for the standard trait method `std::borrow::Borrow::borrow` + --> $DIR/method_list_1.rs:49:5 + | +LL | / pub fn borrow(&self) -> &str { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::borrow::Borrow` or choosing a less ambiguous method name + +error: method `borrow_mut` can be confused for the standard trait method `std::borrow::BorrowMut::borrow_mut` + --> $DIR/method_list_1.rs:53:5 + | +LL | / pub fn borrow_mut(&mut self) -> &mut str { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::borrow::BorrowMut` or choosing a less ambiguous method name + +error: method `clone` can be confused for the standard trait method `std::clone::Clone::clone` + --> $DIR/method_list_1.rs:57:5 + | +LL | / pub fn clone(&self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::clone::Clone` or choosing a less ambiguous method name + +error: method `cmp` can be confused for the standard trait method `std::cmp::Ord::cmp` + --> $DIR/method_list_1.rs:61:5 + | +LL | / pub fn cmp(&self, other: &Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::cmp::Ord` or choosing a less ambiguous method name + +error: method `deref` can be confused for the standard trait method `std::ops::Deref::deref` + --> $DIR/method_list_1.rs:69:5 + | +LL | / pub fn deref(&self) -> &Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Deref` or choosing a less ambiguous method name + +error: method `deref_mut` can be confused for the standard trait method `std::ops::DerefMut::deref_mut` + --> $DIR/method_list_1.rs:73:5 + | +LL | / pub fn deref_mut(&mut self) -> &mut Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::DerefMut` or choosing a less ambiguous method name + +error: method `div` can be confused for the standard trait method `std::ops::Div::div` + --> $DIR/method_list_1.rs:77:5 + | +LL | / pub fn div(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Div` or choosing a less ambiguous method name + +error: method `drop` can be confused for the standard trait method `std::ops::Drop::drop` + --> $DIR/method_list_1.rs:81:5 + | +LL | / pub fn drop(&mut self) { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Drop` or choosing a less ambiguous method name + +error: aborting due to 14 previous errors + diff --git a/tests/ui/should_impl_trait/method_list_2.rs b/tests/ui/should_impl_trait/method_list_2.rs new file mode 100644 index 00000000000..ed5e0d384bf --- /dev/null +++ b/tests/ui/should_impl_trait/method_list_2.rs @@ -0,0 +1,88 @@ +// edition:2018 + +#![warn(clippy::all, clippy::pedantic)] +#![allow( + clippy::missing_errors_doc, + clippy::needless_pass_by_value, + clippy::must_use_candidate, + clippy::unused_self, + clippy::needless_lifetimes, + clippy::missing_safety_doc, + clippy::wrong_self_convention +)] + +use std::ops::Mul; +use std::rc::{self, Rc}; +use std::sync::{self, Arc}; + +fn main() {} +pub struct T; + +impl T { + // ***************************************** + // trait method list part 2, should lint all + // ***************************************** + + pub fn eq(&self, other: &Self) -> bool { + unimplemented!() + } + + pub fn from_iter(iter: T) -> Self { + unimplemented!() + } + + pub fn from_str(s: &str) -> Result { + unimplemented!() + } + + pub fn hash(&self, state: &mut T) { + unimplemented!() + } + + pub fn index(&self, index: usize) -> &Self { + unimplemented!() + } + + pub fn index_mut(&mut self, index: usize) -> &mut Self { + unimplemented!() + } + + pub fn into_iter(self) -> Self { + unimplemented!() + } + + pub fn mul(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn neg(self) -> Self { + unimplemented!() + } + + pub fn next(&mut self) -> Option { + unimplemented!() + } + + pub fn not(self) -> Self { + unimplemented!() + } + + pub fn rem(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn shl(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn shr(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn sub(self, rhs: Self) -> Self { + unimplemented!() + } + // ********** + // part 2 end + // ********** +} diff --git a/tests/ui/should_impl_trait/method_list_2.stderr b/tests/ui/should_impl_trait/method_list_2.stderr new file mode 100644 index 00000000000..b6fd4356956 --- /dev/null +++ b/tests/ui/should_impl_trait/method_list_2.stderr @@ -0,0 +1,153 @@ +error: method `eq` can be confused for the standard trait method `std::cmp::PartialEq::eq` + --> $DIR/method_list_2.rs:26:5 + | +LL | / pub fn eq(&self, other: &Self) -> bool { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = note: `-D clippy::should-implement-trait` implied by `-D warnings` + = help: consider implementing the trait `std::cmp::PartialEq` or choosing a less ambiguous method name + +error: method `from_iter` can be confused for the standard trait method `std::iter::FromIterator::from_iter` + --> $DIR/method_list_2.rs:30:5 + | +LL | / pub fn from_iter(iter: T) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::iter::FromIterator` or choosing a less ambiguous method name + +error: method `from_str` can be confused for the standard trait method `std::str::FromStr::from_str` + --> $DIR/method_list_2.rs:34:5 + | +LL | / pub fn from_str(s: &str) -> Result { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::str::FromStr` or choosing a less ambiguous method name + +error: method `hash` can be confused for the standard trait method `std::hash::Hash::hash` + --> $DIR/method_list_2.rs:38:5 + | +LL | / pub fn hash(&self, state: &mut T) { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::hash::Hash` or choosing a less ambiguous method name + +error: method `index` can be confused for the standard trait method `std::ops::Index::index` + --> $DIR/method_list_2.rs:42:5 + | +LL | / pub fn index(&self, index: usize) -> &Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Index` or choosing a less ambiguous method name + +error: method `index_mut` can be confused for the standard trait method `std::ops::IndexMut::index_mut` + --> $DIR/method_list_2.rs:46:5 + | +LL | / pub fn index_mut(&mut self, index: usize) -> &mut Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::IndexMut` or choosing a less ambiguous method name + +error: method `into_iter` can be confused for the standard trait method `std::iter::IntoIterator::into_iter` + --> $DIR/method_list_2.rs:50:5 + | +LL | / pub fn into_iter(self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::iter::IntoIterator` or choosing a less ambiguous method name + +error: method `mul` can be confused for the standard trait method `std::ops::Mul::mul` + --> $DIR/method_list_2.rs:54:5 + | +LL | / pub fn mul(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Mul` or choosing a less ambiguous method name + +error: method `neg` can be confused for the standard trait method `std::ops::Neg::neg` + --> $DIR/method_list_2.rs:58:5 + | +LL | / pub fn neg(self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Neg` or choosing a less ambiguous method name + +error: method `next` can be confused for the standard trait method `std::iter::Iterator::next` + --> $DIR/method_list_2.rs:62:5 + | +LL | / pub fn next(&mut self) -> Option { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::iter::Iterator` or choosing a less ambiguous method name + +error: method `not` can be confused for the standard trait method `std::ops::Not::not` + --> $DIR/method_list_2.rs:66:5 + | +LL | / pub fn not(self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Not` or choosing a less ambiguous method name + +error: method `rem` can be confused for the standard trait method `std::ops::Rem::rem` + --> $DIR/method_list_2.rs:70:5 + | +LL | / pub fn rem(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Rem` or choosing a less ambiguous method name + +error: method `shl` can be confused for the standard trait method `std::ops::Shl::shl` + --> $DIR/method_list_2.rs:74:5 + | +LL | / pub fn shl(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Shl` or choosing a less ambiguous method name + +error: method `shr` can be confused for the standard trait method `std::ops::Shr::shr` + --> $DIR/method_list_2.rs:78:5 + | +LL | / pub fn shr(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Shr` or choosing a less ambiguous method name + +error: method `sub` can be confused for the standard trait method `std::ops::Sub::sub` + --> $DIR/method_list_2.rs:82:5 + | +LL | / pub fn sub(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Sub` or choosing a less ambiguous method name + +error: aborting due to 15 previous errors + diff --git a/tests/ui/single_char_push_str.fixed b/tests/ui/single_char_push_str.fixed new file mode 100644 index 00000000000..0812c026a64 --- /dev/null +++ b/tests/ui/single_char_push_str.fixed @@ -0,0 +1,15 @@ +// run-rustfix +#![warn(clippy::single_char_push_str)] + +fn main() { + let mut string = String::new(); + string.push('R'); + string.push('\''); + + string.push('u'); + string.push_str("st"); + string.push_str(""); + string.push('\x52'); + string.push('\u{0052}'); + string.push('a'); +} diff --git a/tests/ui/single_char_push_str.rs b/tests/ui/single_char_push_str.rs new file mode 100644 index 00000000000..ab293bbe4ee --- /dev/null +++ b/tests/ui/single_char_push_str.rs @@ -0,0 +1,15 @@ +// run-rustfix +#![warn(clippy::single_char_push_str)] + +fn main() { + let mut string = String::new(); + string.push_str("R"); + string.push_str("'"); + + string.push('u'); + string.push_str("st"); + string.push_str(""); + string.push_str("\x52"); + string.push_str("\u{0052}"); + string.push_str(r##"a"##); +} diff --git a/tests/ui/single_char_push_str.stderr b/tests/ui/single_char_push_str.stderr new file mode 100644 index 00000000000..0e9bdaa23e7 --- /dev/null +++ b/tests/ui/single_char_push_str.stderr @@ -0,0 +1,34 @@ +error: calling `push_str()` using a single-character string literal + --> $DIR/single_char_push_str.rs:6:5 + | +LL | string.push_str("R"); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('R')` + | + = note: `-D clippy::single-char-push-str` implied by `-D warnings` + +error: calling `push_str()` using a single-character string literal + --> $DIR/single_char_push_str.rs:7:5 + | +LL | string.push_str("'"); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/'')` + +error: calling `push_str()` using a single-character string literal + --> $DIR/single_char_push_str.rs:12:5 + | +LL | string.push_str("/x52"); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/x52')` + +error: calling `push_str()` using a single-character string literal + --> $DIR/single_char_push_str.rs:13:5 + | +LL | string.push_str("/u{0052}"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/u{0052}')` + +error: calling `push_str()` using a single-character string literal + --> $DIR/single_char_push_str.rs:14:5 + | +LL | string.push_str(r##"a"##); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('a')` + +error: aborting due to 5 previous errors + diff --git a/tests/ui/stable_sort_primitive.stderr b/tests/ui/stable_sort_primitive.stderr index b0b729ede48..780389f32bc 100644 --- a/tests/ui/stable_sort_primitive.stderr +++ b/tests/ui/stable_sort_primitive.stderr @@ -1,4 +1,4 @@ -error: Use sort_unstable instead of sort +error: used sort instead of sort_unstable to sort primitive type `i32` --> $DIR/stable_sort_primitive.rs:7:5 | LL | vec.sort(); @@ -6,37 +6,37 @@ LL | vec.sort(); | = note: `-D clippy::stable-sort-primitive` implied by `-D warnings` -error: Use sort_unstable instead of sort +error: used sort instead of sort_unstable to sort primitive type `bool` --> $DIR/stable_sort_primitive.rs:9:5 | LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` -error: Use sort_unstable instead of sort +error: used sort instead of sort_unstable to sort primitive type `char` --> $DIR/stable_sort_primitive.rs:11:5 | LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` -error: Use sort_unstable instead of sort +error: used sort instead of sort_unstable to sort primitive type `str` --> $DIR/stable_sort_primitive.rs:13:5 | LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` -error: Use sort_unstable instead of sort +error: used sort instead of sort_unstable to sort primitive type `tuple` --> $DIR/stable_sort_primitive.rs:15:5 | LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` -error: Use sort_unstable instead of sort +error: used sort instead of sort_unstable to sort primitive type `array` --> $DIR/stable_sort_primitive.rs:17:5 | LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` -error: Use sort_unstable instead of sort +error: used sort instead of sort_unstable to sort primitive type `i32` --> $DIR/stable_sort_primitive.rs:19:5 | LL | arr.sort(); diff --git a/tests/ui/suspicious_arithmetic_impl.rs b/tests/ui/suspicious_arithmetic_impl.rs index 60c2f3ec9b6..5c280efac1a 100644 --- a/tests/ui/suspicious_arithmetic_impl.rs +++ b/tests/ui/suspicious_arithmetic_impl.rs @@ -1,5 +1,7 @@ #![warn(clippy::suspicious_arithmetic_impl)] -use std::ops::{Add, AddAssign, BitOrAssign, Div, DivAssign, Mul, MulAssign, Sub}; +use std::ops::{ + Add, AddAssign, BitAnd, BitOr, BitOrAssign, BitXor, Div, DivAssign, Mul, MulAssign, Rem, Shl, Shr, Sub, +}; #[derive(Copy, Clone)] struct Foo(u32); @@ -61,6 +63,54 @@ impl Div for Foo { } } +impl Rem for Foo { + type Output = Foo; + + fn rem(self, other: Self) -> Self { + Foo(self.0 / other.0) + } +} + +impl BitAnd for Foo { + type Output = Foo; + + fn bitand(self, other: Self) -> Self { + Foo(self.0 | other.0) + } +} + +impl BitOr for Foo { + type Output = Foo; + + fn bitor(self, other: Self) -> Self { + Foo(self.0 ^ other.0) + } +} + +impl BitXor for Foo { + type Output = Foo; + + fn bitxor(self, other: Self) -> Self { + Foo(self.0 & other.0) + } +} + +impl Shl for Foo { + type Output = Foo; + + fn shl(self, other: Self) -> Self { + Foo(self.0 >> other.0) + } +} + +impl Shr for Foo { + type Output = Foo; + + fn shr(self, other: Self) -> Self { + Foo(self.0 << other.0) + } +} + struct Bar(i32); impl Add for Bar { diff --git a/tests/ui/suspicious_arithmetic_impl.stderr b/tests/ui/suspicious_arithmetic_impl.stderr index 23d47e3f1ff..388fc740082 100644 --- a/tests/ui/suspicious_arithmetic_impl.stderr +++ b/tests/ui/suspicious_arithmetic_impl.stderr @@ -1,5 +1,5 @@ error: suspicious use of binary operator in `Add` impl - --> $DIR/suspicious_arithmetic_impl.rs:11:20 + --> $DIR/suspicious_arithmetic_impl.rs:13:20 | LL | Foo(self.0 - other.0) | ^ @@ -7,7 +7,7 @@ LL | Foo(self.0 - other.0) = note: `-D clippy::suspicious-arithmetic-impl` implied by `-D warnings` error: suspicious use of binary operator in `AddAssign` impl - --> $DIR/suspicious_arithmetic_impl.rs:17:23 + --> $DIR/suspicious_arithmetic_impl.rs:19:23 | LL | *self = *self - other; | ^ @@ -15,10 +15,46 @@ LL | *self = *self - other; = note: `#[deny(clippy::suspicious_op_assign_impl)]` on by default error: suspicious use of binary operator in `MulAssign` impl - --> $DIR/suspicious_arithmetic_impl.rs:30:16 + --> $DIR/suspicious_arithmetic_impl.rs:32:16 | LL | self.0 /= other.0; | ^^ -error: aborting due to 3 previous errors +error: suspicious use of binary operator in `Rem` impl + --> $DIR/suspicious_arithmetic_impl.rs:70:20 + | +LL | Foo(self.0 / other.0) + | ^ + +error: suspicious use of binary operator in `BitAnd` impl + --> $DIR/suspicious_arithmetic_impl.rs:78:20 + | +LL | Foo(self.0 | other.0) + | ^ + +error: suspicious use of binary operator in `BitOr` impl + --> $DIR/suspicious_arithmetic_impl.rs:86:20 + | +LL | Foo(self.0 ^ other.0) + | ^ + +error: suspicious use of binary operator in `BitXor` impl + --> $DIR/suspicious_arithmetic_impl.rs:94:20 + | +LL | Foo(self.0 & other.0) + | ^ + +error: suspicious use of binary operator in `Shl` impl + --> $DIR/suspicious_arithmetic_impl.rs:102:20 + | +LL | Foo(self.0 >> other.0) + | ^^ + +error: suspicious use of binary operator in `Shr` impl + --> $DIR/suspicious_arithmetic_impl.rs:110:20 + | +LL | Foo(self.0 << other.0) + | ^^ + +error: aborting due to 9 previous errors diff --git a/tests/ui/to_string_in_display.rs b/tests/ui/to_string_in_display.rs new file mode 100644 index 00000000000..eb8105c6b6d --- /dev/null +++ b/tests/ui/to_string_in_display.rs @@ -0,0 +1,69 @@ +#![warn(clippy::to_string_in_display)] +#![allow(clippy::inherent_to_string_shadow_display)] + +use std::fmt; + +struct A; +impl A { + fn fmt(&self) { + self.to_string(); + } +} + +trait B { + fn fmt(&self) {} +} + +impl B for A { + fn fmt(&self) { + self.to_string(); + } +} + +impl fmt::Display for A { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.to_string()) + } +} + +fn fmt(a: A) { + a.to_string(); +} + +struct C; + +impl C { + fn to_string(&self) -> String { + String::from("I am C") + } +} + +impl fmt::Display for C { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.to_string()) + } +} + +enum D { + E(String), + F, +} + +impl std::fmt::Display for D { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self { + Self::E(string) => write!(f, "E {}", string.to_string()), + Self::F => write!(f, "F"), + } + } +} + +fn main() { + let a = A; + a.to_string(); + a.fmt(); + fmt(a); + + let c = C; + c.to_string(); +} diff --git a/tests/ui/to_string_in_display.stderr b/tests/ui/to_string_in_display.stderr new file mode 100644 index 00000000000..5f26ef413e2 --- /dev/null +++ b/tests/ui/to_string_in_display.stderr @@ -0,0 +1,10 @@ +error: using `to_string` in `fmt::Display` implementation might lead to infinite recursion + --> $DIR/to_string_in_display.rs:25:25 + | +LL | write!(f, "{}", self.to_string()) + | ^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::to-string-in-display` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/transmute.rs b/tests/ui/transmute.rs index bb853d23704..9f1948359e7 100644 --- a/tests/ui/transmute.rs +++ b/tests/ui/transmute.rs @@ -1,3 +1,4 @@ +#![feature(const_fn_transmute)] #![allow(dead_code)] extern crate core; @@ -81,9 +82,26 @@ fn int_to_bool() { } #[warn(clippy::transmute_int_to_float)] -fn int_to_float() { - let _: f32 = unsafe { std::mem::transmute(0_u32) }; - let _: f32 = unsafe { std::mem::transmute(0_i32) }; +mod int_to_float { + fn test() { + let _: f32 = unsafe { std::mem::transmute(0_u32) }; + let _: f32 = unsafe { std::mem::transmute(0_i32) }; + let _: f64 = unsafe { std::mem::transmute(0_u64) }; + let _: f64 = unsafe { std::mem::transmute(0_i64) }; + } + + mod issue_5747 { + const VALUE32: f32 = unsafe { std::mem::transmute(0_u32) }; + const VALUE64: f64 = unsafe { std::mem::transmute(0_i64) }; + + const fn from_bits_32(v: i32) -> f32 { + unsafe { std::mem::transmute(v) } + } + + const fn from_bits_64(v: u64) -> f64 { + unsafe { std::mem::transmute(v) } + } + } } fn bytes_to_str(b: &[u8], mb: &mut [u8]) { diff --git a/tests/ui/transmute.stderr b/tests/ui/transmute.stderr index 8582080498f..ad9953d12bc 100644 --- a/tests/ui/transmute.stderr +++ b/tests/ui/transmute.stderr @@ -1,5 +1,5 @@ error: transmute from a type (`&T`) to itself - --> $DIR/transmute.rs:19:20 + --> $DIR/transmute.rs:20:20 | LL | let _: &'a T = core::intrinsics::transmute(t); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,67 +7,67 @@ LL | let _: &'a T = core::intrinsics::transmute(t); = note: `-D clippy::useless-transmute` implied by `-D warnings` error: transmute from a reference to a pointer - --> $DIR/transmute.rs:23:23 + --> $DIR/transmute.rs:24:23 | LL | let _: *const T = core::intrinsics::transmute(t); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T` error: transmute from a reference to a pointer - --> $DIR/transmute.rs:25:21 + --> $DIR/transmute.rs:26:21 | LL | let _: *mut T = core::intrinsics::transmute(t); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *mut T` error: transmute from a reference to a pointer - --> $DIR/transmute.rs:27:23 + --> $DIR/transmute.rs:28:23 | LL | let _: *const U = core::intrinsics::transmute(t); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *const U` error: transmute from a type (`std::vec::Vec`) to itself - --> $DIR/transmute.rs:33:27 + --> $DIR/transmute.rs:34:27 | LL | let _: Vec = core::intrinsics::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> $DIR/transmute.rs:35:27 + --> $DIR/transmute.rs:36:27 | LL | let _: Vec = core::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> $DIR/transmute.rs:37:27 + --> $DIR/transmute.rs:38:27 | LL | let _: Vec = std::intrinsics::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> $DIR/transmute.rs:39:27 + --> $DIR/transmute.rs:40:27 | LL | let _: Vec = std::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> $DIR/transmute.rs:41:27 + --> $DIR/transmute.rs:42:27 | LL | let _: Vec = my_transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^ error: transmute from an integer to a pointer - --> $DIR/transmute.rs:43:31 + --> $DIR/transmute.rs:44:31 | LL | let _: *const usize = std::mem::transmute(5_isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `5_isize as *const usize` error: transmute from an integer to a pointer - --> $DIR/transmute.rs:47:31 + --> $DIR/transmute.rs:48:31 | LL | let _: *const usize = std::mem::transmute(1 + 1usize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(1 + 1usize) as *const usize` error: transmute from a type (`*const Usize`) to the type that it points to (`Usize`) - --> $DIR/transmute.rs:62:24 + --> $DIR/transmute.rs:63:24 | LL | let _: Usize = core::intrinsics::transmute(int_const_ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -75,25 +75,25 @@ LL | let _: Usize = core::intrinsics::transmute(int_const_ptr); = note: `-D clippy::crosspointer-transmute` implied by `-D warnings` error: transmute from a type (`*mut Usize`) to the type that it points to (`Usize`) - --> $DIR/transmute.rs:64:24 + --> $DIR/transmute.rs:65:24 | LL | let _: Usize = core::intrinsics::transmute(int_mut_ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`Usize`) to a pointer to that type (`*const Usize`) - --> $DIR/transmute.rs:66:31 + --> $DIR/transmute.rs:67:31 | LL | let _: *const Usize = core::intrinsics::transmute(my_int()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`Usize`) to a pointer to that type (`*mut Usize`) - --> $DIR/transmute.rs:68:29 + --> $DIR/transmute.rs:69:29 | LL | let _: *mut Usize = core::intrinsics::transmute(my_int()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a `u32` to a `char` - --> $DIR/transmute.rs:74:28 + --> $DIR/transmute.rs:75:28 | LL | let _: char = unsafe { std::mem::transmute(0_u32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_u32).unwrap()` @@ -101,13 +101,13 @@ LL | let _: char = unsafe { std::mem::transmute(0_u32) }; = note: `-D clippy::transmute-int-to-char` implied by `-D warnings` error: transmute from a `i32` to a `char` - --> $DIR/transmute.rs:75:28 + --> $DIR/transmute.rs:76:28 | LL | let _: char = unsafe { std::mem::transmute(0_i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_i32 as u32).unwrap()` error: transmute from a `u8` to a `bool` - --> $DIR/transmute.rs:80:28 + --> $DIR/transmute.rs:81:28 | LL | let _: bool = unsafe { std::mem::transmute(0_u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `0_u8 != 0` @@ -115,21 +115,33 @@ LL | let _: bool = unsafe { std::mem::transmute(0_u8) }; = note: `-D clippy::transmute-int-to-bool` implied by `-D warnings` error: transmute from a `u32` to a `f32` - --> $DIR/transmute.rs:85:27 + --> $DIR/transmute.rs:87:31 | -LL | let _: f32 = unsafe { std::mem::transmute(0_u32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)` +LL | let _: f32 = unsafe { std::mem::transmute(0_u32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)` | = note: `-D clippy::transmute-int-to-float` implied by `-D warnings` error: transmute from a `i32` to a `f32` - --> $DIR/transmute.rs:86:27 + --> $DIR/transmute.rs:88:31 | -LL | let _: f32 = unsafe { std::mem::transmute(0_i32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_i32 as u32)` +LL | let _: f32 = unsafe { std::mem::transmute(0_i32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_i32 as u32)` + +error: transmute from a `u64` to a `f64` + --> $DIR/transmute.rs:89:31 + | +LL | let _: f64 = unsafe { std::mem::transmute(0_u64) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_u64)` + +error: transmute from a `i64` to a `f64` + --> $DIR/transmute.rs:90:31 + | +LL | let _: f64 = unsafe { std::mem::transmute(0_i64) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_i64 as u64)` error: transmute from a `&[u8]` to a `&str` - --> $DIR/transmute.rs:90:28 + --> $DIR/transmute.rs:108:28 | LL | let _: &str = unsafe { std::mem::transmute(b) }; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(b).unwrap()` @@ -137,10 +149,10 @@ LL | let _: &str = unsafe { std::mem::transmute(b) }; = note: `-D clippy::transmute-bytes-to-str` implied by `-D warnings` error: transmute from a `&mut [u8]` to a `&mut str` - --> $DIR/transmute.rs:91:32 + --> $DIR/transmute.rs:109:32 | LL | let _: &mut str = unsafe { std::mem::transmute(mb) }; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()` -error: aborting due to 22 previous errors +error: aborting due to 24 previous errors diff --git a/tests/ui/transmute_float_to_int.rs b/tests/ui/transmute_float_to_int.rs index ce942751ada..1040fee4b34 100644 --- a/tests/ui/transmute_float_to_int.rs +++ b/tests/ui/transmute_float_to_int.rs @@ -1,4 +1,5 @@ -#[warn(clippy::transmute_float_to_int)] +#![feature(const_fn_transmute)] +#![warn(clippy::transmute_float_to_int)] fn float_to_int() { let _: u32 = unsafe { std::mem::transmute(1f32) }; @@ -9,4 +10,17 @@ fn float_to_int() { let _: u64 = unsafe { std::mem::transmute(-1.0) }; } +mod issue_5747 { + const VALUE32: i32 = unsafe { std::mem::transmute(1f32) }; + const VALUE64: u64 = unsafe { std::mem::transmute(1f64) }; + + const fn to_bits_32(v: f32) -> u32 { + unsafe { std::mem::transmute(v) } + } + + const fn to_bits_64(v: f64) -> i64 { + unsafe { std::mem::transmute(v) } + } +} + fn main() {} diff --git a/tests/ui/transmute_float_to_int.stderr b/tests/ui/transmute_float_to_int.stderr index eb786bb39f9..5a40cf381d6 100644 --- a/tests/ui/transmute_float_to_int.stderr +++ b/tests/ui/transmute_float_to_int.stderr @@ -1,5 +1,5 @@ error: transmute from a `f32` to a `u32` - --> $DIR/transmute_float_to_int.rs:4:27 + --> $DIR/transmute_float_to_int.rs:5:27 | LL | let _: u32 = unsafe { std::mem::transmute(1f32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f32.to_bits()` @@ -7,31 +7,31 @@ LL | let _: u32 = unsafe { std::mem::transmute(1f32) }; = note: `-D clippy::transmute-float-to-int` implied by `-D warnings` error: transmute from a `f32` to a `i32` - --> $DIR/transmute_float_to_int.rs:5:27 + --> $DIR/transmute_float_to_int.rs:6:27 | LL | let _: i32 = unsafe { std::mem::transmute(1f32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f32.to_bits() as i32` error: transmute from a `f64` to a `u64` - --> $DIR/transmute_float_to_int.rs:6:27 + --> $DIR/transmute_float_to_int.rs:7:27 | LL | let _: u64 = unsafe { std::mem::transmute(1f64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f64.to_bits()` error: transmute from a `f64` to a `i64` - --> $DIR/transmute_float_to_int.rs:7:27 + --> $DIR/transmute_float_to_int.rs:8:27 | LL | let _: i64 = unsafe { std::mem::transmute(1f64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f64.to_bits() as i64` error: transmute from a `f64` to a `u64` - --> $DIR/transmute_float_to_int.rs:8:27 + --> $DIR/transmute_float_to_int.rs:9:27 | LL | let _: u64 = unsafe { std::mem::transmute(1.0) }; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1.0f64.to_bits()` error: transmute from a `f64` to a `u64` - --> $DIR/transmute_float_to_int.rs:9:27 + --> $DIR/transmute_float_to_int.rs:10:27 | LL | let _: u64 = unsafe { std::mem::transmute(-1.0) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-1.0f64).to_bits()` diff --git a/tests/ui/trivially_copy_pass_by_ref.rs b/tests/ui/trivially_copy_pass_by_ref.rs index 316426f1cf1..e7e0a31febc 100644 --- a/tests/ui/trivially_copy_pass_by_ref.rs +++ b/tests/ui/trivially_copy_pass_by_ref.rs @@ -97,6 +97,24 @@ mod issue3992 { pub fn c(d: &u16) {} } +mod issue5876 { + // Don't lint here as it is always inlined + #[inline(always)] + fn foo_always(x: &i32) { + println!("{}", x); + } + + #[inline(never)] + fn foo_never(x: &i32) { + println!("{}", x); + } + + #[inline] + fn foo(x: &i32) { + println!("{}", x); + } +} + fn main() { let (mut foo, bar) = (Foo(0), Bar([0; 24])); let (mut a, b, c, x, y, z) = (0, 0, Bar([0; 24]), 0, Foo(0), 0); diff --git a/tests/ui/trivially_copy_pass_by_ref.stderr b/tests/ui/trivially_copy_pass_by_ref.stderr index be0914e4a79..ccc3cdb2b74 100644 --- a/tests/ui/trivially_copy_pass_by_ref.stderr +++ b/tests/ui/trivially_copy_pass_by_ref.stderr @@ -94,5 +94,17 @@ error: this argument (N byte) is passed by reference, but would be more efficien LL | fn trait_method2(&self, _color: &Color); | ^^^^^^ help: consider passing by value instead: `Color` -error: aborting due to 15 previous errors +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> $DIR/trivially_copy_pass_by_ref.rs:108:21 + | +LL | fn foo_never(x: &i32) { + | ^^^^ help: consider passing by value instead: `i32` + +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> $DIR/trivially_copy_pass_by_ref.rs:113:15 + | +LL | fn foo(x: &i32) { + | ^^^^ help: consider passing by value instead: `i32` + +error: aborting due to 17 previous errors diff --git a/tests/ui/unnecessary_clone.rs b/tests/ui/unnecessary_clone.rs index 2c9d4d39e6c..e785ac02feb 100644 --- a/tests/ui/unnecessary_clone.rs +++ b/tests/ui/unnecessary_clone.rs @@ -90,3 +90,21 @@ mod many_derefs { let _ = &encoded.clone(); } } + +mod issue2076 { + use std::rc::Rc; + + macro_rules! try_opt { + ($expr: expr) => { + match $expr { + Some(value) => value, + None => return None, + } + }; + } + + fn func() -> Option> { + let rc = Rc::new(42); + Some(try_opt!(Some(rc)).clone()) + } +} diff --git a/tests/ui/unnecessary_clone.stderr b/tests/ui/unnecessary_clone.stderr index 113fab69009..5ffa6c4fd06 100644 --- a/tests/ui/unnecessary_clone.stderr +++ b/tests/ui/unnecessary_clone.stderr @@ -96,5 +96,11 @@ help: or try being explicit if you are sure, that you want to clone a reference LL | let _ = &<&[u8]>::clone(encoded); | ^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 11 previous errors +error: using `.clone()` on a ref-counted pointer + --> $DIR/unnecessary_clone.rs:108:14 + | +LL | Some(try_opt!(Some(rc)).clone()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Rc::::clone(&try_opt!(Some(rc)))` + +error: aborting due to 12 previous errors diff --git a/tests/ui/unnecessary_lazy_eval.fixed b/tests/ui/unnecessary_lazy_eval.fixed new file mode 100644 index 00000000000..fa66e68794e --- /dev/null +++ b/tests/ui/unnecessary_lazy_eval.fixed @@ -0,0 +1,117 @@ +// run-rustfix +#![warn(clippy::unnecessary_lazy_evaluations)] +#![allow(clippy::redundant_closure)] +#![allow(clippy::bind_instead_of_map)] + +struct Deep(Option); + +#[derive(Copy, Clone)] +struct SomeStruct { + some_field: usize, +} + +impl SomeStruct { + fn return_some_field(&self) -> usize { + self.some_field + } +} + +fn some_call() -> T { + T::default() +} + +fn main() { + let astronomers_pi = 10; + let ext_arr: [usize; 1] = [2]; + let ext_str = SomeStruct { some_field: 10 }; + + let mut opt = Some(42); + let ext_opt = Some(42); + let nested_opt = Some(Some(42)); + let nested_tuple_opt = Some(Some((42, 43))); + + // Should lint - Option + let _ = opt.unwrap_or(2); + let _ = opt.unwrap_or(astronomers_pi); + let _ = opt.unwrap_or(ext_str.some_field); + let _ = opt.unwrap_or(ext_arr[0]); + let _ = opt.and(ext_opt); + let _ = opt.or(ext_opt); + let _ = opt.or(None); + let _ = opt.get_or_insert(2); + let _ = opt.ok_or(2); + let _ = opt.ok_or(ext_arr[0]); + + // Cases when unwrap is not called on a simple variable + let _ = Some(10).unwrap_or(2); + let _ = Some(10).and(ext_opt); + let _: Option = None.or(ext_opt); + let _ = None.get_or_insert(2); + let _: Result = None.ok_or(2); + let _: Option = None.or(None); + + let mut deep = Deep(Some(42)); + let _ = deep.0.unwrap_or(2); + let _ = deep.0.and(ext_opt); + let _ = deep.0.or(None); + let _ = deep.0.get_or_insert(2); + let _ = deep.0.ok_or(2); + + // Should not lint - Option + let _ = opt.unwrap_or_else(|| ext_str.return_some_field()); + let _ = nested_opt.unwrap_or_else(|| Some(some_call())); + let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2))); + let _ = nested_tuple_opt.unwrap_or_else(|| Some((some_call(), some_call()))); + let _ = opt.or_else(some_call); + let _ = opt.or_else(|| some_call()); + let _: Result = opt.ok_or_else(|| some_call()); + let _: Result = opt.ok_or_else(some_call); + let _ = deep.0.get_or_insert_with(|| some_call()); + let _ = deep.0.or_else(some_call); + let _ = deep.0.or_else(|| some_call()); + + // These are handled by bind_instead_of_map + let _ = Some(10).and_then(|idx| Some(ext_arr[idx])); + let _ = Some(10).and_then(|idx| Some(idx)); + let _: Option = None.or_else(|| Some(3)); + let _ = deep.0.or_else(|| Some(3)); + let _ = opt.or_else(|| Some(3)); + + // Should lint - Result + let res: Result = Err(5); + let res2: Result = Err(SomeStruct { some_field: 5 }); + + let _ = res2.unwrap_or(2); + let _ = res2.unwrap_or(astronomers_pi); + let _ = res2.unwrap_or(ext_str.some_field); + + // Should not lint - Result + let _ = res.unwrap_or_else(|err| err); + let _ = res.unwrap_or_else(|err| ext_arr[err]); + let _ = res2.unwrap_or_else(|err| err.some_field); + let _ = res2.unwrap_or_else(|err| err.return_some_field()); + let _ = res2.unwrap_or_else(|_| ext_str.return_some_field()); + + let _: Result = res.and_then(|x| Ok(x)); + let _: Result = res.and_then(|x| Err(x)); + + let _: Result = res.or_else(|err| Ok(err)); + let _: Result = res.or_else(|err| Err(err)); + + // These are handled by bind_instead_of_map + let _: Result = res.and_then(|_| Ok(2)); + let _: Result = res.and_then(|_| Ok(astronomers_pi)); + let _: Result = res.and_then(|_| Ok(ext_str.some_field)); + + let _: Result = res.and_then(|_| Err(2)); + let _: Result = res.and_then(|_| Err(astronomers_pi)); + let _: Result = res.and_then(|_| Err(ext_str.some_field)); + + let _: Result = res.or_else(|_| Ok(2)); + let _: Result = res.or_else(|_| Ok(astronomers_pi)); + let _: Result = res.or_else(|_| Ok(ext_str.some_field)); + + let _: Result = res.or_else(|_| Err(2)); + let _: Result = res.or_else(|_| Err(astronomers_pi)); + let _: Result = res.or_else(|_| Err(ext_str.some_field)); +} diff --git a/tests/ui/unnecessary_lazy_eval.rs b/tests/ui/unnecessary_lazy_eval.rs new file mode 100644 index 00000000000..04f47d1aa29 --- /dev/null +++ b/tests/ui/unnecessary_lazy_eval.rs @@ -0,0 +1,117 @@ +// run-rustfix +#![warn(clippy::unnecessary_lazy_evaluations)] +#![allow(clippy::redundant_closure)] +#![allow(clippy::bind_instead_of_map)] + +struct Deep(Option); + +#[derive(Copy, Clone)] +struct SomeStruct { + some_field: usize, +} + +impl SomeStruct { + fn return_some_field(&self) -> usize { + self.some_field + } +} + +fn some_call() -> T { + T::default() +} + +fn main() { + let astronomers_pi = 10; + let ext_arr: [usize; 1] = [2]; + let ext_str = SomeStruct { some_field: 10 }; + + let mut opt = Some(42); + let ext_opt = Some(42); + let nested_opt = Some(Some(42)); + let nested_tuple_opt = Some(Some((42, 43))); + + // Should lint - Option + let _ = opt.unwrap_or_else(|| 2); + let _ = opt.unwrap_or_else(|| astronomers_pi); + let _ = opt.unwrap_or_else(|| ext_str.some_field); + let _ = opt.unwrap_or_else(|| ext_arr[0]); + let _ = opt.and_then(|_| ext_opt); + let _ = opt.or_else(|| ext_opt); + let _ = opt.or_else(|| None); + let _ = opt.get_or_insert_with(|| 2); + let _ = opt.ok_or_else(|| 2); + let _ = opt.ok_or_else(|| ext_arr[0]); + + // Cases when unwrap is not called on a simple variable + let _ = Some(10).unwrap_or_else(|| 2); + let _ = Some(10).and_then(|_| ext_opt); + let _: Option = None.or_else(|| ext_opt); + let _ = None.get_or_insert_with(|| 2); + let _: Result = None.ok_or_else(|| 2); + let _: Option = None.or_else(|| None); + + let mut deep = Deep(Some(42)); + let _ = deep.0.unwrap_or_else(|| 2); + let _ = deep.0.and_then(|_| ext_opt); + let _ = deep.0.or_else(|| None); + let _ = deep.0.get_or_insert_with(|| 2); + let _ = deep.0.ok_or_else(|| 2); + + // Should not lint - Option + let _ = opt.unwrap_or_else(|| ext_str.return_some_field()); + let _ = nested_opt.unwrap_or_else(|| Some(some_call())); + let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2))); + let _ = nested_tuple_opt.unwrap_or_else(|| Some((some_call(), some_call()))); + let _ = opt.or_else(some_call); + let _ = opt.or_else(|| some_call()); + let _: Result = opt.ok_or_else(|| some_call()); + let _: Result = opt.ok_or_else(some_call); + let _ = deep.0.get_or_insert_with(|| some_call()); + let _ = deep.0.or_else(some_call); + let _ = deep.0.or_else(|| some_call()); + + // These are handled by bind_instead_of_map + let _ = Some(10).and_then(|idx| Some(ext_arr[idx])); + let _ = Some(10).and_then(|idx| Some(idx)); + let _: Option = None.or_else(|| Some(3)); + let _ = deep.0.or_else(|| Some(3)); + let _ = opt.or_else(|| Some(3)); + + // Should lint - Result + let res: Result = Err(5); + let res2: Result = Err(SomeStruct { some_field: 5 }); + + let _ = res2.unwrap_or_else(|_| 2); + let _ = res2.unwrap_or_else(|_| astronomers_pi); + let _ = res2.unwrap_or_else(|_| ext_str.some_field); + + // Should not lint - Result + let _ = res.unwrap_or_else(|err| err); + let _ = res.unwrap_or_else(|err| ext_arr[err]); + let _ = res2.unwrap_or_else(|err| err.some_field); + let _ = res2.unwrap_or_else(|err| err.return_some_field()); + let _ = res2.unwrap_or_else(|_| ext_str.return_some_field()); + + let _: Result = res.and_then(|x| Ok(x)); + let _: Result = res.and_then(|x| Err(x)); + + let _: Result = res.or_else(|err| Ok(err)); + let _: Result = res.or_else(|err| Err(err)); + + // These are handled by bind_instead_of_map + let _: Result = res.and_then(|_| Ok(2)); + let _: Result = res.and_then(|_| Ok(astronomers_pi)); + let _: Result = res.and_then(|_| Ok(ext_str.some_field)); + + let _: Result = res.and_then(|_| Err(2)); + let _: Result = res.and_then(|_| Err(astronomers_pi)); + let _: Result = res.and_then(|_| Err(ext_str.some_field)); + + let _: Result = res.or_else(|_| Ok(2)); + let _: Result = res.or_else(|_| Ok(astronomers_pi)); + let _: Result = res.or_else(|_| Ok(ext_str.some_field)); + + let _: Result = res.or_else(|_| Err(2)); + let _: Result = res.or_else(|_| Err(astronomers_pi)); + let _: Result = res.or_else(|_| Err(ext_str.some_field)); +} diff --git a/tests/ui/unnecessary_lazy_eval.stderr b/tests/ui/unnecessary_lazy_eval.stderr new file mode 100644 index 00000000000..5c1b2eb1f14 --- /dev/null +++ b/tests/ui/unnecessary_lazy_eval.stderr @@ -0,0 +1,148 @@ +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:34:13 + | +LL | let _ = opt.unwrap_or_else(|| 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(2)` + | + = note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:35:13 + | +LL | let _ = opt.unwrap_or_else(|| astronomers_pi); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(astronomers_pi)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:36:13 + | +LL | let _ = opt.unwrap_or_else(|| ext_str.some_field); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(ext_str.some_field)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:37:13 + | +LL | let _ = opt.unwrap_or_else(|| ext_arr[0]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(ext_arr[0])` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:38:13 + | +LL | let _ = opt.and_then(|_| ext_opt); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `opt.and(ext_opt)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:39:13 + | +LL | let _ = opt.or_else(|| ext_opt); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `opt.or(ext_opt)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:40:13 + | +LL | let _ = opt.or_else(|| None); + | ^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `opt.or(None)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:41:13 + | +LL | let _ = opt.get_or_insert_with(|| 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `opt.get_or_insert(2)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:42:13 + | +LL | let _ = opt.ok_or_else(|| 2); + | ^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `opt.ok_or(2)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:43:13 + | +LL | let _ = opt.ok_or_else(|| ext_arr[0]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `opt.ok_or(ext_arr[0])` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:46:13 + | +LL | let _ = Some(10).unwrap_or_else(|| 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `Some(10).unwrap_or(2)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:47:13 + | +LL | let _ = Some(10).and_then(|_| ext_opt); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `Some(10).and(ext_opt)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:48:28 + | +LL | let _: Option = None.or_else(|| ext_opt); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(ext_opt)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:49:13 + | +LL | let _ = None.get_or_insert_with(|| 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `None.get_or_insert(2)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:50:35 + | +LL | let _: Result = None.ok_or_else(|| 2); + | ^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `None.ok_or(2)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:51:28 + | +LL | let _: Option = None.or_else(|| None); + | ^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(None)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:54:13 + | +LL | let _ = deep.0.unwrap_or_else(|| 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `deep.0.unwrap_or(2)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:55:13 + | +LL | let _ = deep.0.and_then(|_| ext_opt); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `deep.0.and(ext_opt)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:56:13 + | +LL | let _ = deep.0.or_else(|| None); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `deep.0.or(None)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:57:13 + | +LL | let _ = deep.0.get_or_insert_with(|| 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `deep.0.get_or_insert(2)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:58:13 + | +LL | let _ = deep.0.ok_or_else(|| 2); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `deep.0.ok_or(2)` + +error: unnecessary closure used to substitute value for `Result::Err` + --> $DIR/unnecessary_lazy_eval.rs:84:13 + | +LL | let _ = res2.unwrap_or_else(|_| 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(2)` + +error: unnecessary closure used to substitute value for `Result::Err` + --> $DIR/unnecessary_lazy_eval.rs:85:13 + | +LL | let _ = res2.unwrap_or_else(|_| astronomers_pi); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(astronomers_pi)` + +error: unnecessary closure used to substitute value for `Result::Err` + --> $DIR/unnecessary_lazy_eval.rs:86:13 + | +LL | let _ = res2.unwrap_or_else(|_| ext_str.some_field); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(ext_str.some_field)` + +error: aborting due to 24 previous errors + diff --git a/tests/ui/unwrap_in_result.rs b/tests/ui/unwrap_in_result.rs new file mode 100644 index 00000000000..2aa842adc85 --- /dev/null +++ b/tests/ui/unwrap_in_result.rs @@ -0,0 +1,44 @@ +#![warn(clippy::unwrap_in_result)] + +struct A; + +impl A { + // should not be detected + fn good_divisible_by_3(i_str: String) -> Result { + // checks whether a string represents a number divisible by 3 + let i_result = i_str.parse::(); + match i_result { + Err(_e) => Err("Not a number".to_string()), + Ok(i) => { + if i % 3 == 0 { + return Ok(true); + } + Err("Number is not divisible by 3".to_string()) + }, + } + } + + // should be detected + fn bad_divisible_by_3(i_str: String) -> Result { + // checks whether a string represents a number divisible by 3 + let i = i_str.parse::().unwrap(); + if i % 3 == 0 { + Ok(true) + } else { + Err("Number is not divisible by 3".to_string()) + } + } + + fn example_option_expect(i_str: String) -> Option { + let i = i_str.parse::().expect("not a number"); + if i % 3 == 0 { + return Some(true); + } + None + } +} + +fn main() { + A::bad_divisible_by_3("3".to_string()); + A::good_divisible_by_3("3".to_string()); +} diff --git a/tests/ui/unwrap_in_result.stderr b/tests/ui/unwrap_in_result.stderr new file mode 100644 index 00000000000..56bc2f2d1c0 --- /dev/null +++ b/tests/ui/unwrap_in_result.stderr @@ -0,0 +1,41 @@ +error: used unwrap or expect in a function that returns result or option + --> $DIR/unwrap_in_result.rs:22:5 + | +LL | / fn bad_divisible_by_3(i_str: String) -> Result { +LL | | // checks whether a string represents a number divisible by 3 +LL | | let i = i_str.parse::().unwrap(); +LL | | if i % 3 == 0 { +... | +LL | | } +LL | | } + | |_____^ + | + = note: `-D clippy::unwrap-in-result` implied by `-D warnings` + = help: unwrap and expect should not be used in a function that returns result or option +note: potential non-recoverable error(s) + --> $DIR/unwrap_in_result.rs:24:17 + | +LL | let i = i_str.parse::().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: used unwrap or expect in a function that returns result or option + --> $DIR/unwrap_in_result.rs:32:5 + | +LL | / fn example_option_expect(i_str: String) -> Option { +LL | | let i = i_str.parse::().expect("not a number"); +LL | | if i % 3 == 0 { +LL | | return Some(true); +LL | | } +LL | | None +LL | | } + | |_____^ + | + = help: unwrap and expect should not be used in a function that returns result or option +note: potential non-recoverable error(s) + --> $DIR/unwrap_in_result.rs:33:17 + | +LL | let i = i_str.parse::().expect("not a number"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/useless_conversion.fixed b/tests/ui/useless_conversion.fixed index 813cdaecaa9..8a9b0cd3cf0 100644 --- a/tests/ui/useless_conversion.fixed +++ b/tests/ui/useless_conversion.fixed @@ -64,4 +64,9 @@ fn main() { let _ = "".lines(); let _ = vec![1, 2, 3].into_iter(); let _: String = format!("Hello {}", "world"); + + // keep parenthesis around `a + b` for suggestion (see #4750) + let a: i32 = 1; + let b: i32 = 1; + let _ = (a + b) * 3; } diff --git a/tests/ui/useless_conversion.rs b/tests/ui/useless_conversion.rs index 540fea23b36..4faa1572973 100644 --- a/tests/ui/useless_conversion.rs +++ b/tests/ui/useless_conversion.rs @@ -64,4 +64,9 @@ fn main() { let _ = "".lines().into_iter(); let _ = vec![1, 2, 3].into_iter().into_iter(); let _: String = format!("Hello {}", "world").into(); + + // keep parenthesis around `a + b` for suggestion (see #4750) + let a: i32 = 1; + let b: i32 = 1; + let _ = i32::from(a + b) * 3; } diff --git a/tests/ui/useless_conversion.stderr b/tests/ui/useless_conversion.stderr index b958b035452..f1e880d2696 100644 --- a/tests/ui/useless_conversion.stderr +++ b/tests/ui/useless_conversion.stderr @@ -64,5 +64,11 @@ error: useless conversion to the same type LL | let _: String = format!("Hello {}", "world").into(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `format!("Hello {}", "world")` -error: aborting due to 10 previous errors +error: useless conversion to the same type + --> $DIR/useless_conversion.rs:71:13 + | +LL | let _ = i32::from(a + b) * 3; + | ^^^^^^^^^^^^^^^^ help: consider removing `i32::from()`: `(a + b)` + +error: aborting due to 11 previous errors diff --git a/tests/ui/vec.fixed b/tests/ui/vec.fixed index e73a791891f..85677159620 100644 --- a/tests/ui/vec.fixed +++ b/tests/ui/vec.fixed @@ -52,4 +52,11 @@ fn main() { for a in vec![NonCopy, NonCopy] { println!("{:?}", a); } + + on_vec(&vec![1; 201]); // Ok, size of `vec` higher than `too_large_for_stack` + + // Ok + for a in vec![1; 201] { + println!("{:?}", a); + } } diff --git a/tests/ui/vec.rs b/tests/ui/vec.rs index 3eb960f53d7..03b8ee81665 100644 --- a/tests/ui/vec.rs +++ b/tests/ui/vec.rs @@ -52,4 +52,11 @@ fn main() { for a in vec![NonCopy, NonCopy] { println!("{:?}", a); } + + on_vec(&vec![1; 201]); // Ok, size of `vec` higher than `too_large_for_stack` + + // Ok + for a in vec![1; 201] { + println!("{:?}", a); + } } diff --git a/tests/ui/wildcard_imports.fixed b/tests/ui/wildcard_imports.fixed index 67423e6ec1d..287f8935327 100644 --- a/tests/ui/wildcard_imports.fixed +++ b/tests/ui/wildcard_imports.fixed @@ -20,6 +20,7 @@ use wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar; use wildcard_imports_helper::{ExternA, extern_foo}; use std::io::prelude::*; +use wildcard_imports_helper::prelude::v1::*; struct ReadFoo; @@ -75,6 +76,7 @@ fn main() { let _ = A; let _ = inner_struct_mod::C; let _ = ExternA; + let _ = PreludeModAnywhere; double_struct_import_test!(); double_struct_import_test!(); diff --git a/tests/ui/wildcard_imports.rs b/tests/ui/wildcard_imports.rs index 3ad1a29aeba..1f261159f4a 100644 --- a/tests/ui/wildcard_imports.rs +++ b/tests/ui/wildcard_imports.rs @@ -20,6 +20,7 @@ use wildcard_imports_helper::inner::inner_for_self_import::*; use wildcard_imports_helper::*; use std::io::prelude::*; +use wildcard_imports_helper::prelude::v1::*; struct ReadFoo; @@ -75,6 +76,7 @@ fn main() { let _ = A; let _ = inner_struct_mod::C; let _ = ExternA; + let _ = PreludeModAnywhere; double_struct_import_test!(); double_struct_import_test!(); diff --git a/tests/ui/wildcard_imports.stderr b/tests/ui/wildcard_imports.stderr index fab43b738eb..351988f31ea 100644 --- a/tests/ui/wildcard_imports.stderr +++ b/tests/ui/wildcard_imports.stderr @@ -37,55 +37,55 @@ LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:89:13 + --> $DIR/wildcard_imports.rs:91:13 | LL | use crate::fn_mod::*; | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:95:75 + --> $DIR/wildcard_imports.rs:97:75 | LL | use wildcard_imports_helper::inner::inner_for_self_import::{self, *}; | ^ help: try: `inner_extern_foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:96:13 + --> $DIR/wildcard_imports.rs:98:13 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:107:20 + --> $DIR/wildcard_imports.rs:109:20 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^ help: try: `inner::inner_foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:107:30 + --> $DIR/wildcard_imports.rs:109:30 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^^ help: try: `inner2::inner_bar` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:114:13 + --> $DIR/wildcard_imports.rs:116:13 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:143:9 + --> $DIR/wildcard_imports.rs:145:9 | LL | use crate::in_fn_test::*; | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:152:9 + --> $DIR/wildcard_imports.rs:154:9 | LL | use crate:: in_fn_test:: * ; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:153:9 + --> $DIR/wildcard_imports.rs:155:9 | LL | use crate:: fn_mod:: | _________^ @@ -93,31 +93,31 @@ LL | | *; | |_________^ help: try: `crate:: fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:164:13 + --> $DIR/wildcard_imports.rs:166:13 | LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:199:17 + --> $DIR/wildcard_imports.rs:201:17 | LL | use super::*; | ^^^^^^^^ help: try: `super::insidefoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:207:13 + --> $DIR/wildcard_imports.rs:209:13 | LL | use super_imports::*; | ^^^^^^^^^^^^^^^^ help: try: `super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:216:17 + --> $DIR/wildcard_imports.rs:218:17 | LL | use super::super::*; | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:225:13 + --> $DIR/wildcard_imports.rs:227:13 | LL | use super::super::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` diff --git a/tests/ui/wrong_self_convention.rs b/tests/ui/wrong_self_convention.rs index 99652ca4470..f44305d7e48 100644 --- a/tests/ui/wrong_self_convention.rs +++ b/tests/ui/wrong_self_convention.rs @@ -1,3 +1,4 @@ +// edition:2018 #![warn(clippy::wrong_self_convention)] #![warn(clippy::wrong_pub_self_convention)] #![allow(dead_code)] @@ -75,3 +76,15 @@ mod issue4293 { fn into_t3(self: Arc) {} } } + +// False positive for async (see #4037) +mod issue4037 { + pub struct Foo; + pub struct Bar; + + impl Foo { + pub async fn into_bar(self) -> Bar { + Bar + } + } +} diff --git a/tests/ui/wrong_self_convention.stderr b/tests/ui/wrong_self_convention.stderr index 0d0eb19cd07..ef3ad73ebc7 100644 --- a/tests/ui/wrong_self_convention.stderr +++ b/tests/ui/wrong_self_convention.stderr @@ -1,5 +1,5 @@ error: methods called `from_*` usually take no self; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:17:17 + --> $DIR/wrong_self_convention.rs:18:17 | LL | fn from_i32(self) {} | ^^^^ @@ -7,67 +7,67 @@ LL | fn from_i32(self) {} = note: `-D clippy::wrong-self-convention` implied by `-D warnings` error: methods called `from_*` usually take no self; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:23:21 + --> $DIR/wrong_self_convention.rs:24:21 | LL | pub fn from_i64(self) {} | ^^^^ error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:35:15 + --> $DIR/wrong_self_convention.rs:36:15 | LL | fn as_i32(self) {} | ^^^^ error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:37:17 + --> $DIR/wrong_self_convention.rs:38:17 | LL | fn into_i32(&self) {} | ^^^^^ error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:39:15 + --> $DIR/wrong_self_convention.rs:40:15 | LL | fn is_i32(self) {} | ^^^^ error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:41:15 + --> $DIR/wrong_self_convention.rs:42:15 | LL | fn to_i32(self) {} | ^^^^ error: methods called `from_*` usually take no self; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:43:17 + --> $DIR/wrong_self_convention.rs:44:17 | LL | fn from_i32(self) {} | ^^^^ error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:45:19 + --> $DIR/wrong_self_convention.rs:46:19 | LL | pub fn as_i64(self) {} | ^^^^ error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:46:21 + --> $DIR/wrong_self_convention.rs:47:21 | LL | pub fn into_i64(&self) {} | ^^^^^ error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:47:19 + --> $DIR/wrong_self_convention.rs:48:19 | LL | pub fn is_i64(self) {} | ^^^^ error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:48:19 + --> $DIR/wrong_self_convention.rs:49:19 | LL | pub fn to_i64(self) {} | ^^^^ error: methods called `from_*` usually take no self; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:49:21 + --> $DIR/wrong_self_convention.rs:50:21 | LL | pub fn from_i64(self) {} | ^^^^ From 7a66e6502dc3c7085b3f4078c01d4957d96175ed Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sat, 29 Aug 2020 01:18:42 +0200 Subject: [PATCH 09/49] or_fn_call: ignore nullary associated const fns --- clippy_lints/src/utils/mod.rs | 2 +- tests/ui/or_fun_call.fixed | 17 +++++++++++------ tests/ui/or_fun_call.rs | 17 +++++++++++------ tests/ui/or_fun_call.stderr | 20 ++++---------------- 4 files changed, 27 insertions(+), 29 deletions(-) diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 82005257115..fe2ee093157 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -899,7 +899,7 @@ pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_ return match res { def::Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true, // FIXME: check the constness of the arguments, see https://github.com/rust-lang/rust-clippy/pull/5682#issuecomment-638681210 - def::Res::Def(DefKind::Fn, def_id) if has_no_arguments(cx, def_id) => { + def::Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) if has_no_arguments(cx, def_id) => { const_eval::is_const_fn(cx.tcx, def_id) }, def::Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id), diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 67faa8bd4a0..5fb568672d3 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -58,12 +58,6 @@ fn or_fun_call() { let without_default = Some(Foo); without_default.unwrap_or_else(Foo::new); - let mut map = HashMap::::new(); - map.entry(42).or_insert_with(String::new); - - let mut btree = BTreeMap::::new(); - btree.entry(42).or_insert_with(String::new); - let stringy = Some(String::from("")); let _ = stringy.unwrap_or_else(|| "".to_owned()); @@ -122,6 +116,17 @@ pub fn skip_const_fn_with_no_args() { Some(42) } let _ = None.or(foo()); + + // See issue #5693. + let mut map = std::collections::HashMap::new(); + map.insert(1, vec![1]); + map.entry(1).or_insert(vec![]); + + let mut map = HashMap::::new(); + map.entry(42).or_insert(String::new()); + + let mut btree = BTreeMap::::new(); + btree.entry(42).or_insert(String::new()); } fn main() {} diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index 9867e2eedcf..737b0f7e55b 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -58,12 +58,6 @@ fn or_fun_call() { let without_default = Some(Foo); without_default.unwrap_or(Foo::new()); - let mut map = HashMap::::new(); - map.entry(42).or_insert(String::new()); - - let mut btree = BTreeMap::::new(); - btree.entry(42).or_insert(String::new()); - let stringy = Some(String::from("")); let _ = stringy.unwrap_or("".to_owned()); @@ -122,6 +116,17 @@ pub fn skip_const_fn_with_no_args() { Some(42) } let _ = None.or(foo()); + + // See issue #5693. + let mut map = std::collections::HashMap::new(); + map.insert(1, vec![1]); + map.entry(1).or_insert(vec![]); + + let mut map = HashMap::::new(); + map.entry(42).or_insert(String::new()); + + let mut btree = BTreeMap::::new(); + btree.entry(42).or_insert(String::new()); } fn main() {} diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index bc5978b538f..b8a436993f3 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -60,35 +60,23 @@ error: use of `unwrap_or` followed by a function call LL | without_default.unwrap_or(Foo::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(Foo::new)` -error: use of `or_insert` followed by a function call - --> $DIR/or_fun_call.rs:62:19 - | -LL | map.entry(42).or_insert(String::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)` - -error: use of `or_insert` followed by a function call - --> $DIR/or_fun_call.rs:65:21 - | -LL | btree.entry(42).or_insert(String::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)` - error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:68:21 + --> $DIR/or_fun_call.rs:62:21 | LL | let _ = stringy.unwrap_or("".to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| "".to_owned())` error: use of `or` followed by a function call - --> $DIR/or_fun_call.rs:93:35 + --> $DIR/or_fun_call.rs:87:35 | LL | let _ = Some("a".to_string()).or(Some("b".to_string())); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some("b".to_string()))` error: use of `or` followed by a function call - --> $DIR/or_fun_call.rs:97:10 + --> $DIR/or_fun_call.rs:91:10 | LL | .or(Some(Bar(b, Duration::from_secs(2)))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some(Bar(b, Duration::from_secs(2))))` -error: aborting due to 15 previous errors +error: aborting due to 13 previous errors From 5b7590f841974255f74c64d573189aecc7a30b2e Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sat, 29 Aug 2020 14:20:01 +0900 Subject: [PATCH 10/49] Downgrade applicability of `create_dir` --- clippy_lints/src/create_dir.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/create_dir.rs b/clippy_lints/src/create_dir.rs index bf47c1f84a2..1042eb45524 100644 --- a/clippy_lints/src/create_dir.rs +++ b/clippy_lints/src/create_dir.rs @@ -42,7 +42,7 @@ impl LateLintPass<'_> for CreateDir { "calling `std::fs::create_dir` where there may be a better way", "consider calling `std::fs::create_dir_all` instead", format!("std::fs::create_dir_all({})", snippet(cx, args[0].span, "..")), - Applicability::MachineApplicable, + Applicability::MaybeIncorrect, ) } } From 4972989b616cbf96c015cd9fdf1f4b4464ecaace Mon Sep 17 00:00:00 2001 From: Kyle Huey Date: Fri, 14 Aug 2020 17:30:48 -0700 Subject: [PATCH 11/49] Add a lint for an async block/closure that yields a type that is itself awaitable. This catches bugs of the form tokio::spawn(async move { let f = some_async_thing(); f // Oh no I forgot to await f so that work will never complete. }); --- CHANGELOG.md | 1 + clippy_lints/src/async_yields_async.rs | 88 +++++++++++++++++++++++ clippy_lints/src/lib.rs | 5 ++ src/lintlist/mod.rs | 7 ++ tests/ui/async_yields_async.fixed | 61 ++++++++++++++++ tests/ui/async_yields_async.rs | 61 ++++++++++++++++ tests/ui/async_yields_async.stderr | 96 ++++++++++++++++++++++++++ 7 files changed, 319 insertions(+) create mode 100644 clippy_lints/src/async_yields_async.rs create mode 100644 tests/ui/async_yields_async.fixed create mode 100644 tests/ui/async_yields_async.rs create mode 100644 tests/ui/async_yields_async.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 34d48821023..99a8b1a6293 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1512,6 +1512,7 @@ Released 2018-09-13 [`assertions_on_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_constants [`assign_op_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_op_pattern [`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops +[`async_yields_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#async_yields_async [`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock [`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask [`bind_instead_of_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#bind_instead_of_map diff --git a/clippy_lints/src/async_yields_async.rs b/clippy_lints/src/async_yields_async.rs new file mode 100644 index 00000000000..ae347fcd3e8 --- /dev/null +++ b/clippy_lints/src/async_yields_async.rs @@ -0,0 +1,88 @@ +use crate::utils::{implements_trait, snippet, span_lint_and_then}; +use rustc_errors::Applicability; +use rustc_hir::{AsyncGeneratorKind, Body, BodyId, ExprKind, GeneratorKind, QPath}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** + /// Checks for async blocks that yield values of types that can themselves + /// be awaited. + /// + /// **Why is this bad?** + /// An await is likely missing. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// async fn foo() {} + /// + /// fn bar() { + /// let x = async { + /// foo() + /// }; + /// } + /// ``` + /// Use instead: + /// ```rust + /// async fn foo() {} + /// + /// fn bar() { + /// let x = async { + /// foo().await + /// }; + /// } + /// ``` + pub ASYNC_YIELDS_ASYNC, + correctness, + "async blocks that return a type that can be awaited" +} + +declare_lint_pass!(AsyncYieldsAsync => [ASYNC_YIELDS_ASYNC]); + +impl<'tcx> LateLintPass<'tcx> for AsyncYieldsAsync { + fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) { + use AsyncGeneratorKind::{Block, Closure}; + // For functions, with explicitly defined types, don't warn. + // XXXkhuey maybe we should? + if let Some(GeneratorKind::Async(Block | Closure)) = body.generator_kind { + if let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait() { + let body_id = BodyId { + hir_id: body.value.hir_id, + }; + let def_id = cx.tcx.hir().body_owner_def_id(body_id); + let typeck_results = cx.tcx.typeck(def_id); + let expr_ty = typeck_results.expr_ty(&body.value); + + if implements_trait(cx, expr_ty, future_trait_def_id, &[]) { + let return_expr_span = match &body.value.kind { + // XXXkhuey there has to be a better way. + ExprKind::Block(block, _) => block.expr.map(|e| e.span), + ExprKind::Path(QPath::Resolved(_, path)) => Some(path.span), + _ => None, + }; + if let Some(return_expr_span) = return_expr_span { + span_lint_and_then( + cx, + ASYNC_YIELDS_ASYNC, + return_expr_span, + "an async construct yields a type which is itself awaitable", + |db| { + db.span_label(body.value.span, "outer async construct"); + db.span_label(return_expr_span, "awaitable value not awaited"); + db.span_suggestion( + return_expr_span, + "consider awaiting this value", + format!("{}.await", snippet(cx, return_expr_span, "..")), + Applicability::MaybeIncorrect, + ); + }, + ); + } + } + } + } + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 577ce6523b4..0eb1d331366 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -154,6 +154,7 @@ mod arithmetic; mod as_conversions; mod assertions_on_constants; mod assign_ops; +mod async_yields_async; mod atomic_ordering; mod attrs; mod await_holding_lock; @@ -483,6 +484,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &assertions_on_constants::ASSERTIONS_ON_CONSTANTS, &assign_ops::ASSIGN_OP_PATTERN, &assign_ops::MISREFACTORED_ASSIGN_OP, + &async_yields_async::ASYNC_YIELDS_ASYNC, &atomic_ordering::INVALID_ATOMIC_ORDERING, &attrs::BLANKET_CLIPPY_RESTRICTION_LINTS, &attrs::DEPRECATED_CFG_ATTR, @@ -1099,6 +1101,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box unwrap_in_result::UnwrapInResult); store.register_late_pass(|| box self_assignment::SelfAssignment); store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs); + store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1232,6 +1235,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&assertions_on_constants::ASSERTIONS_ON_CONSTANTS), LintId::of(&assign_ops::ASSIGN_OP_PATTERN), LintId::of(&assign_ops::MISREFACTORED_ASSIGN_OP), + LintId::of(&async_yields_async::ASYNC_YIELDS_ASYNC), LintId::of(&atomic_ordering::INVALID_ATOMIC_ORDERING), LintId::of(&attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), LintId::of(&attrs::DEPRECATED_CFG_ATTR), @@ -1675,6 +1679,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_group(true, "clippy::correctness", Some("clippy_correctness"), vec![ LintId::of(&approx_const::APPROX_CONSTANT), + LintId::of(&async_yields_async::ASYNC_YIELDS_ASYNC), LintId::of(&atomic_ordering::INVALID_ATOMIC_ORDERING), LintId::of(&attrs::DEPRECATED_SEMVER), LintId::of(&attrs::MISMATCHED_TARGET_OS), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 687fac7baa8..dff19ef440f 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -52,6 +52,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "assign_ops", }, + Lint { + name: "async_yields_async", + group: "correctness", + desc: "async blocks that return a type that can be awaited", + deprecation: None, + module: "async_yields_async", + }, Lint { name: "await_holding_lock", group: "pedantic", diff --git a/tests/ui/async_yields_async.fixed b/tests/ui/async_yields_async.fixed new file mode 100644 index 00000000000..cadc6494c76 --- /dev/null +++ b/tests/ui/async_yields_async.fixed @@ -0,0 +1,61 @@ +// run-rustfix +// edition:2018 + +#![feature(async_closure)] +#![warn(clippy::async_yields_async)] + +use core::future::Future; +use core::pin::Pin; +use core::task::{Context, Poll}; + +struct CustomFutureType; + +impl Future for CustomFutureType { + type Output = u8; + + fn poll(self: Pin<&mut Self>, _: &mut Context) -> Poll { + Poll::Ready(3) + } +} + +fn custom_future_type_ctor() -> CustomFutureType { + CustomFutureType +} + +#[rustfmt::skip] +fn main() { + let _f = { + 3 + }; + let _g = async { + 3 + }; + let _h = async { + async { + 3 + }.await + }; + let _i = async { + CustomFutureType.await + }; + let _i = async || { + 3 + }; + let _j = async || { + async { + 3 + }.await + }; + let _k = async || { + CustomFutureType.await + }; + let _l = async || CustomFutureType.await; + let _m = async || { + println!("I'm bored"); + // Some more stuff + + // Finally something to await + CustomFutureType.await + }; + let _n = async || custom_future_type_ctor(); +} diff --git a/tests/ui/async_yields_async.rs b/tests/ui/async_yields_async.rs new file mode 100644 index 00000000000..898fe1a9561 --- /dev/null +++ b/tests/ui/async_yields_async.rs @@ -0,0 +1,61 @@ +// run-rustfix +// edition:2018 + +#![feature(async_closure)] +#![warn(clippy::async_yields_async)] + +use core::future::Future; +use core::pin::Pin; +use core::task::{Context, Poll}; + +struct CustomFutureType; + +impl Future for CustomFutureType { + type Output = u8; + + fn poll(self: Pin<&mut Self>, _: &mut Context) -> Poll { + Poll::Ready(3) + } +} + +fn custom_future_type_ctor() -> CustomFutureType { + CustomFutureType +} + +#[rustfmt::skip] +fn main() { + let _f = { + 3 + }; + let _g = async { + 3 + }; + let _h = async { + async { + 3 + } + }; + let _i = async { + CustomFutureType + }; + let _i = async || { + 3 + }; + let _j = async || { + async { + 3 + } + }; + let _k = async || { + CustomFutureType + }; + let _l = async || CustomFutureType; + let _m = async || { + println!("I'm bored"); + // Some more stuff + + // Finally something to await + CustomFutureType + }; + let _n = async || custom_future_type_ctor(); +} diff --git a/tests/ui/async_yields_async.stderr b/tests/ui/async_yields_async.stderr new file mode 100644 index 00000000000..112984cdccb --- /dev/null +++ b/tests/ui/async_yields_async.stderr @@ -0,0 +1,96 @@ +error: an async construct yields a type which is itself awaitable + --> $DIR/async_yields_async.rs:34:9 + | +LL | let _h = async { + | ____________________- +LL | | async { + | |_________^ +LL | || 3 +LL | || } + | ||_________^ awaitable value not awaited +LL | | }; + | |_____- outer async construct + | + = note: `-D clippy::async-yields-async` implied by `-D warnings` +help: consider awaiting this value + | +LL | async { +LL | 3 +LL | }.await + | + +error: an async construct yields a type which is itself awaitable + --> $DIR/async_yields_async.rs:39:9 + | +LL | let _i = async { + | ____________________- +LL | | CustomFutureType + | | ^^^^^^^^^^^^^^^^ + | | | + | | awaitable value not awaited + | | help: consider awaiting this value: `CustomFutureType.await` +LL | | }; + | |_____- outer async construct + +error: an async construct yields a type which is itself awaitable + --> $DIR/async_yields_async.rs:45:9 + | +LL | let _j = async || { + | _______________________- +LL | | async { + | |_________^ +LL | || 3 +LL | || } + | ||_________^ awaitable value not awaited +LL | | }; + | |_____- outer async construct + | +help: consider awaiting this value + | +LL | async { +LL | 3 +LL | }.await + | + +error: an async construct yields a type which is itself awaitable + --> $DIR/async_yields_async.rs:50:9 + | +LL | let _k = async || { + | _______________________- +LL | | CustomFutureType + | | ^^^^^^^^^^^^^^^^ + | | | + | | awaitable value not awaited + | | help: consider awaiting this value: `CustomFutureType.await` +LL | | }; + | |_____- outer async construct + +error: an async construct yields a type which is itself awaitable + --> $DIR/async_yields_async.rs:52:23 + | +LL | let _l = async || CustomFutureType; + | ^^^^^^^^^^^^^^^^ + | | + | outer async construct + | awaitable value not awaited + | help: consider awaiting this value: `CustomFutureType.await` + +error: an async construct yields a type which is itself awaitable + --> $DIR/async_yields_async.rs:58:9 + | +LL | let _m = async || { + | _______________________- +LL | | println!("I'm bored"); +LL | | // Some more stuff +LL | | +LL | | // Finally something to await +LL | | CustomFutureType + | | ^^^^^^^^^^^^^^^^ + | | | + | | awaitable value not awaited + | | help: consider awaiting this value: `CustomFutureType.await` +LL | | }; + | |_____- outer async construct + +error: aborting due to 6 previous errors + From c1d2b9376a6bb4fc06f845e12b9c2a93079bb2ee Mon Sep 17 00:00:00 2001 From: Kyle Huey Date: Sat, 22 Aug 2020 21:36:39 -0700 Subject: [PATCH 12/49] Add a test for an async function. --- tests/ui/async_yields_async.fixed | 7 +++++++ tests/ui/async_yields_async.rs | 7 +++++++ tests/ui/async_yields_async.stderr | 12 ++++++------ 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/tests/ui/async_yields_async.fixed b/tests/ui/async_yields_async.fixed index cadc6494c76..9b1a7ac3ba9 100644 --- a/tests/ui/async_yields_async.fixed +++ b/tests/ui/async_yields_async.fixed @@ -22,6 +22,12 @@ fn custom_future_type_ctor() -> CustomFutureType { CustomFutureType } +async fn f() -> CustomFutureType { + // Don't warn for functions since you have to explicitly declare their + // return types. + CustomFutureType +} + #[rustfmt::skip] fn main() { let _f = { @@ -58,4 +64,5 @@ fn main() { CustomFutureType.await }; let _n = async || custom_future_type_ctor(); + let _o = async || f(); } diff --git a/tests/ui/async_yields_async.rs b/tests/ui/async_yields_async.rs index 898fe1a9561..731c094edb4 100644 --- a/tests/ui/async_yields_async.rs +++ b/tests/ui/async_yields_async.rs @@ -22,6 +22,12 @@ fn custom_future_type_ctor() -> CustomFutureType { CustomFutureType } +async fn f() -> CustomFutureType { + // Don't warn for functions since you have to explicitly declare their + // return types. + CustomFutureType +} + #[rustfmt::skip] fn main() { let _f = { @@ -58,4 +64,5 @@ fn main() { CustomFutureType }; let _n = async || custom_future_type_ctor(); + let _o = async || f(); } diff --git a/tests/ui/async_yields_async.stderr b/tests/ui/async_yields_async.stderr index 112984cdccb..17d0c375106 100644 --- a/tests/ui/async_yields_async.stderr +++ b/tests/ui/async_yields_async.stderr @@ -1,5 +1,5 @@ error: an async construct yields a type which is itself awaitable - --> $DIR/async_yields_async.rs:34:9 + --> $DIR/async_yields_async.rs:40:9 | LL | let _h = async { | ____________________- @@ -20,7 +20,7 @@ LL | }.await | error: an async construct yields a type which is itself awaitable - --> $DIR/async_yields_async.rs:39:9 + --> $DIR/async_yields_async.rs:45:9 | LL | let _i = async { | ____________________- @@ -33,7 +33,7 @@ LL | | }; | |_____- outer async construct error: an async construct yields a type which is itself awaitable - --> $DIR/async_yields_async.rs:45:9 + --> $DIR/async_yields_async.rs:51:9 | LL | let _j = async || { | _______________________- @@ -53,7 +53,7 @@ LL | }.await | error: an async construct yields a type which is itself awaitable - --> $DIR/async_yields_async.rs:50:9 + --> $DIR/async_yields_async.rs:56:9 | LL | let _k = async || { | _______________________- @@ -66,7 +66,7 @@ LL | | }; | |_____- outer async construct error: an async construct yields a type which is itself awaitable - --> $DIR/async_yields_async.rs:52:23 + --> $DIR/async_yields_async.rs:58:23 | LL | let _l = async || CustomFutureType; | ^^^^^^^^^^^^^^^^ @@ -76,7 +76,7 @@ LL | let _l = async || CustomFutureType; | help: consider awaiting this value: `CustomFutureType.await` error: an async construct yields a type which is itself awaitable - --> $DIR/async_yields_async.rs:58:9 + --> $DIR/async_yields_async.rs:64:9 | LL | let _m = async || { | _______________________- From 04912ca115ff153a97d80b604435b10dcb155dd0 Mon Sep 17 00:00:00 2001 From: Kyle Huey Date: Sat, 22 Aug 2020 21:40:01 -0700 Subject: [PATCH 13/49] Formatting changes requested by ThibsG. --- clippy_lints/src/async_yields_async.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/async_yields_async.rs b/clippy_lints/src/async_yields_async.rs index ae347fcd3e8..88d9d3b5a26 100644 --- a/clippy_lints/src/async_yields_async.rs +++ b/clippy_lints/src/async_yields_async.rs @@ -5,12 +5,10 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { - /// **What it does:** - /// Checks for async blocks that yield values of types that can themselves - /// be awaited. + /// **What it does:** Checks for async blocks that yield values of types + /// that can themselves be awaited. /// - /// **Why is this bad?** - /// An await is likely missing. + /// **Why is this bad?** An await is likely missing. /// /// **Known problems:** None. /// From 1c5b0fbe53f842cd5871ea02e4e48571615d5679 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Fri, 28 Aug 2020 13:26:25 -0700 Subject: [PATCH 14/49] Update dataflow analyses to use new interface --- clippy_lints/src/redundant_clone.rs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index 7932be0d4b1..aec99cb4063 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -14,7 +14,6 @@ use rustc_middle::mir::{ visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor as _}, }; use rustc_middle::ty::{self, fold::TypeVisitor, Ty}; -use rustc_mir::dataflow::BottomValue; use rustc_mir::dataflow::{Analysis, AnalysisDomain, GenKill, GenKillAnalysis, ResultsCursor}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::{BytePos, Span}; @@ -411,14 +410,15 @@ impl<'tcx> mir::visit::Visitor<'tcx> for LocalUseVisitor { struct MaybeStorageLive; impl<'tcx> AnalysisDomain<'tcx> for MaybeStorageLive { - type Idx = mir::Local; + type Domain = BitSet; const NAME: &'static str = "maybe_storage_live"; - fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize { - body.local_decls.len() + fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { + // bottom = dead + BitSet::new_empty(body.local_decls.len()) } - fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut BitSet) { + fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain) { for arg in body.args_iter() { state.insert(arg); } @@ -426,6 +426,8 @@ impl<'tcx> AnalysisDomain<'tcx> for MaybeStorageLive { } impl<'tcx> GenKillAnalysis<'tcx> for MaybeStorageLive { + type Idx = mir::Local; + fn statement_effect(&self, trans: &mut impl GenKill, stmt: &mir::Statement<'tcx>, _: mir::Location) { match stmt.kind { mir::StatementKind::StorageLive(l) => trans.gen(l), @@ -454,11 +456,6 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeStorageLive { } } -impl BottomValue for MaybeStorageLive { - /// bottom = dead - const BOTTOM_VALUE: bool = false; -} - /// Collects the possible borrowers of each local. /// For example, `b = &a; c = &a;` will make `b` and (transitively) `c` /// possible borrowers of `a`. From 17b2ba5ded12f59dba63ece659b5cd714b763800 Mon Sep 17 00:00:00 2001 From: Camelid <37223377+camelid@users.noreply.github.com> Date: Sun, 30 Aug 2020 11:24:15 -0700 Subject: [PATCH 15/49] Syntax-highlight `single_char_push_str` lint --- clippy_lints/src/methods/mod.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 9996df69470..7c4a78cbdcd 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1324,20 +1324,20 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Warns when using push_str with a single-character string literal, - /// and push with a char would work fine. + /// **What it does:** Warns when using `push_str` with a single-character string literal, + /// and `push` with a `char` would work fine. /// - /// **Why is this bad?** It's less clear that we are pushing a single character + /// **Why is this bad?** It's less clear that we are pushing a single character. /// /// **Known problems:** None /// /// **Example:** - /// ``` + /// ```rust /// let mut string = String::new(); /// string.push_str("R"); /// ``` /// Could be written as - /// ``` + /// ```rust /// let mut string = String::new(); /// string.push('R'); /// ``` From fdc48fb90c04b26b03ee7d8ae85e3175e5fc538c Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Sun, 30 Aug 2020 19:17:17 -0400 Subject: [PATCH 16/49] Fix clippy --- clippy_lints/src/utils/ast_utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/utils/ast_utils.rs b/clippy_lints/src/utils/ast_utils.rs index 7b419431c0f..3c3f8b26e3a 100644 --- a/clippy_lints/src/utils/ast_utils.rs +++ b/clippy_lints/src/utils/ast_utils.rs @@ -191,7 +191,7 @@ pub fn eq_stmt(l: &Stmt, r: &Stmt) -> bool { (Item(l), Item(r)) => eq_item(l, r, eq_item_kind), (Expr(l), Expr(r)) | (Semi(l), Semi(r)) => eq_expr(l, r), (Empty, Empty) => true, - (MacCall(l), MacCall(r)) => l.1 == r.1 && eq_mac_call(&l.0, &r.0) && over(&l.2, &r.2, |l, r| eq_attr(l, r)), + (MacCall(l), MacCall(r)) => l.style == r.style && eq_mac_call(&l.mac, &r.mac) && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r)), _ => false, } } From 451ef7880392f3f06088ff7a7b957e3485f4bc6c Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Mon, 31 Aug 2020 22:40:47 +0900 Subject: [PATCH 17/49] Use `match_def_path` instead of `match_qpath` --- clippy_lints/src/create_dir.rs | 5 +++-- tests/ui/create_dir.fixed | 6 ++++-- tests/ui/create_dir.rs | 6 ++++-- tests/ui/create_dir.stderr | 4 ++-- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/create_dir.rs b/clippy_lints/src/create_dir.rs index 1042eb45524..f80a0efa7a5 100644 --- a/clippy_lints/src/create_dir.rs +++ b/clippy_lints/src/create_dir.rs @@ -1,4 +1,4 @@ -use crate::utils::{match_qpath, paths, snippet, span_lint_and_sugg}; +use crate::utils::{match_def_path, paths, snippet, span_lint_and_sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -33,7 +33,8 @@ impl LateLintPass<'_> for CreateDir { if_chain! { if let ExprKind::Call(ref func, ref args) = expr.kind; if let ExprKind::Path(ref path) = func.kind; - if match_qpath(path, &paths::STD_FS_CREATE_DIR); + if let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id(); + if match_def_path(cx, def_id, &paths::STD_FS_CREATE_DIR); then { span_lint_and_sugg( cx, diff --git a/tests/ui/create_dir.fixed b/tests/ui/create_dir.fixed index 50f31f0c9c5..0e28f87e33d 100644 --- a/tests/ui/create_dir.fixed +++ b/tests/ui/create_dir.fixed @@ -2,12 +2,14 @@ #![allow(unused_must_use)] #![warn(clippy::create_dir)] -fn not_create_dir() {} +fn create_dir() {} fn main() { + // Should be warned std::fs::create_dir_all("foo"); std::fs::create_dir_all("bar").unwrap(); - not_create_dir(); + // Shouldn't be warned + create_dir(); std::fs::create_dir_all("foobar"); } diff --git a/tests/ui/create_dir.rs b/tests/ui/create_dir.rs index 00ef37f413f..1f226298c0d 100644 --- a/tests/ui/create_dir.rs +++ b/tests/ui/create_dir.rs @@ -2,12 +2,14 @@ #![allow(unused_must_use)] #![warn(clippy::create_dir)] -fn not_create_dir() {} +fn create_dir() {} fn main() { + // Should be warned std::fs::create_dir("foo"); std::fs::create_dir("bar").unwrap(); - not_create_dir(); + // Shouldn't be warned + create_dir(); std::fs::create_dir_all("foobar"); } diff --git a/tests/ui/create_dir.stderr b/tests/ui/create_dir.stderr index 3ae4680d929..0c97bdd0f7a 100644 --- a/tests/ui/create_dir.stderr +++ b/tests/ui/create_dir.stderr @@ -1,5 +1,5 @@ error: calling `std::fs::create_dir` where there may be a better way - --> $DIR/create_dir.rs:8:5 + --> $DIR/create_dir.rs:9:5 | LL | std::fs::create_dir("foo"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `std::fs::create_dir_all` instead: `std::fs::create_dir_all("foo")` @@ -7,7 +7,7 @@ LL | std::fs::create_dir("foo"); = note: `-D clippy::create-dir` implied by `-D warnings` error: calling `std::fs::create_dir` where there may be a better way - --> $DIR/create_dir.rs:9:5 + --> $DIR/create_dir.rs:10:5 | LL | std::fs::create_dir("bar").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `std::fs::create_dir_all` instead: `std::fs::create_dir_all("bar")` From 001f9e45f24c5617d816e7d9dfbca4dc1a694dd9 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 1 Sep 2020 00:05:53 +0900 Subject: [PATCH 18/49] Fix the wrong suggestion when using macro in `collapsible_if` --- clippy_lints/src/utils/sugg.rs | 6 +++++- tests/ui/collapsible_if.fixed | 3 +++ tests/ui/collapsible_if.rs | 5 +++++ tests/ui/collapsible_if.stderr | 10 +++++++++- 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/utils/sugg.rs b/clippy_lints/src/utils/sugg.rs index 2955f8d8e59..811fde388d1 100644 --- a/clippy_lints/src/utils/sugg.rs +++ b/clippy_lints/src/utils/sugg.rs @@ -132,7 +132,11 @@ impl<'a> Sugg<'a> { pub fn ast(cx: &EarlyContext<'_>, expr: &ast::Expr, default: &'a str) -> Self { use rustc_ast::ast::RangeLimits; - let snippet = snippet(cx, expr.span, default); + let snippet = if expr.span.from_expansion() { + snippet_with_macro_callsite(cx, expr.span, default) + } else { + snippet(cx, expr.span, default) + }; match expr.kind { ast::ExprKind::AddrOf(..) diff --git a/tests/ui/collapsible_if.fixed b/tests/ui/collapsible_if.fixed index 561283fc8e7..efd4187947b 100644 --- a/tests/ui/collapsible_if.fixed +++ b/tests/ui/collapsible_if.fixed @@ -135,4 +135,7 @@ fn main() { if truth() {} } } + + // Fix #5962 + if matches!(true, true) && matches!(true, true) {} } diff --git a/tests/ui/collapsible_if.rs b/tests/ui/collapsible_if.rs index dc9d9b451c0..657f32d38a3 100644 --- a/tests/ui/collapsible_if.rs +++ b/tests/ui/collapsible_if.rs @@ -149,4 +149,9 @@ fn main() { if truth() {} } } + + // Fix #5962 + if matches!(true, true) { + if matches!(true, true) {} + } } diff --git a/tests/ui/collapsible_if.stderr b/tests/ui/collapsible_if.stderr index f56dd65b9dd..acd1ec3f2ca 100644 --- a/tests/ui/collapsible_if.stderr +++ b/tests/ui/collapsible_if.stderr @@ -118,5 +118,13 @@ LL | println!("Hello world!"); LL | } | -error: aborting due to 7 previous errors +error: this `if` statement can be collapsed + --> $DIR/collapsible_if.rs:154:5 + | +LL | / if matches!(true, true) { +LL | | if matches!(true, true) {} +LL | | } + | |_____^ help: collapse nested if block: `if matches!(true, true) && matches!(true, true) {}` + +error: aborting due to 8 previous errors From 8b0aa6a00b19b8e47a72157ec8e8f9e9060cb2fb Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Tue, 1 Sep 2020 00:31:53 +0900 Subject: [PATCH 19/49] default_trait_access: Fix wrong suggestion --- clippy_lints/src/default_trait_access.rs | 4 +- tests/ui/default_trait_access.fixed | 106 +++++++++++++++++++++++ tests/ui/default_trait_access.rs | 5 +- tests/ui/default_trait_access.stderr | 26 +++--- 4 files changed, 127 insertions(+), 14 deletions(-) create mode 100644 tests/ui/default_trait_access.fixed diff --git a/clippy_lints/src/default_trait_access.rs b/clippy_lints/src/default_trait_access.rs index 067ea903bdd..0b0a1307876 100644 --- a/clippy_lints/src/default_trait_access.rs +++ b/clippy_lints/src/default_trait_access.rs @@ -55,8 +55,8 @@ impl<'tcx> LateLintPass<'tcx> for DefaultTraitAccess { // TODO: Work out a way to put "whatever the imported way of referencing // this type in this file" rather than a fully-qualified type. let expr_ty = cx.typeck_results().expr_ty(expr); - if let ty::Adt(..) = expr_ty.kind { - let replacement = format!("{}::default()", expr_ty); + if let ty::Adt(def, ..) = expr_ty.kind { + let replacement = format!("{}::default()", cx.tcx.def_path_str(def.did)); span_lint_and_sugg( cx, DEFAULT_TRAIT_ACCESS, diff --git a/tests/ui/default_trait_access.fixed b/tests/ui/default_trait_access.fixed new file mode 100644 index 00000000000..d05567a3f82 --- /dev/null +++ b/tests/ui/default_trait_access.fixed @@ -0,0 +1,106 @@ +// run-rustfix + +#![allow(unused_imports)] +#![deny(clippy::default_trait_access)] + +use std::default; +use std::default::Default as D2; +use std::string; + +fn main() { + let s1: String = std::string::String::default(); + + let s2 = String::default(); + + let s3: String = std::string::String::default(); + + let s4: String = std::string::String::default(); + + let s5 = string::String::default(); + + let s6: String = std::string::String::default(); + + let s7 = std::string::String::default(); + + let s8: String = DefaultFactory::make_t_badly(); + + let s9: String = DefaultFactory::make_t_nicely(); + + let s10 = DerivedDefault::default(); + + let s11: GenericDerivedDefault = GenericDerivedDefault::default(); + + let s12 = GenericDerivedDefault::::default(); + + let s13 = TupleDerivedDefault::default(); + + let s14: TupleDerivedDefault = TupleDerivedDefault::default(); + + let s15: ArrayDerivedDefault = ArrayDerivedDefault::default(); + + let s16 = ArrayDerivedDefault::default(); + + let s17: TupleStructDerivedDefault = TupleStructDerivedDefault::default(); + + let s18 = TupleStructDerivedDefault::default(); + + let s19 = ::default(); + + println!( + "[{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}], [{:?}]", + s1, + s2, + s3, + s4, + s5, + s6, + s7, + s8, + s9, + s10, + s11, + s12, + s13, + s14, + s15, + s16, + s17, + s18, + s19, + ); +} + +struct DefaultFactory; + +impl DefaultFactory { + pub fn make_t_badly() -> T { + Default::default() + } + + pub fn make_t_nicely() -> T { + T::default() + } +} + +#[derive(Debug, Default)] +struct DerivedDefault { + pub s: String, +} + +#[derive(Debug, Default)] +struct GenericDerivedDefault { + pub s: T, +} + +#[derive(Debug, Default)] +struct TupleDerivedDefault { + pub s: (String, String), +} + +#[derive(Debug, Default)] +struct ArrayDerivedDefault { + pub s: [String; 10], +} + +#[derive(Debug, Default)] +struct TupleStructDerivedDefault(String); diff --git a/tests/ui/default_trait_access.rs b/tests/ui/default_trait_access.rs index 2f1490a7036..447e70c0bbb 100644 --- a/tests/ui/default_trait_access.rs +++ b/tests/ui/default_trait_access.rs @@ -1,4 +1,7 @@ -#![warn(clippy::default_trait_access)] +// run-rustfix + +#![allow(unused_imports)] +#![deny(clippy::default_trait_access)] use std::default; use std::default::Default as D2; diff --git a/tests/ui/default_trait_access.stderr b/tests/ui/default_trait_access.stderr index 26b2057548b..df8a5b94ddc 100644 --- a/tests/ui/default_trait_access.stderr +++ b/tests/ui/default_trait_access.stderr @@ -1,49 +1,53 @@ error: calling `std::string::String::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:8:22 + --> $DIR/default_trait_access.rs:11:22 | LL | let s1: String = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()` | - = note: `-D clippy::default-trait-access` implied by `-D warnings` +note: the lint level is defined here + --> $DIR/default_trait_access.rs:4:9 + | +LL | #![deny(clippy::default_trait_access)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: calling `std::string::String::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:12:22 + --> $DIR/default_trait_access.rs:15:22 | LL | let s3: String = D2::default(); | ^^^^^^^^^^^^^ help: try: `std::string::String::default()` error: calling `std::string::String::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:14:22 + --> $DIR/default_trait_access.rs:17:22 | LL | let s4: String = std::default::Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()` error: calling `std::string::String::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:18:22 + --> $DIR/default_trait_access.rs:21:22 | LL | let s6: String = default::Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()` -error: calling `GenericDerivedDefault::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:28:46 +error: calling `GenericDerivedDefault::default()` is more clear than this expression + --> $DIR/default_trait_access.rs:31:46 | LL | let s11: GenericDerivedDefault = Default::default(); - | ^^^^^^^^^^^^^^^^^^ help: try: `GenericDerivedDefault::default()` + | ^^^^^^^^^^^^^^^^^^ help: try: `GenericDerivedDefault::default()` error: calling `TupleDerivedDefault::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:34:36 + --> $DIR/default_trait_access.rs:37:36 | LL | let s14: TupleDerivedDefault = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `TupleDerivedDefault::default()` error: calling `ArrayDerivedDefault::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:36:36 + --> $DIR/default_trait_access.rs:39:36 | LL | let s15: ArrayDerivedDefault = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `ArrayDerivedDefault::default()` error: calling `TupleStructDerivedDefault::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:40:42 + --> $DIR/default_trait_access.rs:43:42 | LL | let s17: TupleStructDerivedDefault = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `TupleStructDerivedDefault::default()` From afeb917fca7eaa5b44acd29a60f687024c0ebeac Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Tue, 1 Sep 2020 11:21:48 +1200 Subject: [PATCH 20/49] Fix a fp in `transmute_ptr_to_ptr` Avoid firing the lint when `transmute` in const contexts as dereferencing raw pointers in consts is unstable. cc #5959 --- clippy_lints/src/transmute.rs | 6 ++++-- tests/ui/transmute_ptr_to_ptr.rs | 8 ++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/transmute.rs b/clippy_lints/src/transmute.rs index 50d9c93f9d4..789d124eae2 100644 --- a/clippy_lints/src/transmute.rs +++ b/clippy_lints/src/transmute.rs @@ -331,8 +331,9 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id(); if match_def_path(cx, def_id, &paths::TRANSMUTE); then { - // Avoid suggesting from/to bits in const contexts. + // Avoid suggesting from/to bits and dereferencing raw pointers in const contexts. // See https://github.com/rust-lang/rust/issues/73736 for progress on making them `const fn`. + // And see https://github.com/rust-lang/rust/issues/51911 for dereferencing raw pointers. let const_context = in_constant(cx, e.hir_id); let from_ty = cx.typeck_results().expr_ty(&args[0]); @@ -486,7 +487,8 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { Applicability::Unspecified, ); } else { - if cx.tcx.erase_regions(&from_ty) != cx.tcx.erase_regions(&to_ty) { + if (cx.tcx.erase_regions(&from_ty) != cx.tcx.erase_regions(&to_ty)) + && !const_context { span_lint_and_then( cx, TRANSMUTE_PTR_TO_PTR, diff --git a/tests/ui/transmute_ptr_to_ptr.rs b/tests/ui/transmute_ptr_to_ptr.rs index 0d8a322f2b2..26b03bdc740 100644 --- a/tests/ui/transmute_ptr_to_ptr.rs +++ b/tests/ui/transmute_ptr_to_ptr.rs @@ -51,4 +51,12 @@ fn transmute_ptr_to_ptr() { let _: &GenericParam<&LifetimeParam<'static>> = unsafe { std::mem::transmute(&GenericParam { t: &lp }) }; } +// dereferencing raw pointers in const contexts, should not lint as it's unstable (issue 5959) +const _: &() = { + struct ZST; + let zst = &ZST; + + unsafe { std::mem::transmute::<&'static ZST, &'static ()>(zst) } +}; + fn main() {} From 2e4b4cebbbb37efa5dc69dd2616f3b7a288b92aa Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Tue, 1 Sep 2020 12:09:32 +0900 Subject: [PATCH 21/49] useless_attribute: Permit wildcard_imports and enum_glob_use --- clippy_lints/src/attrs.rs | 38 ++++++++++++++++++------------- tests/ui/useless_attribute.fixed | 8 +++++++ tests/ui/useless_attribute.rs | 8 +++++++ tests/ui/useless_attribute.stderr | 2 +- 4 files changed, 39 insertions(+), 17 deletions(-) diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index cfcc1b3c5f3..c8f153e7201 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -71,8 +71,9 @@ declare_clippy_lint! { /// **What it does:** Checks for `extern crate` and `use` items annotated with /// lint attributes. /// - /// This lint permits `#[allow(unused_imports)]`, `#[allow(deprecated)]` and - /// `#[allow(unreachable_pub)]` on `use` items and `#[allow(unused_imports)]` on + /// This lint permits `#[allow(unused_imports)]`, `#[allow(deprecated)]`, + /// `#[allow(unreachable_pub)]`, `#[allow(clippy::wildcard_imports)]` and + /// `#[allow(clippy::enum_glob_use)]` on `use` items and `#[allow(unused_imports)]` on /// `extern crate` items with a `#[macro_use]` attribute. /// /// **Why is this bad?** Lint attributes have no effect on crate imports. Most @@ -318,7 +319,8 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { if let Some(ident) = attr.ident() { match &*ident.as_str() { "allow" | "warn" | "deny" | "forbid" => { - // permit `unused_imports`, `deprecated` and `unreachable_pub` for `use` items + // permit `unused_imports`, `deprecated`, `unreachable_pub`, + // `clippy::wildcard_imports`, and `clippy::enum_glob_use` for `use` items // and `unused_imports` for `extern crate` items with `macro_use` for lint in lint_list { match item.kind { @@ -327,6 +329,9 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { || is_word(lint, sym!(deprecated)) || is_word(lint, sym!(unreachable_pub)) || is_word(lint, sym!(unused)) + || extract_clippy_lint(lint) + .map_or(false, |s| s == "wildcard_imports") + || extract_clippy_lint(lint).map_or(false, |s| s == "enum_glob_use") { return; } @@ -387,24 +392,25 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { } } -fn check_clippy_lint_names(cx: &LateContext<'_>, ident: &str, items: &[NestedMetaItem]) { - fn extract_name(lint: &NestedMetaItem) -> Option { - if_chain! { - if let Some(meta_item) = lint.meta_item(); - if meta_item.path.segments.len() > 1; - if let tool_name = meta_item.path.segments[0].ident; - if tool_name.as_str() == "clippy"; - let lint_name = meta_item.path.segments.last().unwrap().ident.name; - then { - return Some(lint_name.as_str()); - } +/// Returns the lint name if it is clippy lint. +fn extract_clippy_lint(lint: &NestedMetaItem) -> Option { + if_chain! { + if let Some(meta_item) = lint.meta_item(); + if meta_item.path.segments.len() > 1; + if let tool_name = meta_item.path.segments[0].ident; + if tool_name.as_str() == "clippy"; + let lint_name = meta_item.path.segments.last().unwrap().ident.name; + then { + return Some(lint_name.as_str()); } - None } + None +} +fn check_clippy_lint_names(cx: &LateContext<'_>, ident: &str, items: &[NestedMetaItem]) { let lint_store = cx.lints(); for lint in items { - if let Some(lint_name) = extract_name(lint) { + if let Some(lint_name) = extract_clippy_lint(lint) { if let CheckLintNameResult::Tool(Err((None, _))) = lint_store.check_lint_name(&lint_name, Some(sym!(clippy))) { diff --git a/tests/ui/useless_attribute.fixed b/tests/ui/useless_attribute.fixed index b222e2f7976..a5fcde768f1 100644 --- a/tests/ui/useless_attribute.fixed +++ b/tests/ui/useless_attribute.fixed @@ -49,6 +49,14 @@ mod a { pub use self::b::C; } +// don't lint on clippy::wildcard_imports for `use` items +#[allow(clippy::wildcard_imports)] +pub use std::io::prelude::*; + +// don't lint on clippy::enum_glob_use for `use` items +#[allow(clippy::enum_glob_use)] +pub use std::cmp::Ordering::*; + fn test_indented_attr() { #![allow(clippy::almost_swapped)] use std::collections::HashSet; diff --git a/tests/ui/useless_attribute.rs b/tests/ui/useless_attribute.rs index 3422eace4ab..0396d39e3d5 100644 --- a/tests/ui/useless_attribute.rs +++ b/tests/ui/useless_attribute.rs @@ -49,6 +49,14 @@ mod a { pub use self::b::C; } +// don't lint on clippy::wildcard_imports for `use` items +#[allow(clippy::wildcard_imports)] +pub use std::io::prelude::*; + +// don't lint on clippy::enum_glob_use for `use` items +#[allow(clippy::enum_glob_use)] +pub use std::cmp::Ordering::*; + fn test_indented_attr() { #[allow(clippy::almost_swapped)] use std::collections::HashSet; diff --git a/tests/ui/useless_attribute.stderr b/tests/ui/useless_attribute.stderr index 57ba976730c..d0194e4bbbe 100644 --- a/tests/ui/useless_attribute.stderr +++ b/tests/ui/useless_attribute.stderr @@ -13,7 +13,7 @@ LL | #[cfg_attr(feature = "cargo-clippy", allow(dead_code))] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![cfg_attr(feature = "cargo-clippy", allow(dead_code)` error: useless lint attribute - --> $DIR/useless_attribute.rs:53:5 + --> $DIR/useless_attribute.rs:61:5 | LL | #[allow(clippy::almost_swapped)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![allow(clippy::almost_swapped)]` From b30422114e6bb7235398f21fe13ffa09429b7d0f Mon Sep 17 00:00:00 2001 From: Koxiaet <38139193+Koxiaet@users.noreply.github.com> Date: Tue, 1 Sep 2020 14:05:19 +0100 Subject: [PATCH 22/49] Allow GraphQL in doc without backticks --- clippy_lints/src/utils/conf.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 292dbd7ad6b..9c5a12ea9c8 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -122,7 +122,7 @@ define_Conf! { "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript", "NaN", "NaNs", - "OAuth", + "OAuth", "GraphQL", "OCaml", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "TensorFlow", From aa7ffa5257667edb284de16b529df3d4111d70ab Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 1 Sep 2020 22:39:09 +0900 Subject: [PATCH 23/49] Fix FP in `same_item_push` Don't emit a lint when the pushed item doesn't have Clone trait --- clippy_lints/src/loops.rs | 78 +++++++++++++++++++++----------------- tests/ui/same_item_push.rs | 6 +++ 2 files changed, 49 insertions(+), 35 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index c95e43a9430..25345f8fa31 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1140,43 +1140,51 @@ fn detect_same_item_push<'tcx>( walk_expr(&mut same_item_push_visitor, body); if same_item_push_visitor.should_lint { if let Some((vec, pushed_item)) = same_item_push_visitor.vec_push { - // Make sure that the push does not involve possibly mutating values - if let PatKind::Wild = pat.kind { - let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); - let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); - if let ExprKind::Path(ref qpath) = pushed_item.kind { - if_chain! { - if let Res::Local(hir_id) = qpath_res(cx, qpath, pushed_item.hir_id); - let node = cx.tcx.hir().get(hir_id); - if let Node::Binding(pat) = node; - if let PatKind::Binding(bind_ann, ..) = pat.kind; - if !matches!(bind_ann, BindingAnnotation::RefMut | BindingAnnotation::Mutable); - then { - span_lint_and_help( - cx, - SAME_ITEM_PUSH, - vec.span, - "it looks like the same item is being pushed into this Vec", - None, - &format!( - "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", - item_str, vec_str, item_str - ), - ) + let ty = cx.typeck_results().expr_ty(pushed_item); + if cx + .tcx + .lang_items() + .clone_trait() + .map_or(false, |id| implements_trait(cx, ty, id, &[])) + { + // Make sure that the push does not involve possibly mutating values + if let PatKind::Wild = pat.kind { + let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); + let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); + if let ExprKind::Path(ref qpath) = pushed_item.kind { + if_chain! { + if let Res::Local(hir_id) = qpath_res(cx, qpath, pushed_item.hir_id); + let node = cx.tcx.hir().get(hir_id); + if let Node::Binding(pat) = node; + if let PatKind::Binding(bind_ann, ..) = pat.kind; + if !matches!(bind_ann, BindingAnnotation::RefMut | BindingAnnotation::Mutable); + then { + span_lint_and_help( + cx, + SAME_ITEM_PUSH, + vec.span, + "it looks like the same item is being pushed into this Vec", + None, + &format!( + "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", + item_str, vec_str, item_str + ), + ) + } } + } else if mutated_variables(pushed_item, cx).map_or(false, |mutvars| mutvars.is_empty()) { + span_lint_and_help( + cx, + SAME_ITEM_PUSH, + vec.span, + "it looks like the same item is being pushed into this Vec", + None, + &format!( + "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", + item_str, vec_str, item_str + ), + ) } - } else if mutated_variables(pushed_item, cx).map_or(false, |mutvars| mutvars.is_empty()) { - span_lint_and_help( - cx, - SAME_ITEM_PUSH, - vec.span, - "it looks like the same item is being pushed into this Vec", - None, - &format!( - "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", - item_str, vec_str, item_str - ), - ) } } } diff --git a/tests/ui/same_item_push.rs b/tests/ui/same_item_push.rs index bfe27e02044..0903a873826 100644 --- a/tests/ui/same_item_push.rs +++ b/tests/ui/same_item_push.rs @@ -94,4 +94,10 @@ fn main() { vec13.push(item); item += 10; } + + // Fix #5979 + let mut vec14: Vec = Vec::new(); + for _ in 0..10 { + vec14.push(std::fs::File::open("foobar").unwrap()); + } } From a5754a1fad6eeb765bc825dcc657134985576787 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Wed, 2 Sep 2020 08:57:00 +0200 Subject: [PATCH 24/49] Run cargo dev fmt --- clippy_lints/src/utils/ast_utils.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/utils/ast_utils.rs b/clippy_lints/src/utils/ast_utils.rs index 3c3f8b26e3a..fa8dd210eba 100644 --- a/clippy_lints/src/utils/ast_utils.rs +++ b/clippy_lints/src/utils/ast_utils.rs @@ -191,7 +191,9 @@ pub fn eq_stmt(l: &Stmt, r: &Stmt) -> bool { (Item(l), Item(r)) => eq_item(l, r, eq_item_kind), (Expr(l), Expr(r)) | (Semi(l), Semi(r)) => eq_expr(l, r), (Empty, Empty) => true, - (MacCall(l), MacCall(r)) => l.style == r.style && eq_mac_call(&l.mac, &r.mac) && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r)), + (MacCall(l), MacCall(r)) => { + l.style == r.style && eq_mac_call(&l.mac, &r.mac) && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r)) + }, _ => false, } } From 246f1f8a8eccf4d997e85ed80097f9263624c4a4 Mon Sep 17 00:00:00 2001 From: Sasha Date: Mon, 31 Aug 2020 11:45:50 +0200 Subject: [PATCH 25/49] Improve recovery on malformed format call If a comma in a format call is replaced with a similar token, then we emit an error and continue parsing, instead of stopping at this point. --- tests/ui/issue-3145.rs | 2 +- tests/ui/issue-3145.stderr | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/ui/issue-3145.rs b/tests/ui/issue-3145.rs index f497d5550af..586d13647d1 100644 --- a/tests/ui/issue-3145.rs +++ b/tests/ui/issue-3145.rs @@ -1,3 +1,3 @@ fn main() { - println!("{}" a); //~ERROR expected token: `,` + println!("{}" a); //~ERROR expected `,`, found `a` } diff --git a/tests/ui/issue-3145.stderr b/tests/ui/issue-3145.stderr index cb0d95f5e26..a35032aa150 100644 --- a/tests/ui/issue-3145.stderr +++ b/tests/ui/issue-3145.stderr @@ -1,7 +1,7 @@ -error: expected token: `,` +error: expected `,`, found `a` --> $DIR/issue-3145.rs:2:19 | -LL | println!("{}" a); //~ERROR expected token: `,` +LL | println!("{}" a); //~ERROR expected `,`, found `a` | ^ expected `,` error: aborting due to previous error From 22c994061359aa9e0e08e44cd698dbfc6553834c Mon Sep 17 00:00:00 2001 From: Koxiaet <38139193+Koxiaet@users.noreply.github.com> Date: Wed, 2 Sep 2020 12:51:44 +0100 Subject: [PATCH 26/49] Add tests for allowed non-backticked identifiers in doc --- tests/ui/doc.rs | 13 +++++++++--- tests/ui/doc.stderr | 50 +++++++++++++++++++++++++-------------------- 2 files changed, 38 insertions(+), 25 deletions(-) diff --git a/tests/ui/doc.rs b/tests/ui/doc.rs index 77620c857e6..68c5d32846f 100644 --- a/tests/ui/doc.rs +++ b/tests/ui/doc.rs @@ -49,6 +49,16 @@ fn test_emphasis() { fn test_units() { } +/// This tests allowed identifiers. +/// DirectX +/// ECMAScript +/// OAuth GraphQL +/// TeX LaTeX BibTeX BibLaTeX +/// CamelCase (see also #2395) +/// be_sure_we_got_to_the_end_of_it +fn test_allowed() { +} + /// This test has [a link_with_underscores][chunked-example] inside it. See #823. /// See also [the issue tracker](https://github.com/rust-lang/rust-clippy/search?q=clippy::doc_markdown&type=Issues) /// on GitHub (which is a camel-cased word, but is OK). And here is another [inline link][inline_link]. @@ -168,9 +178,6 @@ fn issue_1920() {} /// Not ok: http://www.unicode.org/reports/tr9/#Reordering_Resolved_Levels fn issue_1832() {} -/// Ok: CamelCase (It should not be surrounded by backticks) -fn issue_2395() {} - /// An iterator over mycrate::Collection's values. /// It should not lint a `'static` lifetime in ticks. fn issue_2210() {} diff --git a/tests/ui/doc.stderr b/tests/ui/doc.stderr index ae9bb394cb9..23fca43590b 100644 --- a/tests/ui/doc.stderr +++ b/tests/ui/doc.stderr @@ -54,131 +54,137 @@ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the doc LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation + --> $DIR/doc.rs:58:5 + | +LL | /// be_sure_we_got_to_the_end_of_it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error: you should put `link_with_underscores` between ticks in the documentation - --> $DIR/doc.rs:52:22 + --> $DIR/doc.rs:62:22 | LL | /// This test has [a link_with_underscores][chunked-example] inside it. See #823. | ^^^^^^^^^^^^^^^^^^^^^ error: you should put `inline_link2` between ticks in the documentation - --> $DIR/doc.rs:55:21 + --> $DIR/doc.rs:65:21 | LL | /// It can also be [inline_link2]. | ^^^^^^^^^^^^ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:65:5 + --> $DIR/doc.rs:75:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put `CamelCaseThing` between ticks in the documentation - --> $DIR/doc.rs:73:8 + --> $DIR/doc.rs:83:8 | LL | /// ## CamelCaseThing | ^^^^^^^^^^^^^^ error: you should put `CamelCaseThing` between ticks in the documentation - --> $DIR/doc.rs:76:7 + --> $DIR/doc.rs:86:7 | LL | /// # CamelCaseThing | ^^^^^^^^^^^^^^ error: you should put `CamelCaseThing` between ticks in the documentation - --> $DIR/doc.rs:78:22 + --> $DIR/doc.rs:88:22 | LL | /// Not a title #897 CamelCaseThing | ^^^^^^^^^^^^^^ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:79:5 + --> $DIR/doc.rs:89:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:86:5 + --> $DIR/doc.rs:96:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:99:5 + --> $DIR/doc.rs:109:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put `FooBar` between ticks in the documentation - --> $DIR/doc.rs:110:43 + --> $DIR/doc.rs:120:43 | LL | /** E.g., serialization of an empty list: FooBar | ^^^^^^ error: you should put `BarQuz` between ticks in the documentation - --> $DIR/doc.rs:115:5 + --> $DIR/doc.rs:125:5 | LL | And BarQuz too. | ^^^^^^ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:116:1 + --> $DIR/doc.rs:126:1 | LL | be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put `FooBar` between ticks in the documentation - --> $DIR/doc.rs:121:43 + --> $DIR/doc.rs:131:43 | LL | /** E.g., serialization of an empty list: FooBar | ^^^^^^ error: you should put `BarQuz` between ticks in the documentation - --> $DIR/doc.rs:126:5 + --> $DIR/doc.rs:136:5 | LL | And BarQuz too. | ^^^^^^ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:127:1 + --> $DIR/doc.rs:137:1 | LL | be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:138:5 + --> $DIR/doc.rs:148:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put bare URLs between `<`/`>` or make a proper Markdown link - --> $DIR/doc.rs:165:13 + --> $DIR/doc.rs:175:13 | LL | /// Not ok: http://www.unicode.org | ^^^^^^^^^^^^^^^^^^^^^^ error: you should put bare URLs between `<`/`>` or make a proper Markdown link - --> $DIR/doc.rs:166:13 + --> $DIR/doc.rs:176:13 | LL | /// Not ok: https://www.unicode.org | ^^^^^^^^^^^^^^^^^^^^^^^ error: you should put bare URLs between `<`/`>` or make a proper Markdown link - --> $DIR/doc.rs:167:13 + --> $DIR/doc.rs:177:13 | LL | /// Not ok: http://www.unicode.org/ | ^^^^^^^^^^^^^^^^^^^^^^ error: you should put bare URLs between `<`/`>` or make a proper Markdown link - --> $DIR/doc.rs:168:13 + --> $DIR/doc.rs:178:13 | LL | /// Not ok: http://www.unicode.org/reports/tr9/#Reordering_Resolved_Levels | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put `mycrate::Collection` between ticks in the documentation - --> $DIR/doc.rs:174:22 + --> $DIR/doc.rs:181:22 | LL | /// An iterator over mycrate::Collection's values. | ^^^^^^^^^^^^^^^^^^^ -error: aborting due to 30 previous errors +error: aborting due to 31 previous errors From b220ddf146f4c11011b2e1b7f37ecb8e5485555b Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Wed, 2 Sep 2020 23:30:40 +0200 Subject: [PATCH 27/49] unit-arg - pr remarks --- clippy_lints/src/types.rs | 82 ++++++++++++++++++------------ clippy_lints/src/utils/mod.rs | 96 ++++++++++++++++++++--------------- 2 files changed, 103 insertions(+), 75 deletions(-) diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 16e48d91916..e83d491fac3 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -31,8 +31,8 @@ use crate::utils::paths; use crate::utils::{ clip, comparisons, differing_macro_contexts, higher, in_constant, indent_of, int_bits, is_type_diagnostic_item, last_path_segment, match_def_path, match_path, method_chain_args, multispan_sugg, numeric_literal::NumericLiteral, - qpath_res, sext, snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite, span_lint, - span_lint_and_help, span_lint_and_sugg, span_lint_and_then, trim_multiline, unsext, + qpath_res, reindent_multiline, sext, snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite, + span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, unsext, }; declare_clippy_lint! { @@ -802,7 +802,45 @@ impl<'tcx> LateLintPass<'tcx> for UnitArg { } } -#[allow(clippy::too_many_lines)] +fn fmt_stmts_and_call( + cx: &LateContext<'_>, + call_expr: &Expr<'_>, + call_snippet: &str, + args_snippets: &[impl AsRef], + non_empty_block_args_snippets: &[impl AsRef], +) -> String { + let call_expr_indent = indent_of(cx, call_expr.span).unwrap_or(0); + let call_snippet_with_replacements = args_snippets + .iter() + .fold(call_snippet.to_owned(), |acc, arg| acc.replacen(arg.as_ref(), "()", 1)); + + let mut stmts_and_call = non_empty_block_args_snippets + .iter() + .map(|it| it.as_ref().to_owned()) + .collect::>(); + stmts_and_call.push(call_snippet_with_replacements); + stmts_and_call = stmts_and_call + .into_iter() + .map(|v| reindent_multiline(v.into(), true, Some(call_expr_indent)).into_owned()) + .collect(); + + let mut stmts_and_call_snippet = stmts_and_call.join(&format!("{}{}", ";\n", " ".repeat(call_expr_indent))); + // expr is not in a block statement or result expression position, wrap in a block + let parent_node = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(call_expr.hir_id)); + if !matches!(parent_node, Some(Node::Block(_))) && !matches!(parent_node, Some(Node::Stmt(_))) { + let block_indent = call_expr_indent + 4; + stmts_and_call_snippet = + reindent_multiline(stmts_and_call_snippet.into(), true, Some(block_indent)).into_owned(); + stmts_and_call_snippet = format!( + "{{\n{}{}\n{}}}", + " ".repeat(block_indent), + &stmts_and_call_snippet, + " ".repeat(call_expr_indent) + ); + } + stmts_and_call_snippet +} + fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Expr<'_>]) { let mut applicability = Applicability::MachineApplicable; let (singular, plural) = if args_to_recover.len() > 1 { @@ -857,37 +895,15 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp .filter(|arg| !is_empty_block(arg)) .filter_map(|arg| snippet_opt(cx, arg.span)) .collect(); - let indent = indent_of(cx, expr.span).unwrap_or(0); - if let Some(expr_str) = snippet_opt(cx, expr.span) { - let expr_with_replacements = arg_snippets - .iter() - .fold(expr_str, |acc, arg| acc.replacen(arg, "()", 1)); - - // expr is not in a block statement or result expression position, wrap in a block - let parent_node = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(expr.hir_id)); - let wrap_in_block = - !matches!(parent_node, Some(Node::Block(_))) && !matches!(parent_node, Some(Node::Stmt(_))); - - let stmts_indent = if wrap_in_block { indent + 4 } else { indent }; - let mut stmts_and_call = arg_snippets_without_empty_blocks.clone(); - stmts_and_call.push(expr_with_replacements); - let mut stmts_and_call_str = stmts_and_call - .into_iter() - .enumerate() - .map(|(i, v)| { - let with_indent_prefix = if i > 0 { " ".repeat(stmts_indent) + &v } else { v }; - trim_multiline(with_indent_prefix.into(), true, Some(stmts_indent)).into_owned() - }) - .collect::>() - .join(";\n"); - - if wrap_in_block { - stmts_and_call_str = " ".repeat(stmts_indent) + &stmts_and_call_str; - stmts_and_call_str = format!("{{\n{}\n{}}}", &stmts_and_call_str, " ".repeat(indent)); - } - - let sugg = stmts_and_call_str; + if let Some(call_snippet) = snippet_opt(cx, expr.span) { + let sugg = fmt_stmts_and_call( + cx, + expr, + &call_snippet, + &arg_snippets, + &arg_snippets_without_empty_blocks, + ); if arg_snippets_without_empty_blocks.is_empty() { db.multipart_suggestion( diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 4ce4cdeefb4..f394b980127 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -19,6 +19,7 @@ pub mod paths; pub mod ptr; pub mod sugg; pub mod usage; + pub use self::attrs::*; pub use self::diagnostics::*; pub use self::hir_utils::{both, eq_expr_value, over, SpanlessEq, SpanlessHash}; @@ -108,6 +109,7 @@ pub fn in_macro(span: Span) -> bool { false } } + // If the snippet is empty, it's an attribute that was inserted during macro // expansion and we want to ignore those, because they could come from external // sources that the user has no control over. @@ -571,7 +573,7 @@ pub fn snippet_block<'a, T: LintContext>( ) -> Cow<'a, str> { let snip = snippet(cx, span, default); let indent = indent_relative_to.and_then(|s| indent_of(cx, s)); - trim_multiline(snip, true, indent) + reindent_multiline(snip, true, indent) } /// Same as `snippet_block`, but adapts the applicability level by the rules of @@ -585,7 +587,7 @@ pub fn snippet_block_with_applicability<'a, T: LintContext>( ) -> Cow<'a, str> { let snip = snippet_with_applicability(cx, span, default, applicability); let indent = indent_relative_to.and_then(|s| indent_of(cx, s)); - trim_multiline(snip, true, indent) + reindent_multiline(snip, true, indent) } /// Returns a new Span that extends the original Span to the first non-whitespace char of the first @@ -661,16 +663,16 @@ pub fn expr_block<'a, T: LintContext>( } } -/// Trim indentation from a multiline string with possibility of ignoring the -/// first line. -pub fn trim_multiline(s: Cow<'_, str>, ignore_first: bool, indent: Option) -> Cow<'_, str> { - let s_space = trim_multiline_inner(s, ignore_first, indent, ' '); - let s_tab = trim_multiline_inner(s_space, ignore_first, indent, '\t'); - trim_multiline_inner(s_tab, ignore_first, indent, ' ') +/// Reindent a multiline string with possibility of ignoring the first line. +#[allow(clippy::needless_pass_by_value)] +pub fn reindent_multiline(s: Cow<'_, str>, ignore_first: bool, indent: Option) -> Cow<'_, str> { + let s_space = reindent_multiline_inner(&s, ignore_first, indent, ' '); + let s_tab = reindent_multiline_inner(&s_space, ignore_first, indent, '\t'); + reindent_multiline_inner(&s_tab, ignore_first, indent, ' ').into() } -fn trim_multiline_inner(s: Cow<'_, str>, ignore_first: bool, indent: Option, ch: char) -> Cow<'_, str> { - let mut x = s +fn reindent_multiline_inner(s: &str, ignore_first: bool, indent: Option, ch: char) -> String { + let x = s .lines() .skip(ignore_first as usize) .filter_map(|l| { @@ -683,26 +685,20 @@ fn trim_multiline_inner(s: Cow<'_, str>, ignore_first: bool, indent: Option 0 { - Cow::Owned( - s.lines() - .enumerate() - .map(|(i, l)| { - if (ignore_first && i == 0) || l.is_empty() { - l - } else { - l.split_at(x).1 - } - }) - .collect::>() - .join("\n"), - ) - } else { - s - } + let indent = indent.unwrap_or(0); + s.lines() + .enumerate() + .map(|(i, l)| { + if (ignore_first && i == 0) || l.is_empty() { + l.to_owned() + } else if x > indent { + l.split_at(x - indent).1.to_owned() + } else { + " ".repeat(indent - x) + l + } + }) + .collect::>() + .join("\n") } /// Gets the parent expression, if any –- this is useful to constrain a lint. @@ -1475,26 +1471,26 @@ macro_rules! unwrap_cargo_metadata { #[cfg(test)] mod test { - use super::{trim_multiline, without_block_comments}; + use super::{reindent_multiline, without_block_comments}; #[test] - fn test_trim_multiline_single_line() { - assert_eq!("", trim_multiline("".into(), false, None)); - assert_eq!("...", trim_multiline("...".into(), false, None)); - assert_eq!("...", trim_multiline(" ...".into(), false, None)); - assert_eq!("...", trim_multiline("\t...".into(), false, None)); - assert_eq!("...", trim_multiline("\t\t...".into(), false, None)); + fn test_reindent_multiline_single_line() { + assert_eq!("", reindent_multiline("".into(), false, None)); + assert_eq!("...", reindent_multiline("...".into(), false, None)); + assert_eq!("...", reindent_multiline(" ...".into(), false, None)); + assert_eq!("...", reindent_multiline("\t...".into(), false, None)); + assert_eq!("...", reindent_multiline("\t\t...".into(), false, None)); } #[test] #[rustfmt::skip] - fn test_trim_multiline_block() { + fn test_reindent_multiline_block() { assert_eq!("\ if x { y } else { z - }", trim_multiline(" if x { + }", reindent_multiline(" if x { y } else { z @@ -1504,7 +1500,7 @@ mod test { \ty } else { \tz - }", trim_multiline(" if x { + }", reindent_multiline(" if x { \ty } else { \tz @@ -1513,14 +1509,14 @@ mod test { #[test] #[rustfmt::skip] - fn test_trim_multiline_empty_line() { + fn test_reindent_multiline_empty_line() { assert_eq!("\ if x { y } else { z - }", trim_multiline(" if x { + }", reindent_multiline(" if x { y } else { @@ -1528,6 +1524,22 @@ mod test { }".into(), false, None)); } + #[test] + #[rustfmt::skip] + fn test_reindent_multiline_lines_deeper() { + assert_eq!("\ + if x { + y + } else { + z + }", reindent_multiline("\ + if x { + y + } else { + z + }".into(), true, Some(8))); + } + #[test] fn test_without_block_comments_lines_without_block_comments() { let result = without_block_comments(vec!["/*", "", "*/"]); From 93ce686b5df94f52a040a05c9434b4341efffec5 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Thu, 3 Sep 2020 04:58:14 +0200 Subject: [PATCH 28/49] Update ui stderr with improved rustc output Related rust pull request: rust-lang/rust#76160 --- tests/ui/issue-3145.stderr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/issue-3145.stderr b/tests/ui/issue-3145.stderr index cb0d95f5e26..8f2922b022a 100644 --- a/tests/ui/issue-3145.stderr +++ b/tests/ui/issue-3145.stderr @@ -1,4 +1,4 @@ -error: expected token: `,` +error: expected `,`, found `a` --> $DIR/issue-3145.rs:2:19 | LL | println!("{}" a); //~ERROR expected token: `,` From cf1cc7c449e881f0ac5d8474209222bc269df250 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Fri, 4 Sep 2020 05:15:31 +0200 Subject: [PATCH 29/49] Simplify `clippy::default_trait_access` Remove repeated matching on the same QPath. --- clippy_lints/src/default_trait_access.rs | 46 +++++++++--------------- 1 file changed, 16 insertions(+), 30 deletions(-) diff --git a/clippy_lints/src/default_trait_access.rs b/clippy_lints/src/default_trait_access.rs index 0b0a1307876..320a2a257bd 100644 --- a/clippy_lints/src/default_trait_access.rs +++ b/clippy_lints/src/default_trait_access.rs @@ -38,37 +38,23 @@ impl<'tcx> LateLintPass<'tcx> for DefaultTraitAccess { if let ExprKind::Path(ref qpath) = path.kind; if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id(); if match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD); + // Detect and ignore ::default() because these calls do explicitly name the type. + if let QPath::Resolved(None, _path) = qpath; then { - match qpath { - QPath::Resolved(..) => { - if_chain! { - // Detect and ignore ::default() because these calls do - // explicitly name the type. - if let ExprKind::Call(ref method, ref _args) = expr.kind; - if let ExprKind::Path(ref p) = method.kind; - if let QPath::Resolved(Some(_ty), _path) = p; - then { - return; - } - } - - // TODO: Work out a way to put "whatever the imported way of referencing - // this type in this file" rather than a fully-qualified type. - let expr_ty = cx.typeck_results().expr_ty(expr); - if let ty::Adt(def, ..) = expr_ty.kind { - let replacement = format!("{}::default()", cx.tcx.def_path_str(def.did)); - span_lint_and_sugg( - cx, - DEFAULT_TRAIT_ACCESS, - expr.span, - &format!("calling `{}` is more clear than this expression", replacement), - "try", - replacement, - Applicability::Unspecified, // First resolve the TODO above - ); - } - }, - QPath::TypeRelative(..) | QPath::LangItem(..) => {}, + let expr_ty = cx.typeck_results().expr_ty(expr); + if let ty::Adt(def, ..) = expr_ty.kind { + // TODO: Work out a way to put "whatever the imported way of referencing + // this type in this file" rather than a fully-qualified type. + let replacement = format!("{}::default()", cx.tcx.def_path_str(def.did)); + span_lint_and_sugg( + cx, + DEFAULT_TRAIT_ACCESS, + expr.span, + &format!("calling `{}` is more clear than this expression", replacement), + "try", + replacement, + Applicability::Unspecified, // First resolve the TODO above + ); } } } From 28f9b84042b7d0ffd1f913e8ba82348ebe23dd1b Mon Sep 17 00:00:00 2001 From: LeSeulArtichaut Date: Tue, 4 Aug 2020 00:18:29 +0200 Subject: [PATCH 30/49] `ty.kind` -> `ty.kind()` in rustdoc and clippy --- clippy_lints/src/atomic_ordering.rs | 2 +- clippy_lints/src/await_holding_lock.rs | 2 +- clippy_lints/src/bytecount.rs | 2 +- clippy_lints/src/consts.rs | 24 ++++----- clippy_lints/src/default_trait_access.rs | 2 +- clippy_lints/src/derive.rs | 8 +-- clippy_lints/src/doc.rs | 4 +- clippy_lints/src/drop_forget_ref.rs | 2 +- clippy_lints/src/enum_clike.rs | 4 +- clippy_lints/src/eta_reduction.rs | 8 +-- clippy_lints/src/eval_order_dependence.rs | 4 +- .../src/float_equality_without_abs.rs | 4 +- clippy_lints/src/float_literal.rs | 2 +- clippy_lints/src/floating_point_arithmetic.rs | 2 +- clippy_lints/src/format.rs | 2 +- clippy_lints/src/functions.rs | 2 +- clippy_lints/src/future_not_send.rs | 2 +- clippy_lints/src/identity_op.rs | 2 +- clippy_lints/src/indexing_slicing.rs | 4 +- clippy_lints/src/large_const_arrays.rs | 2 +- clippy_lints/src/large_stack_arrays.rs | 2 +- clippy_lints/src/len_zero.rs | 2 +- clippy_lints/src/loops.rs | 12 ++--- clippy_lints/src/map_clone.rs | 4 +- clippy_lints/src/map_unit_fn.rs | 4 +- clippy_lints/src/matches.rs | 16 +++--- .../src/methods/inefficient_to_string.rs | 4 +- clippy_lints/src/methods/mod.rs | 54 +++++++++---------- clippy_lints/src/misc.rs | 6 +-- clippy_lints/src/modulo_arithmetic.rs | 2 +- clippy_lints/src/mut_key.rs | 4 +- clippy_lints/src/mut_mut.rs | 2 +- clippy_lints/src/mut_reference.rs | 4 +- clippy_lints/src/mutable_debug_assertion.rs | 2 +- clippy_lints/src/mutex_atomic.rs | 6 +-- clippy_lints/src/needless_borrow.rs | 6 +-- clippy_lints/src/needless_pass_by_value.rs | 2 +- clippy_lints/src/needless_update.rs | 2 +- clippy_lints/src/pattern_type_mismatch.rs | 10 ++-- clippy_lints/src/ptr.rs | 2 +- clippy_lints/src/ranges.rs | 2 +- clippy_lints/src/redundant_clone.rs | 4 +- clippy_lints/src/shadow.rs | 2 +- clippy_lints/src/swap.rs | 4 +- clippy_lints/src/to_digit_is_some.rs | 2 +- clippy_lints/src/transmute.rs | 12 ++--- .../src/trivially_copy_pass_by_ref.rs | 4 +- clippy_lints/src/try_err.rs | 12 ++--- clippy_lints/src/types.rs | 40 +++++++------- clippy_lints/src/unit_return_expecting_ord.rs | 2 +- clippy_lints/src/unnamed_address.rs | 4 +- clippy_lints/src/unnecessary_sort_by.rs | 2 +- clippy_lints/src/useless_conversion.rs | 4 +- clippy_lints/src/utils/mod.rs | 45 ++++++++-------- clippy_lints/src/vec.rs | 6 +-- 55 files changed, 187 insertions(+), 188 deletions(-) diff --git a/clippy_lints/src/atomic_ordering.rs b/clippy_lints/src/atomic_ordering.rs index 277fe350055..2d964ac2b9f 100644 --- a/clippy_lints/src/atomic_ordering.rs +++ b/clippy_lints/src/atomic_ordering.rs @@ -53,7 +53,7 @@ const ATOMIC_TYPES: [&str; 12] = [ ]; fn type_is_atomic(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - if let ty::Adt(&ty::AdtDef { did, .. }, _) = cx.typeck_results().expr_ty(expr).kind { + if let ty::Adt(&ty::AdtDef { did, .. }, _) = cx.typeck_results().expr_ty(expr).kind() { ATOMIC_TYPES .iter() .any(|ty| match_def_path(cx, did, &["core", "sync", "atomic", ty])) diff --git a/clippy_lints/src/await_holding_lock.rs b/clippy_lints/src/await_holding_lock.rs index b10b1e0a65a..f18e7e5d997 100644 --- a/clippy_lints/src/await_holding_lock.rs +++ b/clippy_lints/src/await_holding_lock.rs @@ -67,7 +67,7 @@ impl LateLintPass<'_> for AwaitHoldingLock { fn check_interior_types(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) { for ty_cause in ty_causes { - if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind { + if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() { if is_mutex_guard(cx, adt.did) { span_lint_and_note( cx, diff --git a/clippy_lints/src/bytecount.rs b/clippy_lints/src/bytecount.rs index cdb49d777d8..189c07427ae 100644 --- a/clippy_lints/src/bytecount.rs +++ b/clippy_lints/src/bytecount.rs @@ -63,7 +63,7 @@ impl<'tcx> LateLintPass<'tcx> for ByteCount { _ => { return; } } }; - if ty::Uint(UintTy::U8) != walk_ptrs_ty(cx.typeck_results().expr_ty(needle)).kind { + if ty::Uint(UintTy::U8) != *walk_ptrs_ty(cx.typeck_results().expr_ty(needle)).kind() { return; } let haystack = if let ExprKind::MethodCall(ref path, _, ref args, _) = diff --git a/clippy_lints/src/consts.rs b/clippy_lints/src/consts.rs index c77b80bc237..3ee022e4e68 100644 --- a/clippy_lints/src/consts.rs +++ b/clippy_lints/src/consts.rs @@ -123,7 +123,7 @@ impl Constant { (&Self::Str(ref ls), &Self::Str(ref rs)) => Some(ls.cmp(rs)), (&Self::Char(ref l), &Self::Char(ref r)) => Some(l.cmp(r)), (&Self::Int(l), &Self::Int(r)) => { - if let ty::Int(int_ty) = cmp_type.kind { + if let ty::Int(int_ty) = *cmp_type.kind() { Some(sext(tcx, l, int_ty).cmp(&sext(tcx, r, int_ty))) } else { Some(l.cmp(&r)) @@ -162,7 +162,7 @@ pub fn lit_to_constant(lit: &LitKind, ty: Option>) -> Constant { FloatTy::F32 => Constant::F32(is.as_str().parse().unwrap()), FloatTy::F64 => Constant::F64(is.as_str().parse().unwrap()), }, - LitKind::Float(ref is, LitFloatType::Unsuffixed) => match ty.expect("type of float is known").kind { + LitKind::Float(ref is, LitFloatType::Unsuffixed) => match ty.expect("type of float is known").kind() { ty::Float(FloatTy::F32) => Constant::F32(is.as_str().parse().unwrap()), ty::Float(FloatTy::F64) => Constant::F64(is.as_str().parse().unwrap()), _ => bug!(), @@ -230,7 +230,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { ExprKind::Array(ref vec) => self.multi(vec).map(Constant::Vec), ExprKind::Tup(ref tup) => self.multi(tup).map(Constant::Tuple), ExprKind::Repeat(ref value, _) => { - let n = match self.typeck_results.expr_ty(e).kind { + let n = match self.typeck_results.expr_ty(e).kind() { ty::Array(_, n) => n.try_eval_usize(self.lcx.tcx, self.lcx.param_env)?, _ => span_bug!(e.span, "typeck error"), }; @@ -281,7 +281,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { Bool(b) => Some(Bool(!b)), Int(value) => { let value = !value; - match ty.kind { + match *ty.kind() { ty::Int(ity) => Some(Int(unsext(self.lcx.tcx, value as i128, ity))), ty::Uint(ity) => Some(Int(clip(self.lcx.tcx, value, ity))), _ => None, @@ -295,7 +295,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { use self::Constant::{Int, F32, F64}; match *o { Int(value) => { - let ity = match ty.kind { + let ity = match *ty.kind() { ty::Int(ity) => ity, _ => return None, }; @@ -402,7 +402,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { let l = self.expr(left)?; let r = self.expr(right); match (l, r) { - (Constant::Int(l), Some(Constant::Int(r))) => match self.typeck_results.expr_ty_opt(left)?.kind { + (Constant::Int(l), Some(Constant::Int(r))) => match *self.typeck_results.expr_ty_opt(left)?.kind() { ty::Int(ity) => { let l = sext(self.lcx.tcx, l, ity); let r = sext(self.lcx.tcx, r, ity); @@ -495,7 +495,7 @@ pub fn miri_to_const(result: &ty::Const<'_>) -> Option { use rustc_middle::mir::interpret::{ConstValue, Scalar}; match result.val { ty::ConstKind::Value(ConstValue::Scalar(Scalar::Raw { data: d, .. })) => { - match result.ty.kind { + match result.ty.kind() { ty::Bool => Some(Constant::Bool(d == 1)), ty::Uint(_) | ty::Int(_) => Some(Constant::Int(d)), ty::Float(FloatTy::F32) => Some(Constant::F32(f32::from_bits( @@ -505,7 +505,7 @@ pub fn miri_to_const(result: &ty::Const<'_>) -> Option { d.try_into().expect("invalid f64 bit representation"), ))), ty::RawPtr(type_and_mut) => { - if let ty::Uint(_) = type_and_mut.ty.kind { + if let ty::Uint(_) = type_and_mut.ty.kind() { return Some(Constant::RawPtr(d)); } None @@ -514,8 +514,8 @@ pub fn miri_to_const(result: &ty::Const<'_>) -> Option { _ => None, } }, - ty::ConstKind::Value(ConstValue::Slice { data, start, end }) => match result.ty.kind { - ty::Ref(_, tam, _) => match tam.kind { + ty::ConstKind::Value(ConstValue::Slice { data, start, end }) => match result.ty.kind() { + ty::Ref(_, tam, _) => match tam.kind() { ty::Str => String::from_utf8( data.inspect_with_uninit_and_ptr_outside_interpreter(start..end) .to_owned(), @@ -526,8 +526,8 @@ pub fn miri_to_const(result: &ty::Const<'_>) -> Option { }, _ => None, }, - ty::ConstKind::Value(ConstValue::ByRef { alloc, offset: _ }) => match result.ty.kind { - ty::Array(sub_type, len) => match sub_type.kind { + ty::ConstKind::Value(ConstValue::ByRef { alloc, offset: _ }) => match result.ty.kind() { + ty::Array(sub_type, len) => match sub_type.kind() { ty::Float(FloatTy::F32) => match miri_to_const(len) { Some(Constant::Int(len)) => alloc .inspect_with_uninit_and_ptr_outside_interpreter(0..(4 * len as usize)) diff --git a/clippy_lints/src/default_trait_access.rs b/clippy_lints/src/default_trait_access.rs index 067ea903bdd..1654df56a9a 100644 --- a/clippy_lints/src/default_trait_access.rs +++ b/clippy_lints/src/default_trait_access.rs @@ -55,7 +55,7 @@ impl<'tcx> LateLintPass<'tcx> for DefaultTraitAccess { // TODO: Work out a way to put "whatever the imported way of referencing // this type in this file" rather than a fully-qualified type. let expr_ty = cx.typeck_results().expr_ty(expr); - if let ty::Adt(..) = expr_ty.kind { + if let ty::Adt(..) = expr_ty.kind() { let replacement = format!("{}::default()", expr_ty); span_lint_and_sugg( cx, diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 58b0704294b..bf8e030cc29 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -299,20 +299,20 @@ fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &T return; } - match ty.kind { + match *ty.kind() { ty::Adt(def, _) if def.is_union() => return, // Some types are not Clone by default but could be cloned “by hand” if necessary ty::Adt(def, substs) => { for variant in &def.variants { for field in &variant.fields { - if let ty::FnDef(..) = field.ty(cx.tcx, substs).kind { + if let ty::FnDef(..) = field.ty(cx.tcx, substs).kind() { return; } } for subst in substs { if let ty::subst::GenericArgKind::Type(subst) = subst.unpack() { - if let ty::Param(_) = subst.kind { + if let ty::Param(_) = subst.kind() { return; } } @@ -353,7 +353,7 @@ fn check_unsafe_derive_deserialize<'tcx>( if_chain! { if match_path(&trait_ref.path, &paths::SERDE_DESERIALIZE); - if let ty::Adt(def, _) = ty.kind; + if let ty::Adt(def, _) = ty.kind(); if let Some(local_def_id) = def.did.as_local(); let adt_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id); if !is_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id); diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 9555459e240..50121a054c7 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -239,9 +239,9 @@ fn lint_for_missing_headers<'tcx>( let mir = cx.tcx.optimized_mir(def_id.to_def_id()); let ret_ty = mir.return_ty(); if implements_trait(cx, ret_ty, future, &[]); - if let ty::Opaque(_, subs) = ret_ty.kind; + if let ty::Opaque(_, subs) = ret_ty.kind(); if let Some(gen) = subs.types().next(); - if let ty::Generator(_, subs, _) = gen.kind; + if let ty::Generator(_, subs, _) = gen.kind(); if is_type_diagnostic_item(cx, subs.as_generator().return_ty(), sym!(result_type)); then { span_lint( diff --git a/clippy_lints/src/drop_forget_ref.rs b/clippy_lints/src/drop_forget_ref.rs index 57ff569f14b..cf528d189b4 100644 --- a/clippy_lints/src/drop_forget_ref.rs +++ b/clippy_lints/src/drop_forget_ref.rs @@ -121,7 +121,7 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef { let arg = &args[0]; let arg_ty = cx.typeck_results().expr_ty(arg); - if let ty::Ref(..) = arg_ty.kind { + if let ty::Ref(..) = arg_ty.kind() { if match_def_path(cx, def_id, &paths::DROP) { lint = DROP_REF; msg = DROP_REF_SUMMARY.to_string(); diff --git a/clippy_lints/src/enum_clike.rs b/clippy_lints/src/enum_clike.rs index 48caf48dbdb..fb80f48a9cc 100644 --- a/clippy_lints/src/enum_clike.rs +++ b/clippy_lints/src/enum_clike.rs @@ -53,12 +53,12 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant { .ok() .map(|val| rustc_middle::ty::Const::from_value(cx.tcx, val, ty)); if let Some(Constant::Int(val)) = constant.and_then(miri_to_const) { - if let ty::Adt(adt, _) = ty.kind { + if let ty::Adt(adt, _) = ty.kind() { if adt.is_enum() { ty = adt.repr.discr_type().to_ty(cx.tcx); } } - match ty.kind { + match ty.kind() { ty::Int(IntTy::Isize) => { let val = ((val as i128) << 64) >> 64; if i32::try_from(val).is_ok() { diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 87254c1dbc4..53df3abbf54 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -99,7 +99,7 @@ fn check_closure(cx: &LateContext<'_>, expr: &Expr<'_>) { let fn_ty = cx.typeck_results().expr_ty(caller); - if matches!(fn_ty.kind, ty::FnDef(_, _) | ty::FnPtr(_) | ty::Closure(_, _)); + if matches!(fn_ty.kind(), ty::FnDef(_, _) | ty::FnPtr(_) | ty::Closure(_, _)); if !type_is_unsafe_function(cx, fn_ty); @@ -173,14 +173,14 @@ fn get_ufcs_type_name(cx: &LateContext<'_>, method_def_id: def_id::DefId, self_a } fn match_borrow_depth(lhs: Ty<'_>, rhs: Ty<'_>) -> bool { - match (&lhs.kind, &rhs.kind) { + match (&lhs.kind(), &rhs.kind()) { (ty::Ref(_, t1, mut1), ty::Ref(_, t2, mut2)) => mut1 == mut2 && match_borrow_depth(&t1, &t2), (l, r) => !matches!((l, r), (ty::Ref(_, _, _), _) | (_, ty::Ref(_, _, _))), } } fn match_types(lhs: Ty<'_>, rhs: Ty<'_>) -> bool { - match (&lhs.kind, &rhs.kind) { + match (&lhs.kind(), &rhs.kind()) { (ty::Bool, ty::Bool) | (ty::Char, ty::Char) | (ty::Int(_), ty::Int(_)) @@ -194,7 +194,7 @@ fn match_types(lhs: Ty<'_>, rhs: Ty<'_>) -> bool { } fn get_type_name(cx: &LateContext<'_>, ty: Ty<'_>) -> String { - match ty.kind { + match ty.kind() { ty::Adt(t, _) => cx.tcx.def_path_str(t.did), ty::Ref(_, r, _) => get_type_name(cx, &r), _ => ty.to_string(), diff --git a/clippy_lints/src/eval_order_dependence.rs b/clippy_lints/src/eval_order_dependence.rs index c00638ecc0c..4240147f498 100644 --- a/clippy_lints/src/eval_order_dependence.rs +++ b/clippy_lints/src/eval_order_dependence.rs @@ -138,10 +138,10 @@ impl<'a, 'tcx> Visitor<'tcx> for DivergenceVisitor<'a, 'tcx> { ExprKind::Continue(_) | ExprKind::Break(_, _) | ExprKind::Ret(_) => self.report_diverging_sub_expr(e), ExprKind::Call(ref func, _) => { let typ = self.cx.typeck_results().expr_ty(func); - match typ.kind { + match typ.kind() { ty::FnDef(..) | ty::FnPtr(_) => { let sig = typ.fn_sig(self.cx.tcx); - if let ty::Never = self.cx.tcx.erase_late_bound_regions(&sig).output().kind { + if let ty::Never = self.cx.tcx.erase_late_bound_regions(&sig).output().kind() { self.report_diverging_sub_expr(e); } }, diff --git a/clippy_lints/src/float_equality_without_abs.rs b/clippy_lints/src/float_equality_without_abs.rs index 9ac5a45eb45..69818b4d3c6 100644 --- a/clippy_lints/src/float_equality_without_abs.rs +++ b/clippy_lints/src/float_equality_without_abs.rs @@ -81,8 +81,8 @@ impl<'tcx> LateLintPass<'tcx> for FloatEqualityWithoutAbs { // values of the substractions on the left hand side are of the type float let t_val_l = cx.typeck_results().expr_ty(val_l); let t_val_r = cx.typeck_results().expr_ty(val_r); - if let ty::Float(_) = t_val_l.kind; - if let ty::Float(_) = t_val_r.kind; + if let ty::Float(_) = t_val_l.kind(); + if let ty::Float(_) = t_val_r.kind(); then { let sug_l = sugg::Sugg::hir(cx, &val_l, ".."); diff --git a/clippy_lints/src/float_literal.rs b/clippy_lints/src/float_literal.rs index 358b9f6dcd0..1fe4461533b 100644 --- a/clippy_lints/src/float_literal.rs +++ b/clippy_lints/src/float_literal.rs @@ -62,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatLiteral { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if_chain! { let ty = cx.typeck_results().expr_ty(expr); - if let ty::Float(fty) = ty.kind; + if let ty::Float(fty) = *ty.kind(); if let hir::ExprKind::Lit(ref lit) = expr.kind; if let LitKind::Float(sym, lit_float_ty) = lit.node; then { diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 1b02cee126d..18fea8b34bf 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -136,7 +136,7 @@ fn prepare_receiver_sugg<'a>(cx: &LateContext<'_>, mut expr: &'a Expr<'a>) -> Su if_chain! { // if the expression is a float literal and it is unsuffixed then // add a suffix so the suggestion is valid and unambiguous - if let ty::Float(float_ty) = cx.typeck_results().expr_ty(expr).kind; + if let ty::Float(float_ty) = cx.typeck_results().expr_ty(expr).kind(); if let ExprKind::Lit(lit) = &expr.kind; if let ast::LitKind::Float(sym, ast::LitFloatType::Unsuffixed) = lit.node; then { diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index 572c839502f..8bd85af8768 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -91,7 +91,7 @@ fn on_argumentv1_new<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arms: & if pats.len() == 1; then { let ty = walk_ptrs_ty(cx.typeck_results().pat_ty(&pats[0])); - if ty.kind != rustc_middle::ty::Str && !is_type_diagnostic_item(cx, ty, sym!(string_type)) { + 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 { diff --git a/clippy_lints/src/functions.rs b/clippy_lints/src/functions.rs index ac1c7aa9bbb..89fde1d509d 100644 --- a/clippy_lints/src/functions.rs +++ b/clippy_lints/src/functions.rs @@ -505,7 +505,7 @@ fn is_mutable_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, tys: &mut FxHashSet< static KNOWN_WRAPPER_TYS: &[&[&str]] = &[&["alloc", "rc", "Rc"], &["std", "sync", "Arc"]]; fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, tys: &mut FxHashSet) -> bool { - match ty.kind { + match *ty.kind() { // primitive types are never mutable ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => false, ty::Adt(ref adt, ref substs) => { diff --git a/clippy_lints/src/future_not_send.rs b/clippy_lints/src/future_not_send.rs index 0fdb5b8c2a4..2ab257ca88e 100644 --- a/clippy_lints/src/future_not_send.rs +++ b/clippy_lints/src/future_not_send.rs @@ -61,7 +61,7 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { return; } let ret_ty = utils::return_ty(cx, hir_id); - if let Opaque(id, subst) = ret_ty.kind { + if let Opaque(id, subst) = *ret_ty.kind() { let preds = cx.tcx.predicates_of(id).instantiate(cx.tcx, subst); let mut is_future = false; for p in preds.predicates { diff --git a/clippy_lints/src/identity_op.rs b/clippy_lints/src/identity_op.rs index 4c62637858c..8501d347702 100644 --- a/clippy_lints/src/identity_op.rs +++ b/clippy_lints/src/identity_op.rs @@ -75,7 +75,7 @@ fn is_allowed(cx: &LateContext<'_>, cmp: BinOp, left: &Expr<'_>, right: &Expr<'_ #[allow(clippy::cast_possible_wrap)] fn check(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span) { if let Some(Constant::Int(v)) = constant_simple(cx, cx.typeck_results(), e) { - let check = match cx.typeck_results().expr_ty(e).kind { + let check = match *cx.typeck_results().expr_ty(e).kind() { ty::Int(ity) => unsext(cx.tcx, -1_i128, ity), ty::Uint(uty) => clip(cx.tcx, !0, uty), _ => return, diff --git a/clippy_lints/src/indexing_slicing.rs b/clippy_lints/src/indexing_slicing.rs index 90b1a529be7..a28eda8be15 100644 --- a/clippy_lints/src/indexing_slicing.rs +++ b/clippy_lints/src/indexing_slicing.rs @@ -91,7 +91,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { let ty = cx.typeck_results().expr_ty(array); if let Some(range) = higher::range(index) { // Ranged indexes, i.e., &x[n..m], &x[n..], &x[..n] and &x[..] - if let ty::Array(_, s) = ty.kind { + if let ty::Array(_, s) = ty.kind() { let size: u128 = if let Some(size) = s.try_eval_usize(cx.tcx, cx.param_env) { size.into() } else { @@ -141,7 +141,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { span_lint_and_help(cx, INDEXING_SLICING, expr.span, "slicing may panic.", None, help_msg); } else { // Catchall non-range index, i.e., [n] or [n << m] - if let ty::Array(..) = ty.kind { + if let ty::Array(..) = ty.kind() { // Index is a constant uint. if let Some(..) = constant(cx, cx.typeck_results(), index) { // Let rustc's `const_err` lint handle constant `usize` indexing on arrays. diff --git a/clippy_lints/src/large_const_arrays.rs b/clippy_lints/src/large_const_arrays.rs index c6cc174a8c9..025ff86da39 100644 --- a/clippy_lints/src/large_const_arrays.rs +++ b/clippy_lints/src/large_const_arrays.rs @@ -51,7 +51,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeConstArrays { if !item.span.from_expansion(); if let ItemKind::Const(hir_ty, _) = &item.kind; let ty = hir_ty_to_ty(cx.tcx, hir_ty); - if let ty::Array(element_type, cst) = ty.kind; + if let ty::Array(element_type, cst) = ty.kind(); if let ConstKind::Value(val) = cst.val; if let ConstValue::Scalar(element_count) = val; if let Ok(element_count) = element_count.to_machine_usize(&cx.tcx); diff --git a/clippy_lints/src/large_stack_arrays.rs b/clippy_lints/src/large_stack_arrays.rs index a7c71587923..9fd3780e14e 100644 --- a/clippy_lints/src/large_stack_arrays.rs +++ b/clippy_lints/src/large_stack_arrays.rs @@ -42,7 +42,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackArrays { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { if let ExprKind::Repeat(_, _) = expr.kind; - if let ty::Array(element_type, cst) = cx.typeck_results().expr_ty(expr).kind; + if let ty::Array(element_type, cst) = cx.typeck_results().expr_ty(expr).kind(); if let ConstKind::Value(val) = cst.val; if let ConstValue::Scalar(element_count) = val; if let Ok(element_count) = element_count.to_machine_usize(&cx.tcx); diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index b691d363d2f..42a98dc963d 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -286,7 +286,7 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { } let ty = &walk_ptrs_ty(cx.typeck_results().expr_ty(expr)); - match ty.kind { + match ty.kind() { ty::Dynamic(ref tt, ..) => tt.principal().map_or(false, |principal| { cx.tcx .associated_items(principal.def_id()) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index c95e43a9430..604a97e3c08 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -826,7 +826,7 @@ struct FixedOffsetVar<'hir> { } fn is_slice_like<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'_>) -> bool { - let is_slice = match ty.kind { + let is_slice = match ty.kind() { ty::Ref(_, subty, _) => is_slice_like(cx, subty), ty::Slice(..) | ty::Array(..) => true, _ => false, @@ -1375,7 +1375,7 @@ fn is_end_eq_array_len<'tcx>( if_chain! { if let ExprKind::Lit(ref lit) = end.kind; if let ast::LitKind::Int(end_int, _) = lit.node; - if let ty::Array(_, arr_len_const) = indexed_ty.kind; + if let ty::Array(_, arr_len_const) = indexed_ty.kind(); if let Some(arr_len) = arr_len_const.try_eval_usize(cx.tcx, cx.param_env); then { return match limits { @@ -1612,7 +1612,7 @@ fn check_for_loop_over_map_kv<'tcx>( if let PatKind::Tuple(ref pat, _) = pat.kind { if pat.len() == 2 { let arg_span = arg.span; - let (new_pat_span, kind, ty, mutbl) = match cx.typeck_results().expr_ty(arg).kind { + let (new_pat_span, kind, ty, mutbl) = match *cx.typeck_results().expr_ty(arg).kind() { ty::Ref(_, ty, mutbl) => match (&pat[0].kind, &pat[1].kind) { (key, _) if pat_is_wild(key, body) => (pat[1].span, "value", ty, mutbl), (_, value) if pat_is_wild(value, body) => (pat[0].span, "key", ty, Mutability::Not), @@ -1940,7 +1940,7 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { for expr in args { let ty = self.cx.typeck_results().expr_ty_adjusted(expr); self.prefer_mutable = false; - if let ty::Ref(_, _, mutbl) = ty.kind { + if let ty::Ref(_, _, mutbl) = *ty.kind() { if mutbl == Mutability::Mut { self.prefer_mutable = true; } @@ -1952,7 +1952,7 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { let def_id = self.cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap(); for (ty, expr) in self.cx.tcx.fn_sig(def_id).inputs().skip_binder().iter().zip(args) { self.prefer_mutable = false; - if let ty::Ref(_, _, mutbl) = ty.kind { + if let ty::Ref(_, _, mutbl) = *ty.kind() { if mutbl == Mutability::Mut { self.prefer_mutable = true; } @@ -2050,7 +2050,7 @@ fn is_ref_iterable_type(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { fn is_iterable_array<'tcx>(ty: Ty<'tcx>, cx: &LateContext<'tcx>) -> bool { // IntoIterator is currently only implemented for array sizes <= 32 in rustc - match ty.kind { + match ty.kind() { ty::Array(_, n) => n .try_eval_usize(cx.tcx, cx.param_env) .map_or(false, |val| (0..=32).contains(&val)), diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs index 1cd5b201292..6d1c2ffbfbd 100644 --- a/clippy_lints/src/map_clone.rs +++ b/clippy_lints/src/map_clone.rs @@ -70,7 +70,7 @@ impl<'tcx> LateLintPass<'tcx> for MapClone { match closure_expr.kind { hir::ExprKind::Unary(hir::UnOp::UnDeref, ref inner) => { if ident_eq(name, inner) { - if let ty::Ref(.., Mutability::Not) = cx.typeck_results().expr_ty(inner).kind { + if let ty::Ref(.., Mutability::Not) = cx.typeck_results().expr_ty(inner).kind() { lint(cx, e.span, args[0].span, true); } } @@ -80,7 +80,7 @@ impl<'tcx> LateLintPass<'tcx> for MapClone { && match_trait_method(cx, closure_expr, &paths::CLONE_TRAIT) { let obj_ty = cx.typeck_results().expr_ty(&obj[0]); - if let ty::Ref(_, ty, _) = obj_ty.kind { + if let ty::Ref(_, ty, _) = obj_ty.kind() { let copy = is_copy(cx, ty); lint(cx, e.span, args[0].span, copy); } else { diff --git a/clippy_lints/src/map_unit_fn.rs b/clippy_lints/src/map_unit_fn.rs index 198251c58dd..1f9ae8c931a 100644 --- a/clippy_lints/src/map_unit_fn.rs +++ b/clippy_lints/src/map_unit_fn.rs @@ -93,7 +93,7 @@ declare_clippy_lint! { declare_lint_pass!(MapUnit => [OPTION_MAP_UNIT_FN, RESULT_MAP_UNIT_FN]); fn is_unit_type(ty: Ty<'_>) -> bool { - match ty.kind { + match ty.kind() { ty::Tuple(slice) => slice.is_empty(), ty::Never => true, _ => false, @@ -103,7 +103,7 @@ fn is_unit_type(ty: Ty<'_>) -> bool { fn is_unit_function(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { let ty = cx.typeck_results().expr_ty(expr); - if let ty::FnDef(id, _) = ty.kind { + if let ty::FnDef(id, _) = *ty.kind() { if let Some(fn_type) = cx.tcx.fn_sig(id).no_bound_vars() { return is_unit_type(fn_type.output()); } diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index be879dfe28d..7ba7397c29c 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -573,7 +573,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { if let QPath::Resolved(_, ref path) = qpath; if let Some(def_id) = path.res.opt_def_id(); let ty = cx.tcx.type_of(def_id); - if let ty::Adt(def, _) = ty.kind; + if let ty::Adt(def, _) = ty.kind(); if def.is_struct() || def.is_union(); if fields.len() == def.non_enum_variant().fields.len(); @@ -621,7 +621,7 @@ fn check_single_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], exp }; let ty = cx.typeck_results().expr_ty(ex); - if ty.kind != ty::Bool || is_allowed(cx, MATCH_BOOL, ex.hir_id) { + if *ty.kind() != ty::Bool || is_allowed(cx, MATCH_BOOL, ex.hir_id) { check_single_match_single_pattern(cx, ex, arms, expr, els); check_single_match_opt_like(cx, ex, arms, expr, ty, els); } @@ -712,7 +712,7 @@ fn check_single_match_opt_like( fn check_match_bool(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) { // Type of expression is `bool`. - if cx.typeck_results().expr_ty(ex).kind == ty::Bool { + if *cx.typeck_results().expr_ty(ex).kind() == ty::Bool { span_lint_and_then( cx, MATCH_BOOL, @@ -860,7 +860,7 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) // already covered. let mut missing_variants = vec![]; - if let ty::Adt(def, _) = ty.kind { + if let ty::Adt(def, _) = ty.kind() { for variant in &def.variants { missing_variants.push(variant); } @@ -914,7 +914,7 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) let mut message = "wildcard match will miss any future added variants"; - if let ty::Adt(def, _) = ty.kind { + if let ty::Adt(def, _) = ty.kind() { if def.is_variant_list_non_exhaustive() { message = "match on non-exhaustive enum doesn't explicitly match all known variants"; suggestion.push(String::from("_")); @@ -1014,11 +1014,11 @@ fn check_match_as_ref(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], exp let input_ty = cx.typeck_results().expr_ty(ex); let cast = if_chain! { - if let ty::Adt(_, substs) = input_ty.kind; + if let ty::Adt(_, substs) = input_ty.kind(); let input_ty = substs.type_at(0); - if let ty::Adt(_, substs) = output_ty.kind; + if let ty::Adt(_, substs) = output_ty.kind(); let output_ty = substs.type_at(0); - if let ty::Ref(_, output_ty, _) = output_ty.kind; + if let ty::Ref(_, output_ty, _) = *output_ty.kind(); if input_ty != output_ty; then { ".map(|x| x as _)" diff --git a/clippy_lints/src/methods/inefficient_to_string.rs b/clippy_lints/src/methods/inefficient_to_string.rs index e5f815772eb..5dae7efad97 100644 --- a/clippy_lints/src/methods/inefficient_to_string.rs +++ b/clippy_lints/src/methods/inefficient_to_string.rs @@ -46,7 +46,7 @@ pub fn lint<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, arg: &hir::Expr< /// Returns whether `ty` specializes `ToString`. /// Currently, these are `str`, `String`, and `Cow<'_, str>`. fn specializes_tostring(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - if let ty::Str = ty.kind { + if let ty::Str = ty.kind() { return true; } @@ -54,7 +54,7 @@ fn specializes_tostring(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { return true; } - if let ty::Adt(adt, substs) = ty.kind { + if let ty::Adt(adt, substs) = ty.kind() { match_def_path(cx, adt.did, &paths::COW) && substs.type_at(1).is_str() } else { false diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 9996df69470..a7a3d675156 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1534,8 +1534,8 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } } - match self_ty.kind { - ty::Ref(_, ty, _) if ty.kind == ty::Str => { + match self_ty.kind() { + ty::Ref(_, ty, _) if *ty.kind() == ty::Str => { for &(method, pos) in &PATTERN_METHODS { if method_call.ident.name.as_str() == method && args.len() > pos { lint_single_char_pattern(cx, expr, &args[pos]); @@ -1661,7 +1661,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } // if return type is impl trait, check the associated types - if let ty::Opaque(def_id, _) = ret_ty.kind { + if let ty::Opaque(def_id, _) = *ret_ty.kind() { // one of the associated types must be Self for &(predicate, _span) in cx.tcx.predicates_of(def_id).predicates { if let ty::PredicateAtom::Projection(projection_predicate) = predicate.skip_binders() { @@ -1803,7 +1803,7 @@ fn lint_or_fun_call<'tcx>( if path.ident.as_str() == "len" { let ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&args[0])); - match ty.kind { + match ty.kind() { ty::Slice(_) | ty::Array(_, _) => return, _ => (), } @@ -1910,7 +1910,7 @@ fn lint_expect_fun_call( && { let arg_type = cx.typeck_results().expr_ty(&call_args[0]); let base_type = walk_ptrs_ty(arg_type); - base_type.kind == ty::Str || is_type_diagnostic_item(cx, base_type, sym!(string_type)) + *base_type.kind() == ty::Str || is_type_diagnostic_item(cx, base_type, sym!(string_type)) } { &call_args[0] @@ -1931,8 +1931,8 @@ fn lint_expect_fun_call( if is_type_diagnostic_item(cx, arg_ty, sym!(string_type)) { return false; } - if let ty::Ref(_, ty, ..) = arg_ty.kind { - if ty.kind == ty::Str && can_be_static_str(cx, arg) { + if let ty::Ref(_, ty, ..) = arg_ty.kind() { + if *ty.kind() == ty::Str && can_be_static_str(cx, arg) { return false; } }; @@ -1948,7 +1948,7 @@ fn lint_expect_fun_call( if let hir::ExprKind::Path(ref p) = fun.kind { match cx.qpath_res(p, fun.hir_id) { hir::def::Res::Def(hir::def::DefKind::Fn | hir::def::DefKind::AssocFn, def_id) => matches!( - cx.tcx.fn_sig(def_id).output().skip_binder().kind, + cx.tcx.fn_sig(def_id).output().skip_binder().kind(), ty::Ref(ty::ReStatic, ..) ), _ => false, @@ -1962,7 +1962,7 @@ fn lint_expect_fun_call( .type_dependent_def_id(arg.hir_id) .map_or(false, |method_id| { matches!( - cx.tcx.fn_sig(method_id).output().skip_binder().kind, + cx.tcx.fn_sig(method_id).output().skip_binder().kind(), ty::Ref(ty::ReStatic, ..) ) }) @@ -2081,8 +2081,8 @@ fn lint_expect_fun_call( /// Checks for the `CLONE_ON_COPY` lint. fn lint_clone_on_copy(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>, arg_ty: Ty<'_>) { let ty = cx.typeck_results().expr_ty(expr); - if let ty::Ref(_, inner, _) = arg_ty.kind { - if let ty::Ref(_, innermost, _) = inner.kind { + if let ty::Ref(_, inner, _) = arg_ty.kind() { + if let ty::Ref(_, innermost, _) = inner.kind() { span_lint_and_then( cx, CLONE_DOUBLE_REF, @@ -2093,7 +2093,7 @@ fn lint_clone_on_copy(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Exp if let Some(snip) = sugg::Sugg::hir_opt(cx, arg) { let mut ty = innermost; let mut n = 0; - while let ty::Ref(_, inner, _) = ty.kind { + while let ty::Ref(_, inner, _) = ty.kind() { ty = inner; n += 1; } @@ -2172,7 +2172,7 @@ fn lint_clone_on_copy(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Exp fn lint_clone_on_ref_ptr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>) { let obj_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(arg)); - if let ty::Adt(_, subst) = obj_ty.kind { + if let ty::Adt(_, subst) = obj_ty.kind() { let caller_type = if is_type_diagnostic_item(cx, obj_ty, sym::Rc) { "Rc" } else if is_type_diagnostic_item(cx, obj_ty, sym::Arc) { @@ -2202,7 +2202,7 @@ fn lint_string_extend(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::E if let Some(arglists) = method_chain_args(arg, &["chars"]) { let target = &arglists[0][0]; let self_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(target)); - let ref_str = if self_ty.kind == ty::Str { + let ref_str = if *self_ty.kind() == ty::Str { "" } else if is_type_diagnostic_item(cx, self_ty, sym!(string_type)) { "&" @@ -2238,7 +2238,7 @@ fn lint_extend(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_> fn lint_cstring_as_ptr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, source: &hir::Expr<'_>, unwrap: &hir::Expr<'_>) { if_chain! { let source_type = cx.typeck_results().expr_ty(source); - if let ty::Adt(def, substs) = source_type.kind; + if let ty::Adt(def, substs) = source_type.kind(); if cx.tcx.is_diagnostic_item(sym!(result_type), def.did); if match_type(cx, substs.type_at(0), &paths::CSTRING); then { @@ -2412,7 +2412,7 @@ fn lint_iter_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, iter_ } } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(caller_expr), sym!(vec_type)) || matches!( - &walk_ptrs_ty(cx.typeck_results().expr_ty(caller_expr)).kind, + &walk_ptrs_ty(cx.typeck_results().expr_ty(caller_expr)).kind(), ty::Array(_, _) ) { @@ -2579,7 +2579,7 @@ fn derefs_to_slice<'tcx>( ty: Ty<'tcx>, ) -> Option<&'tcx hir::Expr<'tcx>> { fn may_slice<'a>(cx: &LateContext<'a>, ty: Ty<'a>) -> bool { - match ty.kind { + match ty.kind() { ty::Slice(_) => true, ty::Adt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()), ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym!(vec_type)), @@ -2598,7 +2598,7 @@ fn derefs_to_slice<'tcx>( None } } else { - match ty.kind { + match ty.kind() { ty::Slice(_) => Some(expr), ty::Adt(def, _) if def.is_box() && may_slice(cx, ty.boxed_ty()) => Some(expr), ty::Ref(_, inner, _) => { @@ -2692,9 +2692,9 @@ fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map // lint if caller of `.map().flatten()` is an Iterator if match_trait_method(cx, expr, &paths::ITERATOR) { let map_closure_ty = cx.typeck_results().expr_ty(&map_args[1]); - let is_map_to_option = match map_closure_ty.kind { + let is_map_to_option = match map_closure_ty.kind() { ty::Closure(_, _) | ty::FnDef(_, _) | ty::FnPtr(_) => { - let map_closure_sig = match map_closure_ty.kind { + let map_closure_sig = match map_closure_ty.kind() { ty::Closure(_, substs) => substs.as_closure().sig(), _ => map_closure_ty.fn_sig(cx.tcx), }; @@ -3164,7 +3164,7 @@ fn lint_chars_cmp( let mut applicability = Applicability::MachineApplicable; let self_ty = walk_ptrs_ty(cx.typeck_results().expr_ty_adjusted(&args[0][0])); - if self_ty.kind != ty::Str { + if *self_ty.kind() != ty::Str { return false; } @@ -3352,7 +3352,7 @@ fn lint_asref(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, as_re fn ty_has_iter_method(cx: &LateContext<'_>, self_ref_ty: Ty<'_>) -> Option<(&'static str, &'static str)> { has_iter_method(cx, self_ref_ty).map(|ty_name| { - let mutbl = match self_ref_ty.kind { + let mutbl = match self_ref_ty.kind() { ty::Ref(_, _, mutbl) => mutbl, _ => unreachable!(), }; @@ -3404,7 +3404,7 @@ fn lint_maybe_uninit(cx: &LateContext<'_>, expr: &hir::Expr<'_>, outer: &hir::Ex } fn is_maybe_uninit_ty_valid(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - match ty.kind { + match ty.kind() { ty::Array(ref component, _) => is_maybe_uninit_ty_valid(cx, component), ty::Tuple(ref types) => types.types().all(|ty| is_maybe_uninit_ty_valid(cx, ty)), ty::Adt(ref adt, _) => match_def_path(cx, adt.did, &paths::MEM_MAYBEUNINIT), @@ -3531,7 +3531,7 @@ fn lint_option_as_ref_deref<'tcx>( /// Given a `Result` type, return its error type (`E`). fn get_error_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option> { - match ty.kind { + match ty.kind() { ty::Adt(_, substs) if is_type_diagnostic_item(cx, ty, sym!(result_type)) => substs.types().nth(1), _ => None, } @@ -3685,7 +3685,7 @@ impl SelfKind { } else if ty.is_box() { ty.boxed_ty() == parent_ty } else if is_type_diagnostic_item(cx, ty, sym::Rc) || is_type_diagnostic_item(cx, ty, sym::Arc) { - if let ty::Adt(_, substs) = ty.kind { + if let ty::Adt(_, substs) = ty.kind() { substs.types().next().map_or(false, |t| t == parent_ty) } else { false @@ -3696,7 +3696,7 @@ impl SelfKind { } fn matches_ref<'a>(cx: &LateContext<'a>, mutability: hir::Mutability, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool { - if let ty::Ref(_, t, m) = ty.kind { + if let ty::Ref(_, t, m) = *ty.kind() { return m == mutability && t == parent_ty; } @@ -3813,7 +3813,7 @@ fn contains_return(expr: &hir::Expr<'_>) -> bool { fn check_pointer_offset(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { if_chain! { if args.len() == 2; - if let ty::RawPtr(ty::TypeAndMut { ref ty, .. }) = cx.typeck_results().expr_ty(&args[0]).kind; + if let ty::RawPtr(ty::TypeAndMut { ref ty, .. }) = cx.typeck_results().expr_ty(&args[0]).kind(); if let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty)); if layout.is_zst(); then { diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 06f367a8b77..d4a50dd9013 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -561,17 +561,17 @@ fn is_signum(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { } fn is_float(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - let value = &walk_ptrs_ty(cx.typeck_results().expr_ty(expr)).kind; + let value = &walk_ptrs_ty(cx.typeck_results().expr_ty(expr)).kind(); if let ty::Array(arr_ty, _) = value { - return matches!(arr_ty.kind, ty::Float(_)); + return matches!(arr_ty.kind(), ty::Float(_)); }; matches!(value, ty::Float(_)) } fn is_array(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - matches!(&walk_ptrs_ty(cx.typeck_results().expr_ty(expr)).kind, ty::Array(_, _)) + matches!(&walk_ptrs_ty(cx.typeck_results().expr_ty(expr)).kind(), ty::Array(_, _)) } fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) { diff --git a/clippy_lints/src/modulo_arithmetic.rs b/clippy_lints/src/modulo_arithmetic.rs index b1d788b5c68..da3ae1d652f 100644 --- a/clippy_lints/src/modulo_arithmetic.rs +++ b/clippy_lints/src/modulo_arithmetic.rs @@ -38,7 +38,7 @@ struct OperandInfo { fn analyze_operand(operand: &Expr<'_>, cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { match constant(cx, cx.typeck_results(), operand) { - Some((Constant::Int(v), _)) => match cx.typeck_results().expr_ty(expr).kind { + Some((Constant::Int(v), _)) => match *cx.typeck_results().expr_ty(expr).kind() { ty::Int(ity) => { let value = sext(cx.tcx, v, ity); return Some(OperandInfo { diff --git a/clippy_lints/src/mut_key.rs b/clippy_lints/src/mut_key.rs index 9f8f401cc0f..7423107e8f9 100644 --- a/clippy_lints/src/mut_key.rs +++ b/clippy_lints/src/mut_key.rs @@ -97,7 +97,7 @@ fn check_sig<'tcx>(cx: &LateContext<'tcx>, item_hir_id: hir::HirId, decl: &hir:: // generics (because the compiler cannot ensure immutability for unknown types). fn check_ty<'tcx>(cx: &LateContext<'tcx>, span: Span, ty: Ty<'tcx>) { let ty = walk_ptrs_ty(ty); - if let Adt(def, substs) = ty.kind { + if let Adt(def, substs) = ty.kind() { if [&paths::HASHMAP, &paths::BTREEMAP, &paths::HASHSET, &paths::BTREESET] .iter() .any(|path| match_def_path(cx, def.did, &**path)) @@ -109,7 +109,7 @@ fn check_ty<'tcx>(cx: &LateContext<'tcx>, span: Span, ty: Ty<'tcx>) { } fn is_mutable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span) -> bool { - match ty.kind { + match *ty.kind() { RawPtr(TypeAndMut { ty: inner_ty, mutbl }) | Ref(_, inner_ty, mutbl) => { mutbl == hir::Mutability::Mut || is_mutable_type(cx, inner_ty, span) }, diff --git a/clippy_lints/src/mut_mut.rs b/clippy_lints/src/mut_mut.rs index b02e86bca27..2f3cdb894f0 100644 --- a/clippy_lints/src/mut_mut.rs +++ b/clippy_lints/src/mut_mut.rs @@ -69,7 +69,7 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> { expr.span, "generally you want to avoid `&mut &mut _` if possible", ); - } else if let ty::Ref(_, _, hir::Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind { + } else if let ty::Ref(_, _, hir::Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind() { span_lint( self.cx, MUT_MUT, diff --git a/clippy_lints/src/mut_reference.rs b/clippy_lints/src/mut_reference.rs index c506440ed79..3f0b765df15 100644 --- a/clippy_lints/src/mut_reference.rs +++ b/clippy_lints/src/mut_reference.rs @@ -61,11 +61,11 @@ fn check_arguments<'tcx>( name: &str, fn_kind: &str, ) { - match type_definition.kind { + match type_definition.kind() { ty::FnDef(..) | ty::FnPtr(_) => { let parameters = type_definition.fn_sig(cx.tcx).skip_binder().inputs(); for (argument, parameter) in arguments.iter().zip(parameters.iter()) { - match parameter.kind { + match parameter.kind() { ty::Ref(_, _, Mutability::Not) | ty::RawPtr(ty::TypeAndMut { mutbl: Mutability::Not, .. diff --git a/clippy_lints/src/mutable_debug_assertion.rs b/clippy_lints/src/mutable_debug_assertion.rs index 7f529f0404c..cc635c2a202 100644 --- a/clippy_lints/src/mutable_debug_assertion.rs +++ b/clippy_lints/src/mutable_debug_assertion.rs @@ -138,7 +138,7 @@ impl<'a, 'tcx> Visitor<'tcx> for MutArgVisitor<'a, 'tcx> { if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) { if adj .iter() - .any(|a| matches!(a.target.kind, ty::Ref(_, _, Mutability::Mut))) + .any(|a| matches!(a.target.kind(), ty::Ref(_, _, Mutability::Mut))) { self.found = true; return; diff --git a/clippy_lints/src/mutex_atomic.rs b/clippy_lints/src/mutex_atomic.rs index 21efee71269..ea986874291 100644 --- a/clippy_lints/src/mutex_atomic.rs +++ b/clippy_lints/src/mutex_atomic.rs @@ -67,7 +67,7 @@ declare_lint_pass!(Mutex => [MUTEX_ATOMIC, MUTEX_INTEGER]); impl<'tcx> LateLintPass<'tcx> for Mutex { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let ty = cx.typeck_results().expr_ty(expr); - if let ty::Adt(_, subst) = ty.kind { + if let ty::Adt(_, subst) = ty.kind() { if is_type_diagnostic_item(cx, ty, sym!(mutex_type)) { let mutex_param = subst.type_at(0); if let Some(atomic_name) = get_atomic_name(mutex_param) { @@ -76,7 +76,7 @@ impl<'tcx> LateLintPass<'tcx> for Mutex { behavior and not the internal type, consider using `Mutex<()>`", atomic_name ); - match mutex_param.kind { + match *mutex_param.kind() { ty::Uint(t) if t != ast::UintTy::Usize => span_lint(cx, MUTEX_INTEGER, expr.span, &msg), ty::Int(t) if t != ast::IntTy::Isize => span_lint(cx, MUTEX_INTEGER, expr.span, &msg), _ => span_lint(cx, MUTEX_ATOMIC, expr.span, &msg), @@ -88,7 +88,7 @@ impl<'tcx> LateLintPass<'tcx> for Mutex { } fn get_atomic_name(ty: Ty<'_>) -> Option<&'static str> { - match ty.kind { + match ty.kind() { ty::Bool => Some("AtomicBool"), ty::Uint(_) => Some("AtomicUsize"), ty::Int(_) => Some("AtomicIsize"), diff --git a/clippy_lints/src/needless_borrow.rs b/clippy_lints/src/needless_borrow.rs index 9391049c6e8..b71d5496a37 100644 --- a/clippy_lints/src/needless_borrow.rs +++ b/clippy_lints/src/needless_borrow.rs @@ -46,7 +46,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow { return; } if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, ref inner) = e.kind { - if let ty::Ref(..) = cx.typeck_results().expr_ty(inner).kind { + if let ty::Ref(..) = cx.typeck_results().expr_ty(inner).kind() { for adj3 in cx.typeck_results().expr_adjustments(e).windows(3) { if let [Adjustment { kind: Adjust::Deref(_), .. @@ -85,9 +85,9 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow { } if_chain! { if let PatKind::Binding(BindingAnnotation::Ref, .., name, _) = pat.kind; - if let ty::Ref(_, tam, mutbl) = cx.typeck_results().pat_ty(pat).kind; + if let ty::Ref(_, tam, mutbl) = *cx.typeck_results().pat_ty(pat).kind(); if mutbl == Mutability::Not; - if let ty::Ref(_, _, mutbl) = tam.kind; + if let ty::Ref(_, _, mutbl) = *tam.kind(); // only lint immutable refs, because borrowed `&mut T` cannot be moved out if mutbl == Mutability::Not; then { diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 047a78b0878..7e933c674dd 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -194,7 +194,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { // Dereference suggestion let sugg = |diag: &mut DiagnosticBuilder<'_>| { - if let ty::Adt(def, ..) = ty.kind { + if let ty::Adt(def, ..) = ty.kind() { if let Some(span) = cx.tcx.hir().span_if_local(def.did) { if can_type_implement_copy(cx.tcx, cx.param_env, ty).is_ok() { diag.span_help(span, "consider marking this type as `Copy`"); diff --git a/clippy_lints/src/needless_update.rs b/clippy_lints/src/needless_update.rs index ce3f066eff5..98e9078094a 100644 --- a/clippy_lints/src/needless_update.rs +++ b/clippy_lints/src/needless_update.rs @@ -48,7 +48,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessUpdate { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::Struct(_, ref fields, Some(ref base)) = expr.kind { let ty = cx.typeck_results().expr_ty(expr); - if let ty::Adt(def, _) = ty.kind { + if let ty::Adt(def, _) = ty.kind() { if fields.len() == def.non_enum_variant().fields.len() { span_lint( cx, diff --git a/clippy_lints/src/pattern_type_mismatch.rs b/clippy_lints/src/pattern_type_mismatch.rs index ef26fc667b2..5539331d046 100644 --- a/clippy_lints/src/pattern_type_mismatch.rs +++ b/clippy_lints/src/pattern_type_mismatch.rs @@ -187,19 +187,19 @@ fn find_first_mismatch<'tcx>( level: Level, ) -> Option<(Span, Mutability, Level)> { if let PatKind::Ref(ref sub_pat, _) = pat.kind { - if let TyKind::Ref(_, sub_ty, _) = ty.kind { + if let TyKind::Ref(_, sub_ty, _) = ty.kind() { return find_first_mismatch(cx, sub_pat, sub_ty, Level::Lower); } } - if let TyKind::Ref(_, _, mutability) = ty.kind { + if let TyKind::Ref(_, _, mutability) = *ty.kind() { if is_non_ref_pattern(&pat.kind) { return Some((pat.span, mutability, level)); } } if let PatKind::Struct(ref qpath, ref field_pats, _) = pat.kind { - if let TyKind::Adt(ref adt_def, ref substs_ref) = ty.kind { + if let TyKind::Adt(ref adt_def, ref substs_ref) = ty.kind() { if let Some(variant) = get_variant(adt_def, qpath) { let field_defs = &variant.fields; return find_first_mismatch_in_struct(cx, field_pats, field_defs, substs_ref); @@ -208,7 +208,7 @@ fn find_first_mismatch<'tcx>( } if let PatKind::TupleStruct(ref qpath, ref pats, _) = pat.kind { - if let TyKind::Adt(ref adt_def, ref substs_ref) = ty.kind { + if let TyKind::Adt(ref adt_def, ref substs_ref) = ty.kind() { if let Some(variant) = get_variant(adt_def, qpath) { let field_defs = &variant.fields; let ty_iter = field_defs.iter().map(|field_def| field_def.ty(cx.tcx, substs_ref)); @@ -218,7 +218,7 @@ fn find_first_mismatch<'tcx>( } if let PatKind::Tuple(ref pats, _) = pat.kind { - if let TyKind::Tuple(..) = ty.kind { + if let TyKind::Tuple(..) = ty.kind() { return find_first_mismatch_in_tuple(cx, pats, ty.tuple_fields()); } } diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 7dafb1555dc..6b1c848a946 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -180,7 +180,7 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id: } } - if let ty::Ref(_, ty, Mutability::Not) = ty.kind { + if let ty::Ref(_, ty, Mutability::Not) = ty.kind() { if is_type_diagnostic_item(cx, ty, sym!(vec_type)) { let mut ty_snippet = None; if_chain! { diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 7a75fc125d0..cc492917b9d 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -281,7 +281,7 @@ fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { if let Some(higher::Range { start: Some(start), end: Some(end), limits }) = higher::range(expr); let ty = cx.typeck_results().expr_ty(start); - if let ty::Int(_) | ty::Uint(_) = ty.kind; + if let ty::Int(_) | ty::Uint(_) = ty.kind(); if let Some((start_idx, _)) = constant(cx, cx.typeck_results(), start); if let Some((end_idx, _)) = constant(cx, cx.typeck_results(), end); if let Some(ordering) = Constant::partial_cmp(cx.tcx, ty, &start_idx, &end_idx); diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index 7932be0d4b1..4773731e327 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -124,7 +124,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { continue; } - if let ty::Adt(ref def, _) = arg_ty.kind { + if let ty::Adt(ref def, _) = arg_ty.kind() { if match_def_path(cx, def.did, &paths::MEM_MANUALLY_DROP) { continue; } @@ -287,7 +287,7 @@ fn is_call_with_ref_arg<'tcx>( if let mir::TerminatorKind::Call { func, args, destination, .. } = kind; if args.len() == 1; if let mir::Operand::Move(mir::Place { local, .. }) = &args[0]; - if let ty::FnDef(def_id, _) = func.ty(&*mir, cx.tcx).kind; + if let ty::FnDef(def_id, _) = *func.ty(&*mir, cx.tcx).kind(); if let (inner_ty, 1) = walk_ptrs_ty_depth(args[0].ty(&*mir, cx.tcx)); if !is_copy(cx, inner_ty); then { diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index 2610157763a..087d50c90e6 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -165,7 +165,7 @@ fn check_local<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>, bindings: & fn is_binding(cx: &LateContext<'_>, pat_id: HirId) -> bool { let var_ty = cx.typeck_results().node_type_opt(pat_id); - var_ty.map_or(false, |var_ty| !matches!(var_ty.kind, ty::Adt(..))) + var_ty.map_or(false, |var_ty| !matches!(var_ty.kind(), ty::Adt(..))) } fn check_pat<'tcx>( diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index cc39f060fc7..47a73ca9a24 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -196,8 +196,8 @@ fn check_for_slice<'a>(cx: &LateContext<'_>, lhs1: &'a Expr<'_>, lhs2: &'a Expr< if eq_expr_value(cx, lhs1, lhs2) { let ty = walk_ptrs_ty(cx.typeck_results().expr_ty(lhs1)); - if matches!(ty.kind, ty::Slice(_)) - || matches!(ty.kind, ty::Array(_, _)) + if matches!(ty.kind(), ty::Slice(_)) + || matches!(ty.kind(), ty::Array(_, _)) || is_type_diagnostic_item(cx, ty, sym!(vec_type)) || is_type_diagnostic_item(cx, ty, sym!(vecdeque_type)) { diff --git a/clippy_lints/src/to_digit_is_some.rs b/clippy_lints/src/to_digit_is_some.rs index 6750452941f..eeda39bfa20 100644 --- a/clippy_lints/src/to_digit_is_some.rs +++ b/clippy_lints/src/to_digit_is_some.rs @@ -44,7 +44,7 @@ impl<'tcx> LateLintPass<'tcx> for ToDigitIsSome { if let [char_arg, radix_arg] = &**to_digit_args; if to_digits_path.ident.name.as_str() == "to_digit"; let char_arg_ty = cx.typeck_results().expr_ty_adjusted(char_arg); - if char_arg_ty.kind == ty::Char; + if *char_arg_ty.kind() == ty::Char; then { Some((true, char_arg, radix_arg)) } else { diff --git a/clippy_lints/src/transmute.rs b/clippy_lints/src/transmute.rs index 50d9c93f9d4..87c5408c785 100644 --- a/clippy_lints/src/transmute.rs +++ b/clippy_lints/src/transmute.rs @@ -338,7 +338,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { let from_ty = cx.typeck_results().expr_ty(&args[0]); let to_ty = cx.typeck_results().expr_ty(e); - match (&from_ty.kind, &to_ty.kind) { + match (&from_ty.kind(), &to_ty.kind()) { _ if from_ty == to_ty => span_lint( cx, USELESS_TRANSMUTE, @@ -446,7 +446,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { &format!("transmute from a `{}` to a `char`", from_ty), |diag| { let arg = sugg::Sugg::hir(cx, &args[0], ".."); - let arg = if let ty::Int(_) = from_ty.kind { + let arg = if let ty::Int(_) = from_ty.kind() { arg.as_ty(ast::UintTy::U32.name_str()) } else { arg @@ -462,8 +462,8 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { }, (ty::Ref(_, ty_from, from_mutbl), ty::Ref(_, ty_to, to_mutbl)) => { if_chain! { - if let (&ty::Slice(slice_ty), &ty::Str) = (&ty_from.kind, &ty_to.kind); - if let ty::Uint(ast::UintTy::U8) = slice_ty.kind; + if let (&ty::Slice(slice_ty), &ty::Str) = (&ty_from.kind(), &ty_to.kind()); + if let ty::Uint(ast::UintTy::U8) = slice_ty.kind(); if from_mutbl == to_mutbl; then { let postfix = if *from_mutbl == Mutability::Mut { @@ -555,7 +555,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { &format!("transmute from a `{}` to a `{}`", from_ty, to_ty), |diag| { let arg = sugg::Sugg::hir(cx, &args[0], ".."); - let arg = if let ty::Int(int_ty) = from_ty.kind { + let arg = if let ty::Int(int_ty) = from_ty.kind() { arg.as_ty(format!( "u{}", int_ty.bit_width().map_or_else(|| "size".to_string(), |v| v.to_string()) @@ -601,7 +601,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { arg = sugg::Sugg::NonParen(format!("{}.to_bits()", arg.maybe_par()).into()); // cast the result of `to_bits` if `to_ty` is signed - arg = if let ty::Int(int_ty) = to_ty.kind { + arg = if let ty::Int(int_ty) = to_ty.kind() { arg.as_ty(int_ty.name_str().to_string()) } else { arg diff --git a/clippy_lints/src/trivially_copy_pass_by_ref.rs b/clippy_lints/src/trivially_copy_pass_by_ref.rs index 92f42168a1e..1f06d2dbe91 100644 --- a/clippy_lints/src/trivially_copy_pass_by_ref.rs +++ b/clippy_lints/src/trivially_copy_pass_by_ref.rs @@ -83,7 +83,7 @@ impl<'tcx> TriviallyCopyPassByRef { // Use lifetimes to determine if we're returning a reference to the // argument. In that case we can't switch to pass-by-value as the // argument will not live long enough. - let output_lts = match fn_sig.output().kind { + let output_lts = match *fn_sig.output().kind() { ty::Ref(output_lt, _, _) => vec![output_lt], ty::Adt(_, substs) => substs.regions().collect(), _ => vec![], @@ -97,7 +97,7 @@ impl<'tcx> TriviallyCopyPassByRef { } if_chain! { - if let ty::Ref(input_lt, ty, Mutability::Not) = ty.kind; + if let ty::Ref(input_lt, ty, Mutability::Not) = ty.kind(); if !output_lts.contains(&input_lt); if is_copy(cx, ty); if let Some(size) = cx.layout_of(ty).ok().map(|l| l.size.bytes()); diff --git a/clippy_lints/src/try_err.rs b/clippy_lints/src/try_err.rs index a4676e505b6..3e747ec4ad9 100644 --- a/clippy_lints/src/try_err.rs +++ b/clippy_lints/src/try_err.rs @@ -132,7 +132,7 @@ fn find_return_type<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx ExprKind<'_>) -> O /// Extracts the error type from Result. fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { if_chain! { - if let ty::Adt(_, subst) = ty.kind; + if let ty::Adt(_, subst) = ty.kind(); if is_type_diagnostic_item(cx, ty, sym!(result_type)); let err_ty = subst.type_at(1); then { @@ -146,11 +146,11 @@ fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option>. fn poll_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { if_chain! { - if let ty::Adt(def, subst) = ty.kind; + if let ty::Adt(def, subst) = ty.kind(); if match_def_path(cx, def.did, &paths::POLL); let ready_ty = subst.type_at(0); - if let ty::Adt(ready_def, ready_subst) = ready_ty.kind; + if let ty::Adt(ready_def, ready_subst) = ready_ty.kind(); if cx.tcx.is_diagnostic_item(sym!(result_type), ready_def.did); let err_ty = ready_subst.type_at(1); @@ -165,15 +165,15 @@ fn poll_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option< /// Extracts the error type from Poll>>. fn poll_option_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { if_chain! { - if let ty::Adt(def, subst) = ty.kind; + if let ty::Adt(def, subst) = ty.kind(); if match_def_path(cx, def.did, &paths::POLL); let ready_ty = subst.type_at(0); - if let ty::Adt(ready_def, ready_subst) = ready_ty.kind; + if let ty::Adt(ready_def, ready_subst) = ready_ty.kind(); if cx.tcx.is_diagnostic_item(sym!(option_type), ready_def.did); let some_ty = ready_subst.type_at(0); - if let ty::Adt(some_def, some_subst) = some_ty.kind; + if let ty::Adt(some_def, some_subst) = some_ty.kind(); if cx.tcx.is_diagnostic_item(sym!(result_type), some_def.did); let err_ty = some_subst.type_at(1); diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 7e9190bef5e..c82deaa43b2 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -907,7 +907,7 @@ fn is_questionmark_desugar_marked_call(expr: &Expr<'_>) -> bool { } fn is_unit(ty: Ty<'_>) -> bool { - matches!(ty.kind, ty::Tuple(slice) if slice.is_empty()) + matches!(ty.kind(), ty::Tuple(slice) if slice.is_empty()) } fn is_unit_literal(expr: &Expr<'_>) -> bool { @@ -1134,7 +1134,7 @@ declare_clippy_lint! { /// Returns the size in bits of an integral type. /// Will return 0 if the type is not an int or uint variant fn int_ty_to_nbits(typ: Ty<'_>, tcx: TyCtxt<'_>) -> u64 { - match typ.kind { + match typ.kind() { ty::Int(i) => match i { IntTy::Isize => tcx.data_layout.pointer_size.bits(), IntTy::I8 => 8, @@ -1156,7 +1156,7 @@ fn int_ty_to_nbits(typ: Ty<'_>, tcx: TyCtxt<'_>) -> u64 { } fn is_isize_or_usize(typ: Ty<'_>) -> bool { - matches!(typ.kind, ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize)) + matches!(typ.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize)) } fn span_precision_loss_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to_f64: bool) { @@ -1248,7 +1248,7 @@ fn check_loss_of_sign(cx: &LateContext<'_>, expr: &Expr<'_>, op: &Expr<'_>, cast if_chain! { if let Some((const_val, _)) = const_val; if let Constant::Int(n) = const_val; - if let ty::Int(ity) = cast_from.kind; + if let ty::Int(ity) = *cast_from.kind(); if sext(cx.tcx, n, ity) >= 0; then { return @@ -1381,7 +1381,7 @@ declare_lint_pass!(Casts => [ // Check if the given type is either `core::ffi::c_void` or // one of the platform specific `libc::::c_void` of libc. fn is_c_void(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - if let ty::Adt(adt, _) = ty.kind { + if let ty::Adt(adt, _) = ty.kind() { let names = cx.get_def_path(adt.did); if names.is_empty() { @@ -1397,7 +1397,7 @@ fn is_c_void(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { /// Returns the mantissa bits wide of a fp type. /// Will return 0 if the type is not a fp fn fp_ty_mantissa_nbits(typ: Ty<'_>) -> u32 { - match typ.kind { + match typ.kind() { ty::Float(FloatTy::F32) => 23, ty::Float(FloatTy::F64) | ty::Infer(InferTy::FloatVar(_)) => 52, _ => 0, @@ -1437,7 +1437,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { match lit.node { LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => {}, _ => { - if cast_from.kind == cast_to.kind && !in_external_macro(cx.sess(), expr.span) { + if cast_from.kind() == cast_to.kind() && !in_external_macro(cx.sess(), expr.span) { span_lint( cx, UNNECESSARY_CAST, @@ -1470,7 +1470,7 @@ fn lint_numeric_casts<'tcx>( match (cast_from.is_integral(), cast_to.is_integral()) { (true, false) => { let from_nbits = int_ty_to_nbits(cast_from, cx.tcx); - let to_nbits = if let ty::Float(FloatTy::F32) = cast_to.kind { + let to_nbits = if let ty::Float(FloatTy::F32) = cast_to.kind() { 32 } else { 64 @@ -1507,7 +1507,7 @@ fn lint_numeric_casts<'tcx>( check_lossless(cx, expr, cast_expr, cast_from, cast_to); }, (false, false) => { - if let (&ty::Float(FloatTy::F64), &ty::Float(FloatTy::F32)) = (&cast_from.kind, &cast_to.kind) { + if let (&ty::Float(FloatTy::F64), &ty::Float(FloatTy::F32)) = (&cast_from.kind(), &cast_to.kind()) { span_lint( cx, CAST_POSSIBLE_TRUNCATION, @@ -1515,7 +1515,7 @@ fn lint_numeric_casts<'tcx>( "casting `f64` to `f32` may truncate the value", ); } - if let (&ty::Float(FloatTy::F32), &ty::Float(FloatTy::F64)) = (&cast_from.kind, &cast_to.kind) { + if let (&ty::Float(FloatTy::F32), &ty::Float(FloatTy::F64)) = (&cast_from.kind(), &cast_to.kind()) { span_lossless_lint(cx, expr, cast_expr, cast_from, cast_to); } }, @@ -1524,8 +1524,8 @@ fn lint_numeric_casts<'tcx>( fn lint_cast_ptr_alignment<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, cast_from: Ty<'tcx>, cast_to: Ty<'tcx>) { if_chain! { - if let ty::RawPtr(from_ptr_ty) = &cast_from.kind; - if let ty::RawPtr(to_ptr_ty) = &cast_to.kind; + if let ty::RawPtr(from_ptr_ty) = &cast_from.kind(); + if let ty::RawPtr(to_ptr_ty) = &cast_to.kind(); if let Ok(from_layout) = cx.layout_of(from_ptr_ty.ty); if let Ok(to_layout) = cx.layout_of(to_ptr_ty.ty); if from_layout.align.abi < to_layout.align.abi; @@ -1558,11 +1558,11 @@ fn lint_fn_to_numeric_cast( cast_to: Ty<'_>, ) { // We only want to check casts to `ty::Uint` or `ty::Int` - match cast_to.kind { + match cast_to.kind() { ty::Uint(_) | ty::Int(..) => { /* continue on */ }, _ => return, } - match cast_from.kind { + match cast_from.kind() { ty::FnDef(..) | ty::FnPtr(_) => { let mut applicability = Applicability::MaybeIncorrect; let from_snippet = snippet_with_applicability(cx, cast_expr.span, "x", &mut applicability); @@ -1581,7 +1581,7 @@ fn lint_fn_to_numeric_cast( format!("{} as usize", from_snippet), applicability, ); - } else if cast_to.kind != ty::Uint(UintTy::Usize) { + } else if *cast_to.kind() != ty::Uint(UintTy::Usize) { span_lint_and_sugg( cx, FN_TO_NUMERIC_CAST, @@ -1798,7 +1798,7 @@ impl<'tcx> LateLintPass<'tcx> for CharLitAsU8 { if let ExprKind::Cast(e, _) = &expr.kind; if let ExprKind::Lit(l) = &e.kind; if let LitKind::Char(c) = l.node; - if ty::Uint(UintTy::U8) == cx.typeck_results().expr_ty(expr).kind; + if ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(expr).kind(); then { let mut applicability = Applicability::MachineApplicable; let snippet = snippet_with_applicability(cx, e.span, "'x'", &mut applicability); @@ -1937,7 +1937,7 @@ fn detect_extreme_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Op let cv = constant(cx, cx.typeck_results(), expr)?.0; - let which = match (&ty.kind, cv) { + let which = match (ty.kind(), cv) { (&ty::Bool, Constant::Bool(false)) | (&ty::Uint(_), Constant::Int(0)) => Minimum, (&ty::Int(ity), Constant::Int(i)) if i == unsext(cx.tcx, i128::MIN >> (128 - int_bits(cx.tcx, ity)), ity) => { Minimum @@ -2071,7 +2071,7 @@ fn numeric_cast_precast_bounds<'a>(cx: &LateContext<'_>, expr: &'a Expr<'_>) -> if cx.layout_of(pre_cast_ty).ok().map(|l| l.size) == cx.layout_of(cast_ty).ok().map(|l| l.size) { return None; } - match pre_cast_ty.kind { + match pre_cast_ty.kind() { ty::Int(int_ty) => Some(match int_ty { IntTy::I8 => (FullInt::S(i128::from(i8::MIN)), FullInt::S(i128::from(i8::MAX))), IntTy::I16 => (FullInt::S(i128::from(i16::MIN)), FullInt::S(i128::from(i16::MAX))), @@ -2098,7 +2098,7 @@ fn numeric_cast_precast_bounds<'a>(cx: &LateContext<'_>, expr: &'a Expr<'_>) -> fn node_as_const_fullint<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option { let val = constant(cx, cx.typeck_results(), expr)?.0; if let Constant::Int(const_int) = val { - match cx.typeck_results().expr_ty(expr).kind { + match *cx.typeck_results().expr_ty(expr).kind() { ty::Int(ity) => Some(FullInt::S(sext(cx.tcx, const_int, ity))), ty::Uint(_) => Some(FullInt::U(const_int)), _ => None, @@ -2601,7 +2601,7 @@ impl<'tcx> LateLintPass<'tcx> for RefToMut { if let TyKind::Ptr(MutTy { mutbl: Mutability::Mut, .. }) = t.kind; if let ExprKind::Cast(e, t) = &e.kind; if let TyKind::Ptr(MutTy { mutbl: Mutability::Not, .. }) = t.kind; - if let ty::Ref(..) = cx.typeck_results().node_type(e.hir_id).kind; + if let ty::Ref(..) = cx.typeck_results().node_type(e.hir_id).kind(); then { span_lint( cx, diff --git a/clippy_lints/src/unit_return_expecting_ord.rs b/clippy_lints/src/unit_return_expecting_ord.rs index 679aaec9fcd..0d5a5017331 100644 --- a/clippy_lints/src/unit_return_expecting_ord.rs +++ b/clippy_lints/src/unit_return_expecting_ord.rs @@ -110,7 +110,7 @@ fn get_args_to_check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Ve fn check_arg<'tcx>(cx: &LateContext<'tcx>, arg: &'tcx Expr<'tcx>) -> Option<(Span, Option)> { if_chain! { if let ExprKind::Closure(_, _fn_decl, body_id, span, _) = arg.kind; - if let ty::Closure(_def_id, substs) = &cx.typeck_results().node_type(arg.hir_id).kind; + if let ty::Closure(_def_id, substs) = &cx.typeck_results().node_type(arg.hir_id).kind(); let ret_ty = substs.as_closure().sig().output(); let ty = cx.tcx.erase_late_bound_regions(&ret_ty); if ty.is_unit(); diff --git a/clippy_lints/src/unnamed_address.rs b/clippy_lints/src/unnamed_address.rs index 28b393b9f11..9582c162e77 100644 --- a/clippy_lints/src/unnamed_address.rs +++ b/clippy_lints/src/unnamed_address.rs @@ -65,14 +65,14 @@ impl LateLintPass<'_> for UnnamedAddress { } fn is_trait_ptr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - match cx.typeck_results().expr_ty_adjusted(expr).kind { + match cx.typeck_results().expr_ty_adjusted(expr).kind() { ty::RawPtr(ty::TypeAndMut { ty, .. }) => ty.is_trait(), _ => false, } } fn is_fn_def(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - matches!(cx.typeck_results().expr_ty(expr).kind, ty::FnDef(..)) + matches!(cx.typeck_results().expr_ty(expr).kind(), ty::FnDef(..)) } if_chain! { diff --git a/clippy_lints/src/unnecessary_sort_by.rs b/clippy_lints/src/unnecessary_sort_by.rs index 59993d25bb4..8b00d29acb5 100644 --- a/clippy_lints/src/unnecessary_sort_by.rs +++ b/clippy_lints/src/unnecessary_sort_by.rs @@ -230,7 +230,7 @@ fn key_returns_borrow(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { if let Some(def_id) = utils::fn_def_id(cx, expr) { let output = cx.tcx.fn_sig(def_id).output(); let ty = output.skip_binder(); - return matches!(ty.kind, ty::Ref(..)) + return matches!(ty.kind(), ty::Ref(..)) || ty.walk().any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_))); } diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 4ab2b5e796d..615440e15f3 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -107,7 +107,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(&args[0]); if is_type_diagnostic_item(cx, a, sym!(result_type)); - if let ty::Adt(_, substs) = a.kind; + if let ty::Adt(_, substs) = a.kind(); if let Some(a_type) = substs.types().next(); if TyS::same_type(a_type, b); @@ -137,7 +137,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { if_chain! { if match_def_path(cx, def_id, &paths::TRY_FROM); if is_type_diagnostic_item(cx, a, sym!(result_type)); - if let ty::Adt(_, substs) = a.kind; + if let ty::Adt(_, substs) = a.kind(); if let Some(a_type) = substs.types().next(); if TyS::same_type(a_type, b); diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 82005257115..45add9ab284 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -129,7 +129,7 @@ pub fn is_wild<'tcx>(pat: &impl std::ops::Deref>) -> bool { /// Checks if type is struct, enum or union type with the given def path. pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool { - match ty.kind { + match ty.kind() { ty::Adt(adt, _) => match_def_path(cx, adt.did, path), _ => false, } @@ -137,7 +137,7 @@ pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool { /// Checks if the type is equal to a diagnostic item pub fn is_type_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool { - match ty.kind { + match ty.kind() { ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did), _ => false, } @@ -145,7 +145,7 @@ pub fn is_type_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symb /// Checks if the type is equal to a lang item pub fn is_type_lang_item(cx: &LateContext<'_>, ty: Ty<'_>, lang_item: hir::LangItem) -> bool { - match ty.kind { + match ty.kind() { ty::Adt(adt, _) => cx.tcx.lang_items().require(lang_item).unwrap() == adt.did, _ => false, } @@ -754,7 +754,7 @@ pub fn walk_ptrs_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> { /// Returns the base type for references and raw pointers. pub fn walk_ptrs_ty(ty: Ty<'_>) -> Ty<'_> { - match ty.kind { + match ty.kind() { ty::Ref(_, ty, _) => walk_ptrs_ty(ty), _ => ty, } @@ -764,7 +764,7 @@ pub fn walk_ptrs_ty(ty: Ty<'_>) -> Ty<'_> { /// depth. pub fn walk_ptrs_ty_depth(ty: Ty<'_>) -> (Ty<'_>, usize) { fn inner(ty: Ty<'_>, depth: usize) -> (Ty<'_>, usize) { - match ty.kind { + match ty.kind() { ty::Ref(_, ty, _) => inner(ty, depth + 1), _ => (ty, depth), } @@ -877,7 +877,7 @@ pub fn contains_ty(ty: Ty<'_>, other_ty: Ty<'_>) -> bool { /// Returns `true` if the given type is an `unsafe` function. pub fn type_is_unsafe_function<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { - match ty.kind { + match ty.kind() { ty::FnDef(..) | ty::FnPtr(_) => ty.fn_sig(cx.tcx).unsafety() == Unsafety::Unsafe, _ => false, } @@ -942,7 +942,7 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, pats.iter().map(|pat| &**pat)) }, PatKind::Slice(ref head, ref middle, ref tail) => { - match &cx.typeck_results().node_type(pat.hir_id).kind { + match &cx.typeck_results().node_type(pat.hir_id).kind() { ty::Slice(..) => { // [..] is the only irrefutable slice pattern. !head.is_empty() || middle.is_none() || !tail.is_empty() @@ -1156,12 +1156,12 @@ pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option< &paths::RECEIVER, ]; - let ty_to_check = match probably_ref_ty.kind { + let ty_to_check = match probably_ref_ty.kind() { ty::Ref(_, ty_to_check, _) => ty_to_check, _ => probably_ref_ty, }; - let def_id = match ty_to_check.kind { + let def_id = match ty_to_check.kind() { ty::Array(..) => return Some("array"), ty::Slice(..) => return Some("slice"), ty::Adt(adt, _) => adt.did, @@ -1277,7 +1277,7 @@ pub fn must_use_attr(attrs: &[Attribute]) -> Option<&Attribute> { // Returns whether the type has #[must_use] attribute pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { - match ty.kind { + match ty.kind() { ty::Adt(ref adt, _) => must_use_attr(&cx.tcx.get_attrs(adt.did)).is_some(), ty::Foreign(ref did) => must_use_attr(&cx.tcx.get_attrs(*did)).is_some(), ty::Slice(ref ty) @@ -1409,9 +1409,9 @@ pub fn run_lints(cx: &LateContext<'_>, lints: &[&'static Lint], id: HirId) -> bo /// Returns true iff the given type is a primitive (a bool or char, any integer or floating-point /// number type, a str, or an array, slice, or tuple of those types). pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool { - match ty.kind { + match ty.kind() { ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => true, - ty::Ref(_, inner, _) if inner.kind == ty::Str => true, + ty::Ref(_, inner, _) if *inner.kind() == ty::Str => true, ty::Array(inner_type, _) | ty::Slice(inner_type) => is_recursively_primitive_type(inner_type), ty::Tuple(inner_types) => inner_types.types().all(is_recursively_primitive_type), _ => false, @@ -1423,24 +1423,23 @@ pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool { /// `is_recursively_primitive_type` function) and None otherwise. pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { let expr_type = cx.typeck_results().expr_ty_adjusted(expr); - let expr_kind = &expr_type.kind; + let expr_kind = expr_type.kind(); let is_primitive = match expr_kind { - ty::Slice(ref element_type) - | ty::Ref( - _, - ty::TyS { - kind: ty::Slice(ref element_type), - .. - }, - _, - ) => is_recursively_primitive_type(element_type), + ty::Slice(element_type) => is_recursively_primitive_type(element_type), + ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &ty::Slice(_)) => { + if let ty::Slice(element_type) = inner_ty.kind() { + is_recursively_primitive_type(element_type) + } else { + unreachable!() + } + } _ => false, }; if is_primitive { // if we have wrappers like Array, Slice or Tuple, print these // and get the type enclosed in the slice ref - match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind { + match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind() { ty::Slice(..) => return Some("slice".into()), ty::Array(..) => return Some("array".into()), ty::Tuple(..) => return Some("tuple".into()), diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index 84e907d7125..149cceb39dd 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -44,8 +44,8 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // search for `&vec![_]` expressions where the adjusted type is `&[_]` if_chain! { - if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(expr).kind; - if let ty::Slice(..) = ty.kind; + if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(expr).kind(); + if let ty::Slice(..) = ty.kind(); if let ExprKind::AddrOf(BorrowKind::Ref, _, ref addressee) = expr.kind; if let Some(vec_args) = higher::vec_macro(cx, addressee); then { @@ -127,7 +127,7 @@ fn size_of(cx: &LateContext<'_>, expr: &Expr<'_>) -> u64 { /// Returns the item type of the vector (i.e., the `T` in `Vec`). fn vec_type(ty: Ty<'_>) -> Ty<'_> { - if let ty::Adt(_, substs) = ty.kind { + if let ty::Adt(_, substs) = ty.kind() { substs.type_at(0) } else { panic!("The type of `vec!` is a not a struct?"); From 4286d9c87af269e46203fc1ca8108669d00e7c63 Mon Sep 17 00:00:00 2001 From: LeSeulArtichaut Date: Thu, 6 Aug 2020 17:49:46 +0200 Subject: [PATCH 31/49] ty.flags -> ty.flags() --- clippy_lints/src/non_copy_const.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index f1df634701d..73eabd4207e 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -128,7 +128,7 @@ fn verify_ty_bound<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, source: Source) { diag.span_label(const_kw_span, "make this a static item (maybe with lazy_static)"); }, Source::Assoc { ty: ty_span, .. } => { - if ty.flags.intersects(TypeFlags::HAS_FREE_LOCAL_NAMES) { + if ty.flags().intersects(TypeFlags::HAS_FREE_LOCAL_NAMES) { diag.span_label(ty_span, &format!("consider requiring `{}` to be `Copy`", ty)); } }, From 7bcf40a13d90952d9a59ed547832155439e3bcf7 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Fri, 4 Sep 2020 23:30:06 +0200 Subject: [PATCH 32/49] Fix fallout from rustup --- clippy_lints/src/default_trait_access.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/default_trait_access.rs b/clippy_lints/src/default_trait_access.rs index 320a2a257bd..3048436d9a7 100644 --- a/clippy_lints/src/default_trait_access.rs +++ b/clippy_lints/src/default_trait_access.rs @@ -42,7 +42,7 @@ impl<'tcx> LateLintPass<'tcx> for DefaultTraitAccess { if let QPath::Resolved(None, _path) = qpath; then { let expr_ty = cx.typeck_results().expr_ty(expr); - if let ty::Adt(def, ..) = expr_ty.kind { + if let ty::Adt(def, ..) = expr_ty.kind() { // TODO: Work out a way to put "whatever the imported way of referencing // this type in this file" rather than a fully-qualified type. let replacement = format!("{}::default()", cx.tcx.def_path_str(def.did)); From 2905fff93659d17f1c6b1cf270b5731e04ebe46d Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Fri, 4 Sep 2020 23:30:55 +0200 Subject: [PATCH 33/49] Run cargo dev fmt --- clippy_lints/src/utils/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 96e9d0f32f8..bea0a4d2459 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -1432,7 +1432,7 @@ pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option false, }; From 04ba07bdc3c82aa17297e3e91fdc884983be7cad Mon Sep 17 00:00:00 2001 From: Ryan1729 Date: Fri, 4 Sep 2020 19:26:35 -0600 Subject: [PATCH 34/49] add line_count and max_lines to too_many_lines lint message --- clippy_lints/src/functions.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/functions.rs b/clippy_lints/src/functions.rs index 89fde1d509d..50b39cf4ea7 100644 --- a/clippy_lints/src/functions.rs +++ b/clippy_lints/src/functions.rs @@ -374,7 +374,12 @@ impl<'tcx> Functions { } if line_count > self.max_lines { - span_lint(cx, TOO_MANY_LINES, span, "this function has a large number of lines") + span_lint( + cx, + TOO_MANY_LINES, + span, + &format!("this function has too many lines ({}/{})", line_count, self.max_lines), + ) } } From db16fa26cef82fff84751dcad7940fd9b819a169 Mon Sep 17 00:00:00 2001 From: Ryan1729 Date: Fri, 4 Sep 2020 20:16:02 -0600 Subject: [PATCH 35/49] run tests/ui/update-all-references.sh --- tests/ui/functions_maxlines.stderr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/functions_maxlines.stderr b/tests/ui/functions_maxlines.stderr index c640c82d6d7..dc6c8ba2f15 100644 --- a/tests/ui/functions_maxlines.stderr +++ b/tests/ui/functions_maxlines.stderr @@ -1,4 +1,4 @@ -error: this function has a large number of lines +error: this function has too many lines (102/100) --> $DIR/functions_maxlines.rs:58:1 | LL | / fn bad_lines() { From 9e7ce9d3851426f59ec98eabee7f113e4fd18198 Mon Sep 17 00:00:00 2001 From: Ryan1729 Date: Fri, 4 Sep 2020 21:12:16 -0600 Subject: [PATCH 36/49] run the specific script suggested by the error message ``` ./tests/ui-toml/update-references.sh './target/debug/test_build_base' 'functions_maxlines/test.rs' ``` --- tests/ui-toml/functions_maxlines/test.stderr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ui-toml/functions_maxlines/test.stderr b/tests/ui-toml/functions_maxlines/test.stderr index fb12257021a..a27ce945ca5 100644 --- a/tests/ui-toml/functions_maxlines/test.stderr +++ b/tests/ui-toml/functions_maxlines/test.stderr @@ -1,4 +1,4 @@ -error: this function has a large number of lines +error: this function has too many lines (2/1) --> $DIR/test.rs:18:1 | LL | / fn too_many_lines() { @@ -9,7 +9,7 @@ LL | | } | = note: `-D clippy::too-many-lines` implied by `-D warnings` -error: this function has a large number of lines +error: this function has too many lines (2/1) --> $DIR/test.rs:38:1 | LL | / fn comment_before_code() { From 96b31a5b36ed06b6781804d3384a3a84a52b8ce6 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Sun, 6 Sep 2020 00:02:35 +0900 Subject: [PATCH 37/49] Fix FP when coercion kicks in --- clippy_lints/src/loops.rs | 3 ++- tests/ui/same_item_push.rs | 11 +++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 25345f8fa31..b65ae15edcd 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1140,7 +1140,8 @@ fn detect_same_item_push<'tcx>( walk_expr(&mut same_item_push_visitor, body); if same_item_push_visitor.should_lint { if let Some((vec, pushed_item)) = same_item_push_visitor.vec_push { - let ty = cx.typeck_results().expr_ty(pushed_item); + let vec_ty = cx.typeck_results().expr_ty(vec); + let ty = vec_ty.walk().nth(1).unwrap().expect_ty(); if cx .tcx .lang_items() diff --git a/tests/ui/same_item_push.rs b/tests/ui/same_item_push.rs index 0903a873826..0928820892b 100644 --- a/tests/ui/same_item_push.rs +++ b/tests/ui/same_item_push.rs @@ -100,4 +100,15 @@ fn main() { for _ in 0..10 { vec14.push(std::fs::File::open("foobar").unwrap()); } + // Fix #5979 + #[derive(Clone)] + struct S {} + + trait T {} + impl T for S {} + + let mut vec15: Vec> = Vec::new(); + for _ in 0..10 { + vec15.push(Box::new(S {})); + } } From 9fa9208bd5c3725f98dd88f761956c01ce5fd527 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Fri, 4 Sep 2020 11:56:54 +0200 Subject: [PATCH 38/49] Restrict unnecessary_sort_by to non-ref copy types --- clippy_lints/src/unnecessary_sort_by.rs | 13 ++++++-- tests/ui/unnecessary_sort_by.fixed | 42 ++++++++++++++++++++++--- tests/ui/unnecessary_sort_by.rs | 42 ++++++++++++++++++++++--- 3 files changed, 87 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/unnecessary_sort_by.rs b/clippy_lints/src/unnecessary_sort_by.rs index 8b00d29acb5..9b6a9075a29 100644 --- a/clippy_lints/src/unnecessary_sort_by.rs +++ b/clippy_lints/src/unnecessary_sort_by.rs @@ -1,5 +1,4 @@ use crate::utils; -use crate::utils::paths; use crate::utils::sugg::Sugg; use if_chain::if_chain; use rustc_errors::Applicability; @@ -171,12 +170,22 @@ fn mirrored_exprs( } fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { + // NOTE: Vectors of references are not supported. In order to avoid hitting https://github.com/rust-lang/rust/issues/34162, + // (different unnamed lifetimes for closure arg and return type) we need to make sure the suggested + // closure parameter is not a reference in case we suggest `Reverse`. Trying to destructure more + // than one level of references would add some extra complexity as we would have to compensate + // in the closure body. + if_chain! { if let ExprKind::MethodCall(name_ident, _, args, _) = &expr.kind; if let name = name_ident.ident.name.to_ident_string(); if name == "sort_by" || name == "sort_unstable_by"; if let [vec, Expr { kind: ExprKind::Closure(_, _, closure_body_id, _, _), .. }] = args; - if utils::match_type(cx, &cx.typeck_results().expr_ty(vec), &paths::VEC); + let vec_ty = cx.typeck_results().expr_ty(vec); + if utils::is_type_diagnostic_item(cx, vec_ty, sym!(vec_type)); + let ty = vec_ty.walk().nth(1).unwrap().expect_ty(); // T in Vec + if !matches!(&ty.kind(), ty::Ref(..)); + if utils::is_copy(cx, ty); if let closure_body = cx.tcx.hir().body(*closure_body_id); if let &[ Param { pat: Pat { kind: PatKind::Binding(_, _, left_ident, _), .. }, ..}, diff --git a/tests/ui/unnecessary_sort_by.fixed b/tests/ui/unnecessary_sort_by.fixed index 31c2ba0f9c5..ad0d0387db0 100644 --- a/tests/ui/unnecessary_sort_by.fixed +++ b/tests/ui/unnecessary_sort_by.fixed @@ -25,17 +25,25 @@ fn unnecessary_sort_by() { vec.sort_by(|_, b| b.cmp(&5)); vec.sort_by(|_, b| b.cmp(c)); vec.sort_unstable_by(|a, _| a.cmp(c)); + + // Ignore vectors of references + let mut vec: Vec<&&&isize> = vec![&&&3, &&&6, &&&1, &&&2, &&&5]; + vec.sort_by(|a, b| (***a).abs().cmp(&(***b).abs())); + vec.sort_unstable_by(|a, b| (***a).abs().cmp(&(***b).abs())); + vec.sort_by(|a, b| b.cmp(a)); + vec.sort_unstable_by(|a, b| b.cmp(a)); } -// Should not be linted to avoid hitting https://github.com/rust-lang/rust/issues/34162 +// Do not suggest returning a reference to the closure parameter of `Vec::sort_by_key` mod issue_5754 { - struct Test(String); + #[derive(Clone, Copy)] + struct Test(usize); #[derive(PartialOrd, Ord, PartialEq, Eq)] - struct Wrapper<'a>(&'a str); + struct Wrapper<'a>(&'a usize); impl Test { - fn name(&self) -> &str { + fn name(&self) -> &usize { &self.0 } @@ -60,7 +68,33 @@ mod issue_5754 { } } +// `Vec::sort_by_key` closure parameter is `F: FnMut(&T) -> K` +// The suggestion is destructuring T and we know T is not a reference, so test that non-Copy T are +// not linted. +mod issue_6001 { + struct Test(String); + + impl Test { + // Return an owned type so that we don't hit the fix for 5754 + fn name(&self) -> String { + self.0.clone() + } + } + + pub fn test() { + let mut args: Vec = vec![]; + + // Forward + args.sort_by(|a, b| a.name().cmp(&b.name())); + args.sort_unstable_by(|a, b| a.name().cmp(&b.name())); + // Reverse + args.sort_by(|a, b| b.name().cmp(&a.name())); + args.sort_unstable_by(|a, b| b.name().cmp(&a.name())); + } +} + fn main() { unnecessary_sort_by(); issue_5754::test(); + issue_6001::test(); } diff --git a/tests/ui/unnecessary_sort_by.rs b/tests/ui/unnecessary_sort_by.rs index a3c8ae468ed..9746f6e6849 100644 --- a/tests/ui/unnecessary_sort_by.rs +++ b/tests/ui/unnecessary_sort_by.rs @@ -25,17 +25,25 @@ fn unnecessary_sort_by() { vec.sort_by(|_, b| b.cmp(&5)); vec.sort_by(|_, b| b.cmp(c)); vec.sort_unstable_by(|a, _| a.cmp(c)); + + // Ignore vectors of references + let mut vec: Vec<&&&isize> = vec![&&&3, &&&6, &&&1, &&&2, &&&5]; + vec.sort_by(|a, b| (***a).abs().cmp(&(***b).abs())); + vec.sort_unstable_by(|a, b| (***a).abs().cmp(&(***b).abs())); + vec.sort_by(|a, b| b.cmp(a)); + vec.sort_unstable_by(|a, b| b.cmp(a)); } -// Should not be linted to avoid hitting https://github.com/rust-lang/rust/issues/34162 +// Do not suggest returning a reference to the closure parameter of `Vec::sort_by_key` mod issue_5754 { - struct Test(String); + #[derive(Clone, Copy)] + struct Test(usize); #[derive(PartialOrd, Ord, PartialEq, Eq)] - struct Wrapper<'a>(&'a str); + struct Wrapper<'a>(&'a usize); impl Test { - fn name(&self) -> &str { + fn name(&self) -> &usize { &self.0 } @@ -60,7 +68,33 @@ mod issue_5754 { } } +// `Vec::sort_by_key` closure parameter is `F: FnMut(&T) -> K` +// The suggestion is destructuring T and we know T is not a reference, so test that non-Copy T are +// not linted. +mod issue_6001 { + struct Test(String); + + impl Test { + // Return an owned type so that we don't hit the fix for 5754 + fn name(&self) -> String { + self.0.clone() + } + } + + pub fn test() { + let mut args: Vec = vec![]; + + // Forward + args.sort_by(|a, b| a.name().cmp(&b.name())); + args.sort_unstable_by(|a, b| a.name().cmp(&b.name())); + // Reverse + args.sort_by(|a, b| b.name().cmp(&a.name())); + args.sort_unstable_by(|a, b| b.name().cmp(&a.name())); + } +} + fn main() { unnecessary_sort_by(); issue_5754::test(); + issue_6001::test(); } From 3d30ef7818b9ed562f2c48f3d6d15d90253ae732 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Mon, 7 Sep 2020 22:17:31 +0900 Subject: [PATCH 39/49] Restrict `same_item_push` to suppress false positives It emits a lint when the pushed item is a literal, a constant and an immutable binding that are initialized with those. --- clippy_lints/src/loops.rs | 83 ++++++++++++++++++++++++++-------- tests/ui/same_item_push.rs | 13 ++++++ tests/ui/same_item_push.stderr | 34 ++++++++------ 3 files changed, 97 insertions(+), 33 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index f13e369907b..f417e3a0caf 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1153,27 +1153,70 @@ fn detect_same_item_push<'tcx>( let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); if let ExprKind::Path(ref qpath) = pushed_item.kind { - if_chain! { - if let Res::Local(hir_id) = qpath_res(cx, qpath, pushed_item.hir_id); - let node = cx.tcx.hir().get(hir_id); - if let Node::Binding(pat) = node; - if let PatKind::Binding(bind_ann, ..) = pat.kind; - if !matches!(bind_ann, BindingAnnotation::RefMut | BindingAnnotation::Mutable); - then { - span_lint_and_help( - cx, - SAME_ITEM_PUSH, - vec.span, - "it looks like the same item is being pushed into this Vec", - None, - &format!( - "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", - item_str, vec_str, item_str - ), - ) - } + match qpath_res(cx, qpath, pushed_item.hir_id) { + // immutable bindings that are initialized with literal or constant + Res::Local(hir_id) => { + if_chain! { + let node = cx.tcx.hir().get(hir_id); + if let Node::Binding(pat) = node; + if let PatKind::Binding(bind_ann, ..) = pat.kind; + if !matches!(bind_ann, BindingAnnotation::RefMut | BindingAnnotation::Mutable); + let parent_node = cx.tcx.hir().get_parent_node(hir_id); + if let Some(Node::Local(parent_let_expr)) = cx.tcx.hir().find(parent_node); + if let rustc_hir::Local { init: Some(init), .. } = parent_let_expr; + then { + match init.kind { + // immutable bindings that are initialized with literal + ExprKind::Lit(..) => { + span_lint_and_help( + cx, + SAME_ITEM_PUSH, + vec.span, + "it looks like the same item is being pushed into this Vec", + None, + &format!( + "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", + item_str, vec_str, item_str + ), + ) + }, + // immutable bindings that are initialized with constant + ExprKind::Path(ref path) => { + if let Res::Def(DefKind::Const, ..) = qpath_res(cx, path, init.hir_id) { + span_lint_and_help( + cx, + SAME_ITEM_PUSH, + vec.span, + "it looks like the same item is being pushed into this Vec", + None, + &format!( + "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", + item_str, vec_str, item_str + ), + ) + } + } + _ => {}, + } + } + } + }, + // constant + Res::Def(DefKind::Const, ..) => span_lint_and_help( + cx, + SAME_ITEM_PUSH, + vec.span, + "it looks like the same item is being pushed into this Vec", + None, + &format!( + "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", + item_str, vec_str, item_str + ), + ), + _ => {}, } - } else if mutated_variables(pushed_item, cx).map_or(false, |mutvars| mutvars.is_empty()) { + } else if let ExprKind::Lit(..) = pushed_item.kind { + // literal span_lint_and_help( cx, SAME_ITEM_PUSH, diff --git a/tests/ui/same_item_push.rs b/tests/ui/same_item_push.rs index 0928820892b..bd4792c4a76 100644 --- a/tests/ui/same_item_push.rs +++ b/tests/ui/same_item_push.rs @@ -1,5 +1,7 @@ #![warn(clippy::same_item_push)] +const VALUE: u8 = 7; + fn mutate_increment(x: &mut u8) -> u8 { *x += 1; *x @@ -111,4 +113,15 @@ fn main() { for _ in 0..10 { vec15.push(Box::new(S {})); } + + let mut vec16 = Vec::new(); + for _ in 0..20 { + vec16.push(VALUE); + } + + let mut vec17 = Vec::new(); + let item = VALUE; + for _ in 0..20 { + vec17.push(item); + } } diff --git a/tests/ui/same_item_push.stderr b/tests/ui/same_item_push.stderr index ddc5d48cd41..4896479791a 100644 --- a/tests/ui/same_item_push.stderr +++ b/tests/ui/same_item_push.stderr @@ -1,22 +1,14 @@ error: it looks like the same item is being pushed into this Vec - --> $DIR/same_item_push.rs:16:9 - | -LL | spaces.push(vec![b' ']); - | ^^^^^^ - | - = note: `-D clippy::same-item-push` implied by `-D warnings` - = help: try using vec![vec![b' '];SIZE] or spaces.resize(NEW_SIZE, vec![b' ']) - -error: it looks like the same item is being pushed into this Vec - --> $DIR/same_item_push.rs:22:9 + --> $DIR/same_item_push.rs:24:9 | LL | vec2.push(item); | ^^^^ | + = note: `-D clippy::same-item-push` implied by `-D warnings` = help: try using vec![item;SIZE] or vec2.resize(NEW_SIZE, item) error: it looks like the same item is being pushed into this Vec - --> $DIR/same_item_push.rs:28:9 + --> $DIR/same_item_push.rs:30:9 | LL | vec3.push(item); | ^^^^ @@ -24,12 +16,28 @@ LL | vec3.push(item); = help: try using vec![item;SIZE] or vec3.resize(NEW_SIZE, item) error: it looks like the same item is being pushed into this Vec - --> $DIR/same_item_push.rs:33:9 + --> $DIR/same_item_push.rs:35:9 | LL | vec4.push(13); | ^^^^ | = help: try using vec![13;SIZE] or vec4.resize(NEW_SIZE, 13) -error: aborting due to 4 previous errors +error: it looks like the same item is being pushed into this Vec + --> $DIR/same_item_push.rs:119:9 + | +LL | vec16.push(VALUE); + | ^^^^^ + | + = help: try using vec![VALUE;SIZE] or vec16.resize(NEW_SIZE, VALUE) + +error: it looks like the same item is being pushed into this Vec + --> $DIR/same_item_push.rs:125:9 + | +LL | vec17.push(item); + | ^^^^^ + | + = help: try using vec![item;SIZE] or vec17.resize(NEW_SIZE, item) + +error: aborting due to 5 previous errors From 619ca76731e7209288fd3ebf1ae243c050486c12 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 8 Sep 2020 08:25:31 +0900 Subject: [PATCH 40/49] Refactoring: use inner function --- clippy_lints/src/loops.rs | 69 ++++++++++++--------------------------- 1 file changed, 21 insertions(+), 48 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index f417e3a0caf..c27acdd2236 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1150,8 +1150,6 @@ fn detect_same_item_push<'tcx>( { // Make sure that the push does not involve possibly mutating values if let PatKind::Wild = pat.kind { - let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); - let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); if let ExprKind::Path(ref qpath) = pushed_item.kind { match qpath_res(cx, qpath, pushed_item.hir_id) { // immutable bindings that are initialized with literal or constant @@ -1167,33 +1165,11 @@ fn detect_same_item_push<'tcx>( then { match init.kind { // immutable bindings that are initialized with literal - ExprKind::Lit(..) => { - span_lint_and_help( - cx, - SAME_ITEM_PUSH, - vec.span, - "it looks like the same item is being pushed into this Vec", - None, - &format!( - "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", - item_str, vec_str, item_str - ), - ) - }, + ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item), // immutable bindings that are initialized with constant ExprKind::Path(ref path) => { if let Res::Def(DefKind::Const, ..) = qpath_res(cx, path, init.hir_id) { - span_lint_and_help( - cx, - SAME_ITEM_PUSH, - vec.span, - "it looks like the same item is being pushed into this Vec", - None, - &format!( - "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", - item_str, vec_str, item_str - ), - ) + emit_lint(cx, vec, pushed_item); } } _ => {}, @@ -1202,37 +1178,34 @@ fn detect_same_item_push<'tcx>( } }, // constant - Res::Def(DefKind::Const, ..) => span_lint_and_help( - cx, - SAME_ITEM_PUSH, - vec.span, - "it looks like the same item is being pushed into this Vec", - None, - &format!( - "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", - item_str, vec_str, item_str - ), - ), + Res::Def(DefKind::Const, ..) => emit_lint(cx, vec, pushed_item), _ => {}, } } else if let ExprKind::Lit(..) = pushed_item.kind { // literal - span_lint_and_help( - cx, - SAME_ITEM_PUSH, - vec.span, - "it looks like the same item is being pushed into this Vec", - None, - &format!( - "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", - item_str, vec_str, item_str - ), - ) + emit_lint(cx, vec, pushed_item); } } } } } + + fn emit_lint(cx: &LateContext<'_>, vec: &Expr<'_>, pushed_item: &Expr<'_>) { + let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); + let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); + + span_lint_and_help( + cx, + SAME_ITEM_PUSH, + vec.span, + "it looks like the same item is being pushed into this Vec", + None, + &format!( + "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", + item_str, vec_str, item_str + ), + ) + } } /// Checks for looping over a range and then indexing a sequence with it. From 5d085ad011a602e004e7d33fa0b9074c7dab36fc Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 8 Sep 2020 08:34:51 +0900 Subject: [PATCH 41/49] Some refactoring --- clippy_lints/src/loops.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index c27acdd2236..c59185bd7bd 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1131,6 +1131,10 @@ fn detect_same_item_push<'tcx>( body: &'tcx Expr<'_>, _: &'tcx Expr<'_>, ) { + if !matches!(pat.kind, PatKind::Wild) { + return; + } + // Determine whether it is safe to lint the body let mut same_item_push_visitor = SameItemPushVisitor { should_lint: true, @@ -1149,8 +1153,8 @@ fn detect_same_item_push<'tcx>( .map_or(false, |id| implements_trait(cx, ty, id, &[])) { // Make sure that the push does not involve possibly mutating values - if let PatKind::Wild = pat.kind { - if let ExprKind::Path(ref qpath) = pushed_item.kind { + match pushed_item.kind { + ExprKind::Path(ref qpath) => { match qpath_res(cx, qpath, pushed_item.hir_id) { // immutable bindings that are initialized with literal or constant Res::Local(hir_id) => { @@ -1161,7 +1165,7 @@ fn detect_same_item_push<'tcx>( if !matches!(bind_ann, BindingAnnotation::RefMut | BindingAnnotation::Mutable); let parent_node = cx.tcx.hir().get_parent_node(hir_id); if let Some(Node::Local(parent_let_expr)) = cx.tcx.hir().find(parent_node); - if let rustc_hir::Local { init: Some(init), .. } = parent_let_expr; + if let Some(init) = parent_let_expr.init; then { match init.kind { // immutable bindings that are initialized with literal @@ -1181,10 +1185,9 @@ fn detect_same_item_push<'tcx>( Res::Def(DefKind::Const, ..) => emit_lint(cx, vec, pushed_item), _ => {}, } - } else if let ExprKind::Lit(..) = pushed_item.kind { - // literal - emit_lint(cx, vec, pushed_item); - } + }, + ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item), + _ => {}, } } } From 2c9f82e7d22a5cd3f704ed0b932a17fa53f1145b Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 8 Sep 2020 22:45:27 +0900 Subject: [PATCH 42/49] Add some tests to `same_item_push` Add tests in which the variable is initialized with a match expression and function call --- tests/ui/same_item_push.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/ui/same_item_push.rs b/tests/ui/same_item_push.rs index bd4792c4a76..7ca829854db 100644 --- a/tests/ui/same_item_push.rs +++ b/tests/ui/same_item_push.rs @@ -11,6 +11,10 @@ fn increment(x: u8) -> u8 { x + 1 } +fn fun() -> usize { + 42 +} + fn main() { // Test for basic case let mut spaces = Vec::with_capacity(10); @@ -124,4 +128,21 @@ fn main() { for _ in 0..20 { vec17.push(item); } + + let mut vec18 = Vec::new(); + let item = 42; + let item = fun(); + for _ in 0..20 { + vec18.push(item); + } + + let mut vec19 = Vec::new(); + let key = 1; + for _ in 0..20 { + let item = match key { + 1 => 10, + _ => 0, + }; + vec19.push(item); + } } From 952c04674e4225d231f0ba78dff32abf7c06cb8b Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 8 Sep 2020 23:03:15 +0900 Subject: [PATCH 43/49] Refactoring: tests in `same_item_push` --- tests/ui/same_item_push.rs | 111 +++++++++++++++++---------------- tests/ui/same_item_push.stderr | 40 ++++++------ 2 files changed, 77 insertions(+), 74 deletions(-) diff --git a/tests/ui/same_item_push.rs b/tests/ui/same_item_push.rs index 7ca829854db..a37c8782ec3 100644 --- a/tests/ui/same_item_push.rs +++ b/tests/ui/same_item_push.rs @@ -16,64 +16,76 @@ fn fun() -> usize { } fn main() { - // Test for basic case + // ** linted cases ** + let mut vec: Vec = Vec::new(); + let item = 2; + for _ in 5..=20 { + vec.push(item); + } + + let mut vec: Vec = Vec::new(); + for _ in 0..15 { + let item = 2; + vec.push(item); + } + + let mut vec: Vec = Vec::new(); + for _ in 0..15 { + vec.push(13); + } + + let mut vec = Vec::new(); + for _ in 0..20 { + vec.push(VALUE); + } + + let mut vec = Vec::new(); + let item = VALUE; + for _ in 0..20 { + vec.push(item); + } + + // ** non-linted cases ** let mut spaces = Vec::with_capacity(10); for _ in 0..10 { spaces.push(vec![b' ']); } - let mut vec2: Vec = Vec::new(); - let item = 2; - for _ in 5..=20 { - vec2.push(item); - } - - let mut vec3: Vec = Vec::new(); - for _ in 0..15 { - let item = 2; - vec3.push(item); - } - - let mut vec4: Vec = Vec::new(); - for _ in 0..15 { - vec4.push(13); - } - // Suggestion should not be given as pushed variable can mutate - let mut vec5: Vec = Vec::new(); + let mut vec: Vec = Vec::new(); let mut item: u8 = 2; for _ in 0..30 { - vec5.push(mutate_increment(&mut item)); + vec.push(mutate_increment(&mut item)); } - let mut vec6: Vec = Vec::new(); + let mut vec: Vec = Vec::new(); let mut item: u8 = 2; let mut item2 = &mut mutate_increment(&mut item); for _ in 0..30 { - vec6.push(mutate_increment(item2)); + vec.push(mutate_increment(item2)); } - let mut vec7: Vec = Vec::new(); + let mut vec: Vec = Vec::new(); for (a, b) in [0, 1, 4, 9, 16].iter().enumerate() { - vec7.push(a); + vec.push(a); } - let mut vec8: Vec = Vec::new(); + let mut vec: Vec = Vec::new(); for i in 0..30 { - vec8.push(increment(i)); + vec.push(increment(i)); } - let mut vec9: Vec = Vec::new(); + let mut vec: Vec = Vec::new(); for i in 0..30 { - vec9.push(i + i * i); + vec.push(i + i * i); } // Suggestion should not be given as there are multiple pushes that are not the same - let mut vec10: Vec = Vec::new(); + let mut vec: Vec = Vec::new(); let item: u8 = 2; for _ in 0..30 { - vec10.push(item); - vec10.push(item * 2); + vec.push(item); + vec.push(item * 2); } // Suggestion should not be given as Vec is not involved @@ -88,23 +100,23 @@ fn main() { for i in 0..30 { vec_a.push(A { kind: i }); } - let mut vec12: Vec = Vec::new(); + let mut vec: Vec = Vec::new(); for a in vec_a { - vec12.push(2u8.pow(a.kind)); + vec.push(2u8.pow(a.kind)); } // Fix #5902 - let mut vec13: Vec = Vec::new(); + let mut vec: Vec = Vec::new(); let mut item = 0; for _ in 0..10 { - vec13.push(item); + vec.push(item); item += 10; } // Fix #5979 - let mut vec14: Vec = Vec::new(); + let mut vec: Vec = Vec::new(); for _ in 0..10 { - vec14.push(std::fs::File::open("foobar").unwrap()); + vec.push(std::fs::File::open("foobar").unwrap()); } // Fix #5979 #[derive(Clone)] @@ -113,36 +125,27 @@ fn main() { trait T {} impl T for S {} - let mut vec15: Vec> = Vec::new(); + let mut vec: Vec> = Vec::new(); for _ in 0..10 { - vec15.push(Box::new(S {})); + vec.push(Box::new(S {})); } - let mut vec16 = Vec::new(); - for _ in 0..20 { - vec16.push(VALUE); - } - - let mut vec17 = Vec::new(); - let item = VALUE; - for _ in 0..20 { - vec17.push(item); - } - - let mut vec18 = Vec::new(); + // Fix #5985 + let mut vec = Vec::new(); let item = 42; let item = fun(); for _ in 0..20 { - vec18.push(item); + vec.push(item); } - let mut vec19 = Vec::new(); + // Fix #5985 + let mut vec = Vec::new(); let key = 1; for _ in 0..20 { let item = match key { 1 => 10, _ => 0, }; - vec19.push(item); + vec.push(item); } } diff --git a/tests/ui/same_item_push.stderr b/tests/ui/same_item_push.stderr index 4896479791a..d9ffa15780a 100644 --- a/tests/ui/same_item_push.stderr +++ b/tests/ui/same_item_push.stderr @@ -1,43 +1,43 @@ error: it looks like the same item is being pushed into this Vec - --> $DIR/same_item_push.rs:24:9 + --> $DIR/same_item_push.rs:23:9 | -LL | vec2.push(item); - | ^^^^ +LL | vec.push(item); + | ^^^ | = note: `-D clippy::same-item-push` implied by `-D warnings` - = help: try using vec![item;SIZE] or vec2.resize(NEW_SIZE, item) + = help: try using vec![item;SIZE] or vec.resize(NEW_SIZE, item) error: it looks like the same item is being pushed into this Vec - --> $DIR/same_item_push.rs:30:9 + --> $DIR/same_item_push.rs:29:9 | -LL | vec3.push(item); - | ^^^^ +LL | vec.push(item); + | ^^^ | - = help: try using vec![item;SIZE] or vec3.resize(NEW_SIZE, item) + = help: try using vec![item;SIZE] or vec.resize(NEW_SIZE, item) error: it looks like the same item is being pushed into this Vec - --> $DIR/same_item_push.rs:35:9 + --> $DIR/same_item_push.rs:34:9 | -LL | vec4.push(13); - | ^^^^ +LL | vec.push(13); + | ^^^ | - = help: try using vec![13;SIZE] or vec4.resize(NEW_SIZE, 13) + = help: try using vec![13;SIZE] or vec.resize(NEW_SIZE, 13) error: it looks like the same item is being pushed into this Vec - --> $DIR/same_item_push.rs:119:9 + --> $DIR/same_item_push.rs:39:9 | -LL | vec16.push(VALUE); - | ^^^^^ +LL | vec.push(VALUE); + | ^^^ | - = help: try using vec![VALUE;SIZE] or vec16.resize(NEW_SIZE, VALUE) + = help: try using vec![VALUE;SIZE] or vec.resize(NEW_SIZE, VALUE) error: it looks like the same item is being pushed into this Vec - --> $DIR/same_item_push.rs:125:9 + --> $DIR/same_item_push.rs:45:9 | -LL | vec17.push(item); - | ^^^^^ +LL | vec.push(item); + | ^^^ | - = help: try using vec![item;SIZE] or vec17.resize(NEW_SIZE, item) + = help: try using vec![item;SIZE] or vec.resize(NEW_SIZE, item) error: aborting due to 5 previous errors From 1d8ae3aa12567b8461436ac10ba9fc4da55adb29 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 8 Sep 2020 23:12:04 +0900 Subject: [PATCH 44/49] Address `items_after_statement` --- clippy_lints/src/loops.rs | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index c59185bd7bd..6c54c07869a 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1131,6 +1131,23 @@ fn detect_same_item_push<'tcx>( body: &'tcx Expr<'_>, _: &'tcx Expr<'_>, ) { + fn emit_lint(cx: &LateContext<'_>, vec: &Expr<'_>, pushed_item: &Expr<'_>) { + let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); + let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); + + span_lint_and_help( + cx, + SAME_ITEM_PUSH, + vec.span, + "it looks like the same item is being pushed into this Vec", + None, + &format!( + "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", + item_str, vec_str, item_str + ), + ) + } + if !matches!(pat.kind, PatKind::Wild) { return; } @@ -1192,23 +1209,6 @@ fn detect_same_item_push<'tcx>( } } } - - fn emit_lint(cx: &LateContext<'_>, vec: &Expr<'_>, pushed_item: &Expr<'_>) { - let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); - let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); - - span_lint_and_help( - cx, - SAME_ITEM_PUSH, - vec.span, - "it looks like the same item is being pushed into this Vec", - None, - &format!( - "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", - item_str, vec_str, item_str - ), - ) - } } /// Checks for looping over a range and then indexing a sequence with it. From a899ad2e125e8dd3a0a339df1f8457ef8cab3cc7 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Tue, 8 Sep 2020 23:35:19 +0900 Subject: [PATCH 45/49] Change suggestion to `create_dir_all({})` from `std::fs::create_dir_all({})` --- clippy_lints/src/create_dir.rs | 2 +- tests/ui/create_dir.fixed | 6 ++++-- tests/ui/create_dir.rs | 2 ++ tests/ui/create_dir.stderr | 8 ++++---- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/create_dir.rs b/clippy_lints/src/create_dir.rs index f80a0efa7a5..4002fb655a5 100644 --- a/clippy_lints/src/create_dir.rs +++ b/clippy_lints/src/create_dir.rs @@ -42,7 +42,7 @@ impl LateLintPass<'_> for CreateDir { expr.span, "calling `std::fs::create_dir` where there may be a better way", "consider calling `std::fs::create_dir_all` instead", - format!("std::fs::create_dir_all({})", snippet(cx, args[0].span, "..")), + format!("create_dir_all({})", snippet(cx, args[0].span, "..")), Applicability::MaybeIncorrect, ) } diff --git a/tests/ui/create_dir.fixed b/tests/ui/create_dir.fixed index 0e28f87e33d..8ed53a56ac0 100644 --- a/tests/ui/create_dir.fixed +++ b/tests/ui/create_dir.fixed @@ -2,12 +2,14 @@ #![allow(unused_must_use)] #![warn(clippy::create_dir)] +use std::fs::create_dir_all; + fn create_dir() {} fn main() { // Should be warned - std::fs::create_dir_all("foo"); - std::fs::create_dir_all("bar").unwrap(); + create_dir_all("foo"); + create_dir_all("bar").unwrap(); // Shouldn't be warned create_dir(); diff --git a/tests/ui/create_dir.rs b/tests/ui/create_dir.rs index 1f226298c0d..19c8fc24ba2 100644 --- a/tests/ui/create_dir.rs +++ b/tests/ui/create_dir.rs @@ -2,6 +2,8 @@ #![allow(unused_must_use)] #![warn(clippy::create_dir)] +use std::fs::create_dir_all; + fn create_dir() {} fn main() { diff --git a/tests/ui/create_dir.stderr b/tests/ui/create_dir.stderr index 0c97bdd0f7a..67298fc4709 100644 --- a/tests/ui/create_dir.stderr +++ b/tests/ui/create_dir.stderr @@ -1,16 +1,16 @@ error: calling `std::fs::create_dir` where there may be a better way - --> $DIR/create_dir.rs:9:5 + --> $DIR/create_dir.rs:11:5 | LL | std::fs::create_dir("foo"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `std::fs::create_dir_all` instead: `std::fs::create_dir_all("foo")` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `std::fs::create_dir_all` instead: `create_dir_all("foo")` | = note: `-D clippy::create-dir` implied by `-D warnings` error: calling `std::fs::create_dir` where there may be a better way - --> $DIR/create_dir.rs:10:5 + --> $DIR/create_dir.rs:12:5 | LL | std::fs::create_dir("bar").unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `std::fs::create_dir_all` instead: `std::fs::create_dir_all("bar")` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `std::fs::create_dir_all` instead: `create_dir_all("bar")` error: aborting due to 2 previous errors From f23670ed68c4871a3048bf24f7ad2aa426555b6e Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Tue, 8 Sep 2020 17:59:43 -0400 Subject: [PATCH 46/49] Adjust Clippy for CONST_ITEM_MUTATION lint We no longer lint assignments to const item fields in the `temporary_assignment` lint, since this is now covered by the `CONST_ITEM_MUTATION` lint. Additionally, we `#![allow(const_item_mutation)]` in the `borrow_interior_mutable_const.rs` test. Clippy UI tests are run with `-D warnings`, which seems to cause builtin lints to prevent Clippy lints from running. --- clippy_lints/src/temporary_assignment.rs | 4 +-- tests/ui/borrow_interior_mutable_const.rs | 1 + tests/ui/borrow_interior_mutable_const.stderr | 32 ++++++++--------- tests/ui/temporary_assignment.rs | 1 + tests/ui/temporary_assignment.stderr | 34 +++---------------- 5 files changed, 24 insertions(+), 48 deletions(-) diff --git a/clippy_lints/src/temporary_assignment.rs b/clippy_lints/src/temporary_assignment.rs index 1aeff1baa36..2b6ddadd4c1 100644 --- a/clippy_lints/src/temporary_assignment.rs +++ b/clippy_lints/src/temporary_assignment.rs @@ -1,5 +1,4 @@ use crate::utils::{is_adjusted, span_lint}; -use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -22,10 +21,9 @@ declare_clippy_lint! { "assignments to temporaries" } -fn is_temporary(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { +fn is_temporary(_cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { match &expr.kind { ExprKind::Struct(..) | ExprKind::Tup(..) => true, - ExprKind::Path(qpath) => matches!(cx.qpath_res(qpath, expr.hir_id), Res::Def(DefKind::Const, ..)), _ => false, } } diff --git a/tests/ui/borrow_interior_mutable_const.rs b/tests/ui/borrow_interior_mutable_const.rs index a414832bcd3..39f87510548 100644 --- a/tests/ui/borrow_interior_mutable_const.rs +++ b/tests/ui/borrow_interior_mutable_const.rs @@ -1,5 +1,6 @@ #![warn(clippy::borrow_interior_mutable_const)] #![allow(clippy::declare_interior_mutable_const, clippy::ref_in_deref)] +#![allow(const_item_mutation)] use std::borrow::Cow; use std::cell::{Cell, UnsafeCell}; diff --git a/tests/ui/borrow_interior_mutable_const.stderr b/tests/ui/borrow_interior_mutable_const.stderr index 1e0b3e4d20a..5800af7e960 100644 --- a/tests/ui/borrow_interior_mutable_const.stderr +++ b/tests/ui/borrow_interior_mutable_const.stderr @@ -1,5 +1,5 @@ error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:65:5 + --> $DIR/borrow_interior_mutable_const.rs:66:5 | LL | ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability | ^^^^^^ @@ -8,7 +8,7 @@ LL | ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:66:16 + --> $DIR/borrow_interior_mutable_const.rs:67:16 | LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutability | ^^^^^^ @@ -16,7 +16,7 @@ LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutabi = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:69:22 + --> $DIR/borrow_interior_mutable_const.rs:70:22 | LL | let _once_ref = &ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -24,7 +24,7 @@ LL | let _once_ref = &ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:70:25 + --> $DIR/borrow_interior_mutable_const.rs:71:25 | LL | let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -32,7 +32,7 @@ LL | let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:71:27 + --> $DIR/borrow_interior_mutable_const.rs:72:27 | LL | let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -40,7 +40,7 @@ LL | let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:72:26 + --> $DIR/borrow_interior_mutable_const.rs:73:26 | LL | let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -48,7 +48,7 @@ LL | let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:83:14 + --> $DIR/borrow_interior_mutable_const.rs:84:14 | LL | let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -56,7 +56,7 @@ LL | let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:84:14 + --> $DIR/borrow_interior_mutable_const.rs:85:14 | LL | let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -64,7 +64,7 @@ LL | let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:85:19 + --> $DIR/borrow_interior_mutable_const.rs:86:19 | LL | let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL | let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:86:14 + --> $DIR/borrow_interior_mutable_const.rs:87:14 | LL | let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -80,7 +80,7 @@ LL | let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:87:13 + --> $DIR/borrow_interior_mutable_const.rs:88:13 | LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -88,7 +88,7 @@ LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mu = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:93:13 + --> $DIR/borrow_interior_mutable_const.rs:94:13 | LL | let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -96,7 +96,7 @@ LL | let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:98:5 + --> $DIR/borrow_interior_mutable_const.rs:99:5 | LL | CELL.set(2); //~ ERROR interior mutability | ^^^^ @@ -104,7 +104,7 @@ LL | CELL.set(2); //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:99:16 + --> $DIR/borrow_interior_mutable_const.rs:100:16 | LL | assert_eq!(CELL.get(), 6); //~ ERROR interior mutability | ^^^^ @@ -112,7 +112,7 @@ LL | assert_eq!(CELL.get(), 6); //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:112:5 + --> $DIR/borrow_interior_mutable_const.rs:113:5 | LL | u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability | ^^^^^^^^^^^ @@ -120,7 +120,7 @@ LL | u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:113:16 + --> $DIR/borrow_interior_mutable_const.rs:114:16 | LL | assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); //~ ERROR interior mutability | ^^^^^^^^^^^ diff --git a/tests/ui/temporary_assignment.rs b/tests/ui/temporary_assignment.rs index c6c315d5fab..d6f56d40c5d 100644 --- a/tests/ui/temporary_assignment.rs +++ b/tests/ui/temporary_assignment.rs @@ -1,4 +1,5 @@ #![warn(clippy::temporary_assignment)] +#![allow(const_item_mutation)] use std::ops::{Deref, DerefMut}; diff --git a/tests/ui/temporary_assignment.stderr b/tests/ui/temporary_assignment.stderr index 4efe2d4bb67..4cc32c79f05 100644 --- a/tests/ui/temporary_assignment.stderr +++ b/tests/ui/temporary_assignment.stderr @@ -1,5 +1,5 @@ error: assignment to temporary - --> $DIR/temporary_assignment.rs:47:5 + --> $DIR/temporary_assignment.rs:48:5 | LL | Struct { field: 0 }.field = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | Struct { field: 0 }.field = 1; = note: `-D clippy::temporary-assignment` implied by `-D warnings` error: assignment to temporary - --> $DIR/temporary_assignment.rs:48:5 + --> $DIR/temporary_assignment.rs:49:5 | LL | / MultiStruct { LL | | structure: Struct { field: 0 }, @@ -17,40 +17,16 @@ LL | | .field = 1; | |______________^ error: assignment to temporary - --> $DIR/temporary_assignment.rs:53:5 + --> $DIR/temporary_assignment.rs:54:5 | LL | ArrayStruct { array: [0] }.array[0] = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: assignment to temporary - --> $DIR/temporary_assignment.rs:54:5 + --> $DIR/temporary_assignment.rs:55:5 | LL | (0, 0).0 = 1; | ^^^^^^^^^^^^ -error: assignment to temporary - --> $DIR/temporary_assignment.rs:56:5 - | -LL | A.0 = 2; - | ^^^^^^^ - -error: assignment to temporary - --> $DIR/temporary_assignment.rs:57:5 - | -LL | B.field = 2; - | ^^^^^^^^^^^ - -error: assignment to temporary - --> $DIR/temporary_assignment.rs:58:5 - | -LL | C.structure.field = 2; - | ^^^^^^^^^^^^^^^^^^^^^ - -error: assignment to temporary - --> $DIR/temporary_assignment.rs:59:5 - | -LL | D.array[0] = 2; - | ^^^^^^^^^^^^^^ - -error: aborting due to 8 previous errors +error: aborting due to 4 previous errors From 4db10297c1dcd2fc368bd61a24aa0291a6b6c61a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Wed, 9 Sep 2020 16:53:45 +0200 Subject: [PATCH 47/49] link to the Box docs in related lint documentation. --- clippy_lints/src/types.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index c82deaa43b2..550ae2927a8 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -37,6 +37,7 @@ use crate::utils::{ declare_clippy_lint! { /// **What it does:** Checks for use of `Box>` anywhere in the code. + /// Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information. /// /// **Why is this bad?** `Vec` already keeps its contents in a separate area on /// the heap. So if you `Box` it, you just add another level of indirection @@ -65,6 +66,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// **What it does:** Checks for use of `Vec>` where T: Sized anywhere in the code. + /// Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information. /// /// **Why is this bad?** `Vec` already keeps its contents in a separate area on /// the heap. So if you `Box` its contents, you just add another level of indirection. @@ -167,6 +169,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// **What it does:** Checks for use of `&Box` anywhere in the code. + /// Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information. /// /// **Why is this bad?** Any `&Box` can also be a `&T`, which is more /// general. From de195f2d3d3a2039cb8c4141aa37d060780beaa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Wed, 9 Sep 2020 17:59:13 +0200 Subject: [PATCH 48/49] print the unit type `()` in related lint messages. --- clippy_lints/src/map_unit_fn.rs | 6 ++-- tests/ui/option_map_unit_fn_fixable.stderr | 36 ++++++++++---------- tests/ui/result_map_unit_fn_fixable.stderr | 34 +++++++++--------- tests/ui/result_map_unit_fn_unfixable.stderr | 12 +++---- 4 files changed, 44 insertions(+), 44 deletions(-) diff --git a/clippy_lints/src/map_unit_fn.rs b/clippy_lints/src/map_unit_fn.rs index 1f9ae8c931a..076ef235b8b 100644 --- a/clippy_lints/src/map_unit_fn.rs +++ b/clippy_lints/src/map_unit_fn.rs @@ -9,7 +9,7 @@ use rustc_span::source_map::Span; declare_clippy_lint! { /// **What it does:** Checks for usage of `option.map(f)` where f is a function - /// or closure that returns the unit type. + /// or closure that returns the unit type `()`. /// /// **Why is this bad?** Readability, this can be written more clearly with /// an if let statement @@ -51,7 +51,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// **What it does:** Checks for usage of `result.map(f)` where f is a function - /// or closure that returns the unit type. + /// or closure that returns the unit type `()`. /// /// **Why is this bad?** Readability, this can be written more clearly with /// an if let statement @@ -197,7 +197,7 @@ fn let_binding_name(cx: &LateContext<'_>, var_arg: &hir::Expr<'_>) -> String { #[must_use] fn suggestion_msg(function_type: &str, map_type: &str) -> String { format!( - "called `map(f)` on an `{0}` value where `f` is a {1} that returns the unit type", + "called `map(f)` on an `{0}` value where `f` is a {1} that returns the unit type `()`", map_type, function_type ) } diff --git a/tests/ui/option_map_unit_fn_fixable.stderr b/tests/ui/option_map_unit_fn_fixable.stderr index 1312c70b6d5..d7d45ef9b0b 100644 --- a/tests/ui/option_map_unit_fn_fixable.stderr +++ b/tests/ui/option_map_unit_fn_fixable.stderr @@ -1,4 +1,4 @@ -error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:38:5 | LL | x.field.map(do_nothing); @@ -8,7 +8,7 @@ LL | x.field.map(do_nothing); | = note: `-D clippy::option-map-unit-fn` implied by `-D warnings` -error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:40:5 | LL | x.field.map(do_nothing); @@ -16,7 +16,7 @@ LL | x.field.map(do_nothing); | | | help: try this: `if let Some(x_field) = x.field { do_nothing(x_field) }` -error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:42:5 | LL | x.field.map(diverge); @@ -24,7 +24,7 @@ LL | x.field.map(diverge); | | | help: try this: `if let Some(x_field) = x.field { diverge(x_field) }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:48:5 | LL | x.field.map(|value| x.do_option_nothing(value + captured)); @@ -32,7 +32,7 @@ LL | x.field.map(|value| x.do_option_nothing(value + captured)); | | | help: try this: `if let Some(value) = x.field { x.do_option_nothing(value + captured) }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:50:5 | LL | x.field.map(|value| { x.do_option_plus_one(value + captured); }); @@ -40,7 +40,7 @@ LL | x.field.map(|value| { x.do_option_plus_one(value + captured); }); | | | help: try this: `if let Some(value) = x.field { x.do_option_plus_one(value + captured); }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:53:5 | LL | x.field.map(|value| do_nothing(value + captured)); @@ -48,7 +48,7 @@ LL | x.field.map(|value| do_nothing(value + captured)); | | | help: try this: `if let Some(value) = x.field { do_nothing(value + captured) }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:55:5 | LL | x.field.map(|value| { do_nothing(value + captured) }); @@ -56,7 +56,7 @@ LL | x.field.map(|value| { do_nothing(value + captured) }); | | | help: try this: `if let Some(value) = x.field { do_nothing(value + captured) }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:57:5 | LL | x.field.map(|value| { do_nothing(value + captured); }); @@ -64,7 +64,7 @@ LL | x.field.map(|value| { do_nothing(value + captured); }); | | | help: try this: `if let Some(value) = x.field { do_nothing(value + captured); }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:59:5 | LL | x.field.map(|value| { { do_nothing(value + captured); } }); @@ -72,7 +72,7 @@ LL | x.field.map(|value| { { do_nothing(value + captured); } }); | | | help: try this: `if let Some(value) = x.field { do_nothing(value + captured); }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:62:5 | LL | x.field.map(|value| diverge(value + captured)); @@ -80,7 +80,7 @@ LL | x.field.map(|value| diverge(value + captured)); | | | help: try this: `if let Some(value) = x.field { diverge(value + captured) }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:64:5 | LL | x.field.map(|value| { diverge(value + captured) }); @@ -88,7 +88,7 @@ LL | x.field.map(|value| { diverge(value + captured) }); | | | help: try this: `if let Some(value) = x.field { diverge(value + captured) }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:66:5 | LL | x.field.map(|value| { diverge(value + captured); }); @@ -96,7 +96,7 @@ LL | x.field.map(|value| { diverge(value + captured); }); | | | help: try this: `if let Some(value) = x.field { diverge(value + captured); }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:68:5 | LL | x.field.map(|value| { { diverge(value + captured); } }); @@ -104,7 +104,7 @@ LL | x.field.map(|value| { { diverge(value + captured); } }); | | | help: try this: `if let Some(value) = x.field { diverge(value + captured); }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:73:5 | LL | x.field.map(|value| { let y = plus_one(value + captured); }); @@ -112,7 +112,7 @@ LL | x.field.map(|value| { let y = plus_one(value + captured); }); | | | help: try this: `if let Some(value) = x.field { let y = plus_one(value + captured); }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:75:5 | LL | x.field.map(|value| { plus_one(value + captured); }); @@ -120,7 +120,7 @@ LL | x.field.map(|value| { plus_one(value + captured); }); | | | help: try this: `if let Some(value) = x.field { plus_one(value + captured); }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:77:5 | LL | x.field.map(|value| { { plus_one(value + captured); } }); @@ -128,7 +128,7 @@ LL | x.field.map(|value| { { plus_one(value + captured); } }); | | | help: try this: `if let Some(value) = x.field { plus_one(value + captured); }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:80:5 | LL | x.field.map(|ref value| { do_nothing(value + captured) }); @@ -136,7 +136,7 @@ LL | x.field.map(|ref value| { do_nothing(value + captured) }); | | | help: try this: `if let Some(ref value) = x.field { do_nothing(value + captured) }` -error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:82:5 | LL | option().map(do_nothing);} diff --git a/tests/ui/result_map_unit_fn_fixable.stderr b/tests/ui/result_map_unit_fn_fixable.stderr index 467e00263cd..4f3a8c6b792 100644 --- a/tests/ui/result_map_unit_fn_fixable.stderr +++ b/tests/ui/result_map_unit_fn_fixable.stderr @@ -1,4 +1,4 @@ -error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:35:5 | LL | x.field.map(do_nothing); @@ -8,7 +8,7 @@ LL | x.field.map(do_nothing); | = note: `-D clippy::result-map-unit-fn` implied by `-D warnings` -error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:37:5 | LL | x.field.map(do_nothing); @@ -16,7 +16,7 @@ LL | x.field.map(do_nothing); | | | help: try this: `if let Ok(x_field) = x.field { do_nothing(x_field) }` -error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:39:5 | LL | x.field.map(diverge); @@ -24,7 +24,7 @@ LL | x.field.map(diverge); | | | help: try this: `if let Ok(x_field) = x.field { diverge(x_field) }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:45:5 | LL | x.field.map(|value| x.do_result_nothing(value + captured)); @@ -32,7 +32,7 @@ LL | x.field.map(|value| x.do_result_nothing(value + captured)); | | | help: try this: `if let Ok(value) = x.field { x.do_result_nothing(value + captured) }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:47:5 | LL | x.field.map(|value| { x.do_result_plus_one(value + captured); }); @@ -40,7 +40,7 @@ LL | x.field.map(|value| { x.do_result_plus_one(value + captured); }); | | | help: try this: `if let Ok(value) = x.field { x.do_result_plus_one(value + captured); }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:50:5 | LL | x.field.map(|value| do_nothing(value + captured)); @@ -48,7 +48,7 @@ LL | x.field.map(|value| do_nothing(value + captured)); | | | help: try this: `if let Ok(value) = x.field { do_nothing(value + captured) }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:52:5 | LL | x.field.map(|value| { do_nothing(value + captured) }); @@ -56,7 +56,7 @@ LL | x.field.map(|value| { do_nothing(value + captured) }); | | | help: try this: `if let Ok(value) = x.field { do_nothing(value + captured) }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:54:5 | LL | x.field.map(|value| { do_nothing(value + captured); }); @@ -64,7 +64,7 @@ LL | x.field.map(|value| { do_nothing(value + captured); }); | | | help: try this: `if let Ok(value) = x.field { do_nothing(value + captured); }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:56:5 | LL | x.field.map(|value| { { do_nothing(value + captured); } }); @@ -72,7 +72,7 @@ LL | x.field.map(|value| { { do_nothing(value + captured); } }); | | | help: try this: `if let Ok(value) = x.field { do_nothing(value + captured); }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:59:5 | LL | x.field.map(|value| diverge(value + captured)); @@ -80,7 +80,7 @@ LL | x.field.map(|value| diverge(value + captured)); | | | help: try this: `if let Ok(value) = x.field { diverge(value + captured) }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:61:5 | LL | x.field.map(|value| { diverge(value + captured) }); @@ -88,7 +88,7 @@ LL | x.field.map(|value| { diverge(value + captured) }); | | | help: try this: `if let Ok(value) = x.field { diverge(value + captured) }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:63:5 | LL | x.field.map(|value| { diverge(value + captured); }); @@ -96,7 +96,7 @@ LL | x.field.map(|value| { diverge(value + captured); }); | | | help: try this: `if let Ok(value) = x.field { diverge(value + captured); }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:65:5 | LL | x.field.map(|value| { { diverge(value + captured); } }); @@ -104,7 +104,7 @@ LL | x.field.map(|value| { { diverge(value + captured); } }); | | | help: try this: `if let Ok(value) = x.field { diverge(value + captured); }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:70:5 | LL | x.field.map(|value| { let y = plus_one(value + captured); }); @@ -112,7 +112,7 @@ LL | x.field.map(|value| { let y = plus_one(value + captured); }); | | | help: try this: `if let Ok(value) = x.field { let y = plus_one(value + captured); }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:72:5 | LL | x.field.map(|value| { plus_one(value + captured); }); @@ -120,7 +120,7 @@ LL | x.field.map(|value| { plus_one(value + captured); }); | | | help: try this: `if let Ok(value) = x.field { plus_one(value + captured); }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:74:5 | LL | x.field.map(|value| { { plus_one(value + captured); } }); @@ -128,7 +128,7 @@ LL | x.field.map(|value| { { plus_one(value + captured); } }); | | | help: try this: `if let Ok(value) = x.field { plus_one(value + captured); }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:77:5 | LL | x.field.map(|ref value| { do_nothing(value + captured) }); diff --git a/tests/ui/result_map_unit_fn_unfixable.stderr b/tests/ui/result_map_unit_fn_unfixable.stderr index b23cc608621..88e4efdb0f0 100644 --- a/tests/ui/result_map_unit_fn_unfixable.stderr +++ b/tests/ui/result_map_unit_fn_unfixable.stderr @@ -1,4 +1,4 @@ -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_unfixable.rs:23:5 | LL | x.field.map(|value| { do_nothing(value); do_nothing(value) }); @@ -8,7 +8,7 @@ LL | x.field.map(|value| { do_nothing(value); do_nothing(value) }); | = note: `-D clippy::result-map-unit-fn` implied by `-D warnings` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_unfixable.rs:25:5 | LL | x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) }); @@ -16,7 +16,7 @@ LL | x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) | | | help: try this: `if let Ok(value) = x.field { ... }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_unfixable.rs:29:5 | LL | x.field.map(|value| { @@ -30,7 +30,7 @@ LL | || }); | |_______| | -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_unfixable.rs:33:5 | LL | x.field.map(|value| { do_nothing(value); do_nothing(value); }); @@ -38,7 +38,7 @@ LL | x.field.map(|value| { do_nothing(value); do_nothing(value); }); | | | help: try this: `if let Ok(value) = x.field { ... }` -error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()` --> $DIR/result_map_unit_fn_unfixable.rs:37:5 | LL | "12".parse::().map(diverge); @@ -46,7 +46,7 @@ LL | "12".parse::().map(diverge); | | | help: try this: `if let Ok(a) = "12".parse::() { diverge(a) }` -error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()` --> $DIR/result_map_unit_fn_unfixable.rs:43:5 | LL | y.map(do_nothing); From 2d56512580919483268c3e3bf2e028c8614805f2 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 10 Sep 2020 14:18:05 +0200 Subject: [PATCH 49/49] Cleanup of rustup --- clippy_lints/src/temporary_assignment.rs | 9 +++------ tests/ui/temporary_assignment.rs | 6 ------ tests/ui/temporary_assignment.stderr | 8 ++++---- 3 files changed, 7 insertions(+), 16 deletions(-) diff --git a/clippy_lints/src/temporary_assignment.rs b/clippy_lints/src/temporary_assignment.rs index 2b6ddadd4c1..fb891866364 100644 --- a/clippy_lints/src/temporary_assignment.rs +++ b/clippy_lints/src/temporary_assignment.rs @@ -21,11 +21,8 @@ declare_clippy_lint! { "assignments to temporaries" } -fn is_temporary(_cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - match &expr.kind { - ExprKind::Struct(..) | ExprKind::Tup(..) => true, - _ => false, - } +fn is_temporary(expr: &Expr<'_>) -> bool { + matches!(&expr.kind, ExprKind::Struct(..) | ExprKind::Tup(..)) } declare_lint_pass!(TemporaryAssignment => [TEMPORARY_ASSIGNMENT]); @@ -37,7 +34,7 @@ impl<'tcx> LateLintPass<'tcx> for TemporaryAssignment { while let ExprKind::Field(f, _) | ExprKind::Index(f, _) = &base.kind { base = f; } - if is_temporary(cx, base) && !is_adjusted(cx, base) { + if is_temporary(base) && !is_adjusted(cx, base) { span_lint(cx, TEMPORARY_ASSIGNMENT, expr.span, "assignment to temporary"); } } diff --git a/tests/ui/temporary_assignment.rs b/tests/ui/temporary_assignment.rs index d6f56d40c5d..ac4c1bc6597 100644 --- a/tests/ui/temporary_assignment.rs +++ b/tests/ui/temporary_assignment.rs @@ -1,5 +1,4 @@ #![warn(clippy::temporary_assignment)] -#![allow(const_item_mutation)] use std::ops::{Deref, DerefMut}; @@ -54,11 +53,6 @@ fn main() { ArrayStruct { array: [0] }.array[0] = 1; (0, 0).0 = 1; - A.0 = 2; - B.field = 2; - C.structure.field = 2; - D.array[0] = 2; - // no error s.field = 1; t.0 = 1; diff --git a/tests/ui/temporary_assignment.stderr b/tests/ui/temporary_assignment.stderr index 4cc32c79f05..7d79901a28d 100644 --- a/tests/ui/temporary_assignment.stderr +++ b/tests/ui/temporary_assignment.stderr @@ -1,5 +1,5 @@ error: assignment to temporary - --> $DIR/temporary_assignment.rs:48:5 + --> $DIR/temporary_assignment.rs:47:5 | LL | Struct { field: 0 }.field = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | Struct { field: 0 }.field = 1; = note: `-D clippy::temporary-assignment` implied by `-D warnings` error: assignment to temporary - --> $DIR/temporary_assignment.rs:49:5 + --> $DIR/temporary_assignment.rs:48:5 | LL | / MultiStruct { LL | | structure: Struct { field: 0 }, @@ -17,13 +17,13 @@ LL | | .field = 1; | |______________^ error: assignment to temporary - --> $DIR/temporary_assignment.rs:54:5 + --> $DIR/temporary_assignment.rs:53:5 | LL | ArrayStruct { array: [0] }.array[0] = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: assignment to temporary - --> $DIR/temporary_assignment.rs:55:5 + --> $DIR/temporary_assignment.rs:54:5 | LL | (0, 0).0 = 1; | ^^^^^^^^^^^^