1
Fork 0

Merge remote-tracking branch 'upstream/master' into rustup

This commit is contained in:
flip1995 2022-01-27 14:23:31 +01:00
commit d037b28025
No known key found for this signature in database
GPG key ID: 1CA0DF2AF59D68A5
107 changed files with 1875 additions and 1018 deletions

View file

@ -981,7 +981,7 @@ Released 2021-03-25
[#6532](https://github.com/rust-lang/rust-clippy/pull/6532) [#6532](https://github.com/rust-lang/rust-clippy/pull/6532)
* [`single_match`] Suggest `if` over `if let` when possible * [`single_match`] Suggest `if` over `if let` when possible
[#6574](https://github.com/rust-lang/rust-clippy/pull/6574) [#6574](https://github.com/rust-lang/rust-clippy/pull/6574)
* [`ref_in_deref`] Use parentheses correctly in suggestion * `ref_in_deref` Use parentheses correctly in suggestion
[#6609](https://github.com/rust-lang/rust-clippy/pull/6609) [#6609](https://github.com/rust-lang/rust-clippy/pull/6609)
* [`stable_sort_primitive`] Clarify error message * [`stable_sort_primitive`] Clarify error message
[#6611](https://github.com/rust-lang/rust-clippy/pull/6611) [#6611](https://github.com/rust-lang/rust-clippy/pull/6611)
@ -3227,7 +3227,6 @@ Released 2018-09-13
[`redundant_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_slicing [`redundant_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_slicing
[`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes [`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes
[`ref_binding_to_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_binding_to_reference [`ref_binding_to_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_binding_to_reference
[`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref
[`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref [`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref
[`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro [`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro
[`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once [`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once

View file

@ -9,9 +9,14 @@ use walkdir::WalkDir;
use crate::clippy_project_root; use crate::clippy_project_root;
#[cfg(not(windows))]
static CARGO_CLIPPY_EXE: &str = "cargo-clippy";
#[cfg(windows)]
static CARGO_CLIPPY_EXE: &str = "cargo-clippy.exe";
static CLIPPY_BUILD_TIME: SyncLazy<Option<std::time::SystemTime>> = SyncLazy::new(|| { static CLIPPY_BUILD_TIME: SyncLazy<Option<std::time::SystemTime>> = SyncLazy::new(|| {
let mut path = std::env::current_exe().unwrap(); let mut path = std::env::current_exe().unwrap();
path.set_file_name("cargo-clippy"); path.set_file_name(CARGO_CLIPPY_EXE);
fs::metadata(path).ok()?.modified().ok() fs::metadata(path).ok()?.modified().ok()
}); });

View file

@ -7,7 +7,6 @@ pub fn run(filename: &str) {
.args(["-Z", "no-codegen"]) .args(["-Z", "no-codegen"])
.args(["--edition", "2021"]) .args(["--edition", "2021"])
.arg(filename) .arg(filename)
.env("__CLIPPY_INTERNAL_TESTS", "true")
.status() .status()
.expect("failed to run cargo") .expect("failed to run cargo")
.code(); .code();

View file

@ -94,4 +94,6 @@ impl<'tcx> LateLintPass<'tcx> for BorrowAsPtr {
} }
} }
} }
extract_msrv_attr!(LateContext);
} }

View file

@ -23,15 +23,14 @@ pub(super) fn check(
if_chain! { if_chain! {
if let LitKind::Int(n, _) = lit.node; if let LitKind::Int(n, _) = lit.node;
if let Some(src) = snippet_opt(cx, lit.span); if let Some(src) = snippet_opt(cx, cast_expr.span);
if cast_to.is_floating_point(); if cast_to.is_floating_point();
if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node); if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node);
let from_nbits = 128 - n.leading_zeros(); let from_nbits = 128 - n.leading_zeros();
let to_nbits = fp_ty_mantissa_nbits(cast_to); let to_nbits = fp_ty_mantissa_nbits(cast_to);
if from_nbits != 0 && to_nbits != 0 && from_nbits <= to_nbits && num_lit.is_decimal(); if from_nbits != 0 && to_nbits != 0 && from_nbits <= to_nbits && num_lit.is_decimal();
then { then {
let literal_str = if is_unary_neg(cast_expr) { format!("-{}", num_lit.integer) } else { num_lit.integer.into() }; lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to);
lint_unnecessary_cast(cx, expr, &literal_str, cast_from, cast_to);
return true return true
} }
} }
@ -48,7 +47,7 @@ pub(super) fn check(
| LitKind::Float(_, LitFloatType::Suffixed(_)) | LitKind::Float(_, LitFloatType::Suffixed(_))
if cast_from.kind() == cast_to.kind() => if cast_from.kind() == cast_to.kind() =>
{ {
if let Some(src) = snippet_opt(cx, lit.span) { if let Some(src) = snippet_opt(cx, cast_expr.span) {
if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node) { if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node) {
lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to); lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to);
} }
@ -113,7 +112,3 @@ fn fp_ty_mantissa_nbits(typ: Ty<'_>) -> u32 {
_ => 0, _ => 0,
} }
} }
fn is_unary_neg(expr: &Expr<'_>) -> bool {
matches!(expr.kind, ExprKind::Unary(UnOp::Neg, _))
}

View file

@ -1,5 +1,6 @@
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
use clippy_utils::sugg::has_enclosing_paren;
use clippy_utils::ty::peel_mid_ty_refs; use clippy_utils::ty::peel_mid_ty_refs;
use clippy_utils::{get_parent_expr, get_parent_node, is_lint_allowed, path_to_local}; use clippy_utils::{get_parent_expr, get_parent_node, is_lint_allowed, path_to_local};
use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX}; use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX};
@ -10,11 +11,10 @@ use rustc_hir::{
Pat, PatKind, UnOp, Pat, PatKind, UnOp,
}; };
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
use rustc_middle::ty::{self, Ty, TyCtxt, TyS, TypeckResults}; use rustc_middle::ty::{self, Ty, TyCtxt, TyS, TypeckResults};
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{symbol::sym, Span}; use rustc_span::{symbol::sym, Span};
use std::iter;
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
@ -131,8 +131,6 @@ pub struct Dereferencing {
struct StateData { struct StateData {
/// Span of the top level expression /// Span of the top level expression
span: Span, span: Span,
/// The required mutability
target_mut: Mutability,
} }
enum State { enum State {
@ -141,9 +139,13 @@ enum State {
// The number of calls in a sequence which changed the referenced type // The number of calls in a sequence which changed the referenced type
ty_changed_count: usize, ty_changed_count: usize,
is_final_ufcs: bool, is_final_ufcs: bool,
/// The required mutability
target_mut: Mutability,
}, },
DerefedBorrow { DerefedBorrow {
count: u32, count: usize,
required_precedence: i8,
msg: &'static str,
}, },
} }
@ -214,59 +216,98 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
1 1
}, },
is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)), is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)),
},
StateData {
span: expr.span,
target_mut, target_mut,
}, },
StateData { span: expr.span },
)); ));
}, },
RefOp::AddrOf => { RefOp::AddrOf => {
// Find the number of times the borrow is auto-derefed. // Find the number of times the borrow is auto-derefed.
let mut iter = find_adjustments(cx.tcx, typeck, expr).iter(); let mut iter = find_adjustments(cx.tcx, typeck, expr).iter();
if let Some((i, adjust)) = iter.by_ref().enumerate().find_map(|(i, adjust)| { let mut deref_count = 0usize;
if !matches!(adjust.kind, Adjust::Deref(_)) { let next_adjust = loop {
Some((i, adjust)) match iter.next() {
} else if !adjust.target.is_ref() { Some(adjust) => {
// Add one to the number of references found. if !matches!(adjust.kind, Adjust::Deref(_)) {
Some((i + 1, adjust)) break Some(adjust);
} else if !adjust.target.is_ref() {
deref_count += 1;
break iter.next();
}
deref_count += 1;
},
None => break None,
};
};
// Determine the required number of references before any can be removed. In all cases the
// reference made by the current expression will be removed. After that there are four cases to
// handle.
//
// 1. Auto-borrow will trigger in the current position, so no further references are required.
// 2. Auto-deref ends at a reference, or the underlying type, so one extra needs to be left to
// handle the automatically inserted re-borrow.
// 3. Auto-deref hits a user-defined `Deref` impl, so at least one reference needs to exist to
// start auto-deref.
// 4. If the chain of non-user-defined derefs ends with a mutable re-borrow, and re-borrow
// adjustments will not be inserted automatically, then leave one further reference to avoid
// moving a mutable borrow.
// e.g.
// fn foo<T>(x: &mut Option<&mut T>, y: &mut T) {
// let x = match x {
// // Removing the borrow will cause `x` to be moved
// Some(x) => &mut *x,
// None => y
// };
// }
let deref_msg =
"this expression creates a reference which is immediately dereferenced by the compiler";
let borrow_msg = "this expression borrows a value the compiler would automatically borrow";
let (required_refs, required_precedence, msg) = if is_auto_borrow_position(parent, expr.hir_id)
{
(1, PREC_POSTFIX, if deref_count == 1 { borrow_msg } else { deref_msg })
} else if let Some(&Adjust::Borrow(AutoBorrow::Ref(_, mutability))) =
next_adjust.map(|a| &a.kind)
{
if matches!(mutability, AutoBorrowMutability::Mut { .. })
&& !is_auto_reborrow_position(parent)
{
(3, 0, deref_msg)
} else { } else {
None (2, 0, deref_msg)
}
}) {
// Found two consecutive derefs. At least one can be removed.
if i > 1 {
let target_mut = iter::once(adjust)
.chain(iter)
.find_map(|adjust| match adjust.kind {
Adjust::Borrow(AutoBorrow::Ref(_, m)) => Some(m.into()),
_ => None,
})
// This default should never happen. Auto-deref always reborrows.
.unwrap_or(Mutability::Not);
self.state = Some((
// Subtract one for the current borrow expression, and one to cover the last
// reference which can't be removed (it's either reborrowed, or needed for
// auto-deref to happen).
State::DerefedBorrow {
count:
// Truncation here would require more than a `u32::MAX` level reference. The compiler
// does not support this.
#[allow(clippy::cast_possible_truncation)]
{ i as u32 - 2 }
},
StateData {
span: expr.span,
target_mut,
},
));
} }
} else {
(2, 0, deref_msg)
};
if deref_count >= required_refs {
self.state = Some((
State::DerefedBorrow {
// One of the required refs is for the current borrow expression, the remaining ones
// can't be removed without breaking the code. See earlier comment.
count: deref_count - required_refs,
required_precedence,
msg,
},
StateData { span: expr.span },
));
} }
}, },
_ => (), _ => (),
} }
}, },
(Some((State::DerefMethod { ty_changed_count, .. }, data)), RefOp::Method(_)) => { (
Some((
State::DerefMethod {
target_mut,
ty_changed_count,
..
},
data,
)),
RefOp::Method(_),
) => {
self.state = Some(( self.state = Some((
State::DerefMethod { State::DerefMethod {
ty_changed_count: if deref_method_same_type(typeck.expr_ty(expr), typeck.expr_ty(sub_expr)) { ty_changed_count: if deref_method_same_type(typeck.expr_ty(expr), typeck.expr_ty(sub_expr)) {
@ -275,12 +316,30 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
ty_changed_count + 1 ty_changed_count + 1
}, },
is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)), is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)),
target_mut,
}, },
data, data,
)); ));
}, },
(Some((State::DerefedBorrow { count }, data)), RefOp::AddrOf) if count != 0 => { (
self.state = Some((State::DerefedBorrow { count: count - 1 }, data)); Some((
State::DerefedBorrow {
count,
required_precedence,
msg,
},
data,
)),
RefOp::AddrOf,
) if count != 0 => {
self.state = Some((
State::DerefedBorrow {
count: count - 1,
required_precedence,
msg,
},
data,
));
}, },
(Some((state, data)), _) => report(cx, expr, state, data), (Some((state, data)), _) => report(cx, expr, state, data),
@ -455,6 +514,30 @@ fn is_linted_explicit_deref_position(parent: Option<Node<'_>>, child_id: HirId,
} }
} }
/// Checks if the given expression is in a position which can be auto-reborrowed.
/// Note: This is only correct assuming auto-deref is already occurring.
fn is_auto_reborrow_position(parent: Option<Node<'_>>) -> bool {
match parent {
Some(Node::Expr(parent)) => matches!(parent.kind, ExprKind::MethodCall(..) | ExprKind::Call(..)),
Some(Node::Local(_)) => true,
_ => false,
}
}
/// Checks if the given expression is a position which can auto-borrow.
fn is_auto_borrow_position(parent: Option<Node<'_>>, child_id: HirId) -> bool {
if let Some(Node::Expr(parent)) = parent {
match parent.kind {
ExprKind::MethodCall(_, [self_arg, ..], _) => self_arg.hir_id == child_id,
ExprKind::Field(..) => true,
ExprKind::Call(f, _) => f.hir_id == child_id,
_ => false,
}
} else {
false
}
}
/// Adjustments are sometimes made in the parent block rather than the expression itself. /// Adjustments are sometimes made in the parent block rather than the expression itself.
fn find_adjustments<'tcx>( fn find_adjustments<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
@ -503,6 +586,7 @@ fn report(cx: &LateContext<'_>, expr: &Expr<'_>, state: State, data: StateData)
State::DerefMethod { State::DerefMethod {
ty_changed_count, ty_changed_count,
is_final_ufcs, is_final_ufcs,
target_mut,
} => { } => {
let mut app = Applicability::MachineApplicable; let mut app = Applicability::MachineApplicable;
let (expr_str, expr_is_macro_call) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app); let (expr_str, expr_is_macro_call) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
@ -517,12 +601,12 @@ fn report(cx: &LateContext<'_>, expr: &Expr<'_>, state: State, data: StateData)
}; };
let addr_of_str = if ty_changed_count < ref_count { let addr_of_str = if ty_changed_count < ref_count {
// Check if a reborrow from &mut T -> &T is required. // Check if a reborrow from &mut T -> &T is required.
if data.target_mut == Mutability::Not && matches!(ty.kind(), ty::Ref(_, _, Mutability::Mut)) { if target_mut == Mutability::Not && matches!(ty.kind(), ty::Ref(_, _, Mutability::Mut)) {
"&*" "&*"
} else { } else {
"" ""
} }
} else if data.target_mut == Mutability::Mut { } else if target_mut == Mutability::Mut {
"&mut " "&mut "
} else { } else {
"&" "&"
@ -538,7 +622,7 @@ fn report(cx: &LateContext<'_>, expr: &Expr<'_>, state: State, data: StateData)
cx, cx,
EXPLICIT_DEREF_METHODS, EXPLICIT_DEREF_METHODS,
data.span, data.span,
match data.target_mut { match target_mut {
Mutability::Not => "explicit `deref` method call", Mutability::Not => "explicit `deref` method call",
Mutability::Mut => "explicit `deref_mut` method call", Mutability::Mut => "explicit `deref_mut` method call",
}, },
@ -547,19 +631,24 @@ fn report(cx: &LateContext<'_>, expr: &Expr<'_>, state: State, data: StateData)
app, app,
); );
}, },
State::DerefedBorrow { .. } => { State::DerefedBorrow {
required_precedence,
msg,
..
} => {
let mut app = Applicability::MachineApplicable; let mut app = Applicability::MachineApplicable;
let snip = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app).0; let snip = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app).0;
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
NEEDLESS_BORROW, NEEDLESS_BORROW,
data.span, data.span,
&format!( msg,
"this expression borrows a reference (`{}`) that is immediately dereferenced by the compiler",
cx.typeck_results().expr_ty(expr),
),
"change this to", "change this to",
snip.into(), if required_precedence > expr.precedence().order() && !has_enclosing_paren(&snip) {
format!("({})", snip)
} else {
snip.into()
},
app, app,
); );
}, },

View file

@ -172,6 +172,9 @@ fn check_variant(cx: &LateContext<'_>, threshold: u64, def: &EnumDef<'_>, item_n
let name = var.ident.name.as_str(); let name = var.ident.name.as_str();
let variant_split = camel_case_split(name); let variant_split = camel_case_split(name);
if variant_split.len() == 1 {
return;
}
pre = pre pre = pre
.iter() .iter()

View file

@ -1,12 +1,16 @@
use clippy_utils::diagnostics::{multispan_sugg, span_lint, span_lint_and_then}; use clippy_utils::diagnostics::{multispan_sugg, span_lint, span_lint_and_then};
use clippy_utils::get_enclosing_block;
use clippy_utils::macros::{find_assert_eq_args, first_node_macro_backtrace}; use clippy_utils::macros::{find_assert_eq_args, first_node_macro_backtrace};
use clippy_utils::source::snippet; use clippy_utils::source::snippet;
use clippy_utils::ty::{implements_trait, is_copy}; use clippy_utils::ty::{implements_trait, is_copy};
use clippy_utils::{ast_utils::is_useless_with_eq_exprs, eq_expr_value, is_in_test_function}; use clippy_utils::{ast_utils::is_useless_with_eq_exprs, eq_expr_value, is_in_test_function};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind}; use rustc_hir::{
def::Res, def_id::DefId, BinOpKind, BorrowKind, Expr, ExprKind, GenericArg, ItemKind, QPath, Ty, TyKind,
};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, TyS};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! { declare_clippy_lint! {
@ -146,6 +150,13 @@ impl<'tcx> LateLintPass<'tcx> for EqOp {
let rty = cx.typeck_results().expr_ty(r); let rty = cx.typeck_results().expr_ty(r);
let lcpy = is_copy(cx, lty); let lcpy = is_copy(cx, lty);
let rcpy = is_copy(cx, rty); let rcpy = is_copy(cx, rty);
if let Some((self_ty, other_ty)) = in_impl(cx, e, trait_id) {
if (are_equal(cx, rty, self_ty) && are_equal(cx, lty, other_ty))
|| (are_equal(cx, rty, other_ty) && are_equal(cx, lty, self_ty))
{
return; // Don't lint
}
}
// either operator autorefs or both args are copyable // either operator autorefs or both args are copyable
if (requires_ref || (lcpy && rcpy)) && implements_trait(cx, lty, trait_id, &[rty.into()]) { if (requires_ref || (lcpy && rcpy)) && implements_trait(cx, lty, trait_id, &[rty.into()]) {
span_lint_and_then( span_lint_and_then(
@ -206,6 +217,14 @@ impl<'tcx> LateLintPass<'tcx> for EqOp {
// &foo == bar // &foo == bar
(&ExprKind::AddrOf(BorrowKind::Ref, _, l), _) => { (&ExprKind::AddrOf(BorrowKind::Ref, _, l), _) => {
let lty = cx.typeck_results().expr_ty(l); let lty = cx.typeck_results().expr_ty(l);
if let Some((self_ty, other_ty)) = in_impl(cx, e, trait_id) {
let rty = cx.typeck_results().expr_ty(right);
if (are_equal(cx, rty, self_ty) && are_equal(cx, lty, other_ty))
|| (are_equal(cx, rty, other_ty) && are_equal(cx, lty, self_ty))
{
return; // Don't lint
}
}
let lcpy = is_copy(cx, lty); let lcpy = is_copy(cx, lty);
if (requires_ref || lcpy) if (requires_ref || lcpy)
&& implements_trait(cx, lty, trait_id, &[cx.typeck_results().expr_ty(right).into()]) && implements_trait(cx, lty, trait_id, &[cx.typeck_results().expr_ty(right).into()])
@ -230,6 +249,14 @@ impl<'tcx> LateLintPass<'tcx> for EqOp {
// foo == &bar // foo == &bar
(_, &ExprKind::AddrOf(BorrowKind::Ref, _, r)) => { (_, &ExprKind::AddrOf(BorrowKind::Ref, _, r)) => {
let rty = cx.typeck_results().expr_ty(r); let rty = cx.typeck_results().expr_ty(r);
if let Some((self_ty, other_ty)) = in_impl(cx, e, trait_id) {
let lty = cx.typeck_results().expr_ty(left);
if (are_equal(cx, rty, self_ty) && are_equal(cx, lty, other_ty))
|| (are_equal(cx, rty, other_ty) && are_equal(cx, lty, self_ty))
{
return; // Don't lint
}
}
let rcpy = is_copy(cx, rty); let rcpy = is_copy(cx, rty);
if (requires_ref || rcpy) if (requires_ref || rcpy)
&& implements_trait(cx, cx.typeck_results().expr_ty(left), trait_id, &[rty.into()]) && implements_trait(cx, cx.typeck_results().expr_ty(left), trait_id, &[rty.into()])
@ -251,3 +278,43 @@ impl<'tcx> LateLintPass<'tcx> for EqOp {
} }
} }
} }
fn in_impl<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, bin_op: DefId) -> Option<(&'tcx Ty<'tcx>, &'tcx Ty<'tcx>)> {
if_chain! {
if let Some(block) = get_enclosing_block(cx, e.hir_id);
if let Some(impl_def_id) = cx.tcx.impl_of_method(block.hir_id.owner.to_def_id());
let item = cx.tcx.hir().expect_item(impl_def_id.expect_local());
if let ItemKind::Impl(item) = &item.kind;
if let Some(of_trait) = &item.of_trait;
if let Some(seg) = of_trait.path.segments.last();
if let Some(Res::Def(_, trait_id)) = seg.res;
if trait_id == bin_op;
if let Some(generic_args) = seg.args;
if let Some(GenericArg::Type(other_ty)) = generic_args.args.last();
then {
Some((item.self_ty, other_ty))
}
else {
None
}
}
}
fn are_equal<'tcx>(cx: &LateContext<'tcx>, middle_ty: &TyS<'_>, hir_ty: &Ty<'_>) -> bool {
if_chain! {
if let ty::Adt(adt_def, _) = middle_ty.kind();
if let Some(local_did) = adt_def.did.as_local();
let item = cx.tcx.hir().expect_item(local_did);
let middle_ty_id = item.def_id.to_def_id();
if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind;
if let Res::Def(_, hir_ty_id) = path.res;
then {
hir_ty_id == middle_ty_id
}
else {
false
}
}
}

View file

@ -179,7 +179,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher {
) )
.and_then(|snip| { .and_then(|snip| {
let i = snip.find("fn")?; let i = snip.find("fn")?;
Some(item.span.lo() + BytePos((i + (&snip[i..]).find('(')?) as u32)) Some(item.span.lo() + BytePos((i + snip[i..].find('(')?) as u32))
}) })
.expect("failed to create span for type parameters"); .expect("failed to create span for type parameters");
Span::new(pos, pos, item.span.ctxt(), item.span.parent()) Span::new(pos, pos, item.span.ctxt(), item.span.parent())

View file

@ -247,7 +247,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
LintId::of(redundant_slicing::REDUNDANT_SLICING), LintId::of(redundant_slicing::REDUNDANT_SLICING),
LintId::of(redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), LintId::of(redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
LintId::of(reference::DEREF_ADDROF), LintId::of(reference::DEREF_ADDROF),
LintId::of(reference::REF_IN_DEREF),
LintId::of(regex::INVALID_REGEX), LintId::of(regex::INVALID_REGEX),
LintId::of(repeat_once::REPEAT_ONCE), LintId::of(repeat_once::REPEAT_ONCE),
LintId::of(returns::LET_AND_RETURN), LintId::of(returns::LET_AND_RETURN),

View file

@ -71,7 +71,6 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL), LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL),
LintId::of(redundant_slicing::REDUNDANT_SLICING), LintId::of(redundant_slicing::REDUNDANT_SLICING),
LintId::of(reference::DEREF_ADDROF), LintId::of(reference::DEREF_ADDROF),
LintId::of(reference::REF_IN_DEREF),
LintId::of(repeat_once::REPEAT_ONCE), LintId::of(repeat_once::REPEAT_ONCE),
LintId::of(strings::STRING_FROM_UTF8_AS_BYTES), LintId::of(strings::STRING_FROM_UTF8_AS_BYTES),
LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS), LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS),

View file

@ -423,7 +423,6 @@ store.register_lints(&[
redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES, redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES,
ref_option_ref::REF_OPTION_REF, ref_option_ref::REF_OPTION_REF,
reference::DEREF_ADDROF, reference::DEREF_ADDROF,
reference::REF_IN_DEREF,
regex::INVALID_REGEX, regex::INVALID_REGEX,
regex::TRIVIAL_REGEX, regex::TRIVIAL_REGEX,
repeat_once::REPEAT_ONCE, repeat_once::REPEAT_ONCE,

View file

@ -581,6 +581,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(move || Box::new(needless_question_mark::NeedlessQuestionMark)); store.register_late_pass(move || Box::new(needless_question_mark::NeedlessQuestionMark));
store.register_late_pass(move || Box::new(casts::Casts::new(msrv))); store.register_late_pass(move || Box::new(casts::Casts::new(msrv)));
store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(msrv))); store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(msrv)));
store.register_late_pass(move || Box::new(map_clone::MapClone::new(msrv)));
store.register_late_pass(|| Box::new(size_of_in_element_count::SizeOfInElementCount)); store.register_late_pass(|| Box::new(size_of_in_element_count::SizeOfInElementCount));
store.register_late_pass(|| Box::new(same_name_method::SameNameMethod)); store.register_late_pass(|| Box::new(same_name_method::SameNameMethod));
@ -591,7 +592,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
msrv, msrv,
)) ))
}); });
store.register_late_pass(|| Box::new(map_clone::MapClone));
store.register_late_pass(|| Box::new(map_err_ignore::MapErrIgnore)); store.register_late_pass(|| Box::new(map_err_ignore::MapErrIgnore));
store.register_late_pass(|| Box::new(shadow::Shadow::default())); store.register_late_pass(|| Box::new(shadow::Shadow::default()));
store.register_late_pass(|| Box::new(unit_types::UnitTypes)); store.register_late_pass(|| Box::new(unit_types::UnitTypes));
@ -703,7 +703,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|| Box::new(mut_key::MutableKeyType)); store.register_late_pass(|| Box::new(mut_key::MutableKeyType));
store.register_late_pass(|| Box::new(modulo_arithmetic::ModuloArithmetic)); store.register_late_pass(|| Box::new(modulo_arithmetic::ModuloArithmetic));
store.register_early_pass(|| Box::new(reference::DerefAddrOf)); store.register_early_pass(|| Box::new(reference::DerefAddrOf));
store.register_early_pass(|| Box::new(reference::RefInDeref));
store.register_early_pass(|| Box::new(double_parens::DoubleParens)); store.register_early_pass(|| Box::new(double_parens::DoubleParens));
store.register_late_pass(|| Box::new(to_string_in_display::ToStringInDisplay::new())); store.register_late_pass(|| Box::new(to_string_in_display::ToStringInDisplay::new()));
store.register_early_pass(|| Box::new(unsafe_removed_from_name::UnsafeNameRemoval)); store.register_early_pass(|| Box::new(unsafe_removed_from_name::UnsafeNameRemoval));
@ -935,6 +934,7 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) {
ls.register_renamed("clippy::if_let_some_result", "clippy::match_result_ok"); ls.register_renamed("clippy::if_let_some_result", "clippy::match_result_ok");
ls.register_renamed("clippy::disallowed_type", "clippy::disallowed_types"); ls.register_renamed("clippy::disallowed_type", "clippy::disallowed_types");
ls.register_renamed("clippy::disallowed_method", "clippy::disallowed_methods"); ls.register_renamed("clippy::disallowed_method", "clippy::disallowed_methods");
ls.register_renamed("clippy::ref_in_deref", "clippy::needless_borrow");
// uplifted lints // uplifted lints
ls.register_renamed("clippy::invalid_ref", "invalid_value"); ls.register_renamed("clippy::invalid_ref", "invalid_value");

View file

@ -13,7 +13,7 @@ use rustc_hir::{
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span; use rustc_span::source_map::Span;
use rustc_span::symbol::{kw, Symbol}; use rustc_span::symbol::{kw, Ident, Symbol};
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
@ -83,7 +83,7 @@ declare_lint_pass!(Lifetimes => [NEEDLESS_LIFETIMES, EXTRA_UNUSED_LIFETIMES]);
impl<'tcx> LateLintPass<'tcx> for Lifetimes { impl<'tcx> LateLintPass<'tcx> for Lifetimes {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
if let ItemKind::Fn(ref sig, ref generics, id) = item.kind { if let ItemKind::Fn(ref sig, ref generics, id) = item.kind {
check_fn_inner(cx, sig.decl, Some(id), generics, item.span, true); check_fn_inner(cx, sig.decl, Some(id), None, generics, item.span, true);
} }
} }
@ -94,6 +94,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes {
cx, cx,
sig.decl, sig.decl,
Some(id), Some(id),
None,
&item.generics, &item.generics,
item.span, item.span,
report_extra_lifetimes, report_extra_lifetimes,
@ -103,11 +104,11 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes {
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
if let TraitItemKind::Fn(ref sig, ref body) = item.kind { if let TraitItemKind::Fn(ref sig, ref body) = item.kind {
let body = match *body { let (body, trait_sig) = match *body {
TraitFn::Required(_) => None, TraitFn::Required(sig) => (None, Some(sig)),
TraitFn::Provided(id) => Some(id), TraitFn::Provided(id) => (Some(id), None),
}; };
check_fn_inner(cx, sig.decl, body, &item.generics, item.span, true); check_fn_inner(cx, sig.decl, body, trait_sig, &item.generics, item.span, true);
} }
} }
} }
@ -124,6 +125,7 @@ fn check_fn_inner<'tcx>(
cx: &LateContext<'tcx>, cx: &LateContext<'tcx>,
decl: &'tcx FnDecl<'_>, decl: &'tcx FnDecl<'_>,
body: Option<BodyId>, body: Option<BodyId>,
trait_sig: Option<&[Ident]>,
generics: &'tcx Generics<'_>, generics: &'tcx Generics<'_>,
span: Span, span: Span,
report_extra_lifetimes: bool, report_extra_lifetimes: bool,
@ -165,7 +167,7 @@ fn check_fn_inner<'tcx>(
} }
} }
} }
if could_use_elision(cx, decl, body, generics.params) { if could_use_elision(cx, decl, body, trait_sig, generics.params) {
span_lint( span_lint(
cx, cx,
NEEDLESS_LIFETIMES, NEEDLESS_LIFETIMES,
@ -179,10 +181,31 @@ fn check_fn_inner<'tcx>(
} }
} }
// elision doesn't work for explicit self types, see rust-lang/rust#69064
fn explicit_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: Option<Ident>) -> bool {
if_chain! {
if let Some(ident) = ident;
if ident.name == kw::SelfLower;
if !func.implicit_self.has_implicit_self();
if let Some(self_ty) = func.inputs.first();
then {
let mut visitor = RefVisitor::new(cx);
visitor.visit_ty(self_ty);
!visitor.all_lts().is_empty()
}
else {
false
}
}
}
fn could_use_elision<'tcx>( fn could_use_elision<'tcx>(
cx: &LateContext<'tcx>, cx: &LateContext<'tcx>,
func: &'tcx FnDecl<'_>, func: &'tcx FnDecl<'_>,
body: Option<BodyId>, body: Option<BodyId>,
trait_sig: Option<&[Ident]>,
named_generics: &'tcx [GenericParam<'_>], named_generics: &'tcx [GenericParam<'_>],
) -> bool { ) -> bool {
// There are two scenarios where elision works: // There are two scenarios where elision works:
@ -233,11 +256,24 @@ fn could_use_elision<'tcx>(
let input_lts = input_visitor.lts; let input_lts = input_visitor.lts;
let output_lts = output_visitor.lts; let output_lts = output_visitor.lts;
if let Some(trait_sig) = trait_sig {
if explicit_self_type(cx, func, trait_sig.first().copied()) {
return false;
}
}
if let Some(body_id) = body { if let Some(body_id) = body {
let body = cx.tcx.hir().body(body_id);
let first_ident = body.params.first().and_then(|param| param.pat.simple_ident());
if explicit_self_type(cx, func, first_ident) {
return false;
}
let mut checker = BodyLifetimeChecker { let mut checker = BodyLifetimeChecker {
lifetimes_used_in_body: false, lifetimes_used_in_body: false,
}; };
checker.visit_expr(&cx.tcx.hir().body(body_id).value); checker.visit_expr(&body.value);
if checker.lifetimes_used_in_body { if checker.lifetimes_used_in_body {
return false; return false;
} }

View file

@ -35,7 +35,8 @@ struct PathAndSpan {
span: Span, span: Span,
} }
/// `MacroRefData` includes the name of the macro. /// `MacroRefData` includes the name of the macro
/// and the path from `SourceMap::span_to_filename`.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct MacroRefData { pub struct MacroRefData {
name: String, name: String,

View file

@ -6,8 +6,8 @@ use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::intravisit::FnKind; use rustc_hir::intravisit::FnKind;
use rustc_hir::{ use rustc_hir::{
Term, AsyncGeneratorKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, GeneratorKind, GenericArg, GenericBound, HirId, AsyncGeneratorKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, GeneratorKind, GenericArg, GenericBound, HirId,
IsAsync, ItemKind, LifetimeName, TraitRef, Ty, TyKind, TypeBindingKind, IsAsync, ItemKind, LifetimeName, Term, TraitRef, Ty, TyKind, TypeBindingKind,
}; };
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};

View file

@ -72,6 +72,8 @@ impl<'tcx> LateLintPass<'tcx> for ManualBits {
} }
} }
} }
extract_msrv_attr!(LateContext);
} }
fn get_one_size_of_ty<'tcx>( fn get_one_size_of_ty<'tcx>(

View file

@ -1,7 +1,7 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability; use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::{is_copy, is_type_diagnostic_item}; use clippy_utils::ty::{is_copy, is_type_diagnostic_item};
use clippy_utils::{is_trait_method, peel_blocks}; use clippy_utils::{is_trait_method, meets_msrv, msrvs, peel_blocks};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir as hir; use rustc_hir as hir;
@ -9,7 +9,8 @@ use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::mir::Mutability; use rustc_middle::mir::Mutability;
use rustc_middle::ty; use rustc_middle::ty;
use rustc_middle::ty::adjustment::Adjust; use rustc_middle::ty::adjustment::Adjust;
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::symbol::Ident; use rustc_span::symbol::Ident;
use rustc_span::{sym, Span}; use rustc_span::{sym, Span};
@ -42,7 +43,17 @@ declare_clippy_lint! {
"using `iterator.map(|x| x.clone())`, or dereferencing closures for `Copy` types" "using `iterator.map(|x| x.clone())`, or dereferencing closures for `Copy` types"
} }
declare_lint_pass!(MapClone => [MAP_CLONE]); pub struct MapClone {
msrv: Option<RustcVersion>,
}
impl_lint_pass!(MapClone => [MAP_CLONE]);
impl MapClone {
pub fn new(msrv: Option<RustcVersion>) -> Self {
Self { msrv }
}
}
impl<'tcx> LateLintPass<'tcx> for MapClone { impl<'tcx> LateLintPass<'tcx> for MapClone {
fn check_expr(&mut self, cx: &LateContext<'_>, e: &hir::Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'_>, e: &hir::Expr<'_>) {
@ -65,7 +76,7 @@ impl<'tcx> LateLintPass<'tcx> for MapClone {
hir::BindingAnnotation::Unannotated, .., name, None hir::BindingAnnotation::Unannotated, .., name, None
) = inner.kind { ) = inner.kind {
if ident_eq(name, closure_expr) { if ident_eq(name, closure_expr) {
lint(cx, e.span, args[0].span, true); self.lint_explicit_closure(cx, e.span, args[0].span, true);
} }
}, },
hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, .., name, None) => { hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, .., name, None) => {
@ -73,7 +84,7 @@ impl<'tcx> LateLintPass<'tcx> for MapClone {
hir::ExprKind::Unary(hir::UnOp::Deref, inner) => { hir::ExprKind::Unary(hir::UnOp::Deref, inner) => {
if ident_eq(name, 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); self.lint_explicit_closure(cx, e.span, args[0].span, true);
} }
} }
}, },
@ -90,7 +101,7 @@ impl<'tcx> LateLintPass<'tcx> for MapClone {
if let ty::Ref(_, ty, mutability) = obj_ty.kind() { if let ty::Ref(_, ty, mutability) = obj_ty.kind() {
if matches!(mutability, Mutability::Not) { if matches!(mutability, Mutability::Not) {
let copy = is_copy(cx, ty); let copy = is_copy(cx, ty);
lint(cx, e.span, args[0].span, copy); self.lint_explicit_closure(cx, e.span, args[0].span, copy);
} }
} else { } else {
lint_needless_cloning(cx, e.span, args[0].span); lint_needless_cloning(cx, e.span, args[0].span);
@ -105,6 +116,8 @@ impl<'tcx> LateLintPass<'tcx> for MapClone {
} }
} }
} }
extract_msrv_attr!(LateContext);
} }
fn ident_eq(name: Ident, path: &hir::Expr<'_>) -> bool { fn ident_eq(name: Ident, path: &hir::Expr<'_>) -> bool {
@ -127,31 +140,30 @@ fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) {
); );
} }
fn lint(cx: &LateContext<'_>, replace: Span, root: Span, copied: bool) { impl MapClone {
let mut applicability = Applicability::MachineApplicable; fn lint_explicit_closure(&self, cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool) {
if copied { let mut applicability = Applicability::MachineApplicable;
let message = if is_copy {
"you are using an explicit closure for copying elements"
} else {
"you are using an explicit closure for cloning elements"
};
let sugg_method = if is_copy && meets_msrv(self.msrv.as_ref(), &msrvs::ITERATOR_COPIED) {
"copied"
} else {
"cloned"
};
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
MAP_CLONE, MAP_CLONE,
replace, replace,
"you are using an explicit closure for copying elements", message,
"consider calling the dedicated `copied` method", &format!("consider calling the dedicated `{}` method", sugg_method),
format!( format!(
"{}.copied()", "{}.{}()",
snippet_with_applicability(cx, root, "..", &mut applicability) snippet_with_applicability(cx, root, "..", &mut applicability),
), sugg_method,
applicability,
);
} else {
span_lint_and_sugg(
cx,
MAP_CLONE,
replace,
"you are using an explicit closure for cloning elements",
"consider calling the dedicated `cloned` method",
format!(
"{}.cloned()",
snippet_with_applicability(cx, root, "..", &mut applicability)
), ),
applicability, applicability,
); );

View file

@ -30,7 +30,7 @@ use rustc_middle::ty::{self, Ty, TyS, VariantDef};
use rustc_semver::RustcVersion; use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::{Span, Spanned}; use rustc_span::source_map::{Span, Spanned};
use rustc_span::sym; use rustc_span::{sym, symbol::kw};
use std::cmp::Ordering; use std::cmp::Ordering;
use std::collections::hash_map::Entry; use std::collections::hash_map::Entry;
@ -961,13 +961,13 @@ fn check_wild_err_arm<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm
let path_str = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false)); let path_str = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false));
if path_str == "Err" { if path_str == "Err" {
let mut matching_wild = inner.iter().any(is_wild); let mut matching_wild = inner.iter().any(is_wild);
let mut ident_bind_name = String::from("_"); let mut ident_bind_name = kw::Underscore;
if !matching_wild { if !matching_wild {
// Looking for unused bindings (i.e.: `_e`) // Looking for unused bindings (i.e.: `_e`)
for pat in inner.iter() { for pat in inner.iter() {
if let PatKind::Binding(_, id, ident, None) = pat.kind { if let PatKind::Binding(_, id, ident, None) = pat.kind {
if ident.as_str().starts_with('_') && !is_local_used(cx, arm.body, id) { if ident.as_str().starts_with('_') && !is_local_used(cx, arm.body, id) {
ident_bind_name = ident.name.as_str().to_string(); ident_bind_name = ident.name;
matching_wild = true; matching_wild = true;
} }
} }
@ -982,7 +982,7 @@ fn check_wild_err_arm<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm
span_lint_and_note(cx, span_lint_and_note(cx,
MATCH_WILD_ERR_ARM, MATCH_WILD_ERR_ARM,
arm.pat.span, arm.pat.span,
&format!("`Err({})` matches all errors", &ident_bind_name), &format!("`Err({})` matches all errors", ident_bind_name),
None, None,
"match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable", "match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable",
); );

View file

@ -30,7 +30,7 @@ pub(super) fn check<'tcx>(
hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, expr) => expr, hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, expr) => expr,
hir::ExprKind::MethodCall(method_name, call_args, _) => { hir::ExprKind::MethodCall(method_name, call_args, _) => {
if call_args.len() == 1 if call_args.len() == 1
&& (method_name.ident.name == sym::as_str || method_name.ident.name == sym!(as_ref)) && (method_name.ident.name == sym::as_str || method_name.ident.name == sym::as_ref)
&& { && {
let arg_type = cx.typeck_results().expr_ty(&call_args[0]); let arg_type = cx.typeck_results().expr_ty(&call_args[0]);
let base_type = arg_type.peel_refs(); let base_type = arg_type.peel_refs();

View file

@ -2180,8 +2180,8 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) { for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) {
if let ty::PredicateKind::Projection(projection_predicate) = predicate.kind().skip_binder() { if let ty::PredicateKind::Projection(projection_predicate) = predicate.kind().skip_binder() {
let assoc_ty = match projection_predicate.term { let assoc_ty = match projection_predicate.term {
ty::Term::Ty(ty) => ty, ty::Term::Ty(ty) => ty,
ty::Term::Const(_c) => continue, ty::Term::Const(_c) => continue,
}; };
// walk the associated type and check for Self // walk the associated type and check for Self
if let Some(self_adt) = self_ty.ty_adt_def() { if let Some(self_adt) = self_ty.ty_adt_def() {

View file

@ -7,7 +7,8 @@
use clippy_utils::attrs::is_doc_hidden; use clippy_utils::attrs::is_doc_hidden;
use clippy_utils::diagnostics::span_lint; use clippy_utils::diagnostics::span_lint;
use rustc_ast::ast; use if_chain::if_chain;
use rustc_ast::ast::{self, MetaItem, MetaItemKind};
use rustc_hir as hir; use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::ty; use rustc_middle::ty;
@ -57,6 +58,20 @@ impl MissingDoc {
*self.doc_hidden_stack.last().expect("empty doc_hidden_stack") *self.doc_hidden_stack.last().expect("empty doc_hidden_stack")
} }
fn has_include(meta: Option<MetaItem>) -> bool {
if_chain! {
if let Some(meta) = meta;
if let MetaItemKind::List(list) = meta.kind;
if let Some(meta) = list.get(0);
if let Some(name) = meta.ident();
then {
name.name == sym::include
} else {
false
}
}
}
fn check_missing_docs_attrs( fn check_missing_docs_attrs(
&self, &self,
cx: &LateContext<'_>, cx: &LateContext<'_>,
@ -80,7 +95,9 @@ impl MissingDoc {
return; return;
} }
let has_doc = attrs.iter().any(|a| a.doc_str().is_some()); let has_doc = attrs
.iter()
.any(|a| a.doc_str().is_some() || Self::has_include(a.meta()));
if !has_doc { if !has_doc {
span_lint( span_lint(
cx, cx,

View file

@ -92,10 +92,6 @@ impl<'a, 'tcx> Visitor<'tcx> for MutArgVisitor<'a, 'tcx> {
self.found = true; self.found = true;
return; return;
}, },
ExprKind::If(..) => {
self.found = true;
return;
},
ExprKind::Path(_) => { ExprKind::Path(_) => {
if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) { if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) {
if adj if adj

View file

@ -4,7 +4,7 @@ use clippy_utils::source::snippet;
use if_chain::if_chain; use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::LangItem::{OptionSome, ResultOk}; use rustc_hir::LangItem::{OptionSome, ResultOk};
use rustc_hir::{Body, Expr, ExprKind, LangItem, MatchSource, QPath}; use rustc_hir::{AsyncGeneratorKind, Block, Body, Expr, ExprKind, GeneratorKind, LangItem, MatchSource, QPath};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::TyS; use rustc_middle::ty::TyS;
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
@ -88,7 +88,26 @@ impl LateLintPass<'_> for NeedlessQuestionMark {
} }
fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) { fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) {
check(cx, body.value.peel_blocks()); if let Some(GeneratorKind::Async(AsyncGeneratorKind::Fn)) = body.generator_kind {
if let ExprKind::Block(
Block {
expr:
Some(Expr {
kind: ExprKind::DropTemps(async_body),
..
}),
..
},
_,
) = body.value.kind
{
if let ExprKind::Block(Block { expr: Some(expr), .. }, ..) = async_body.kind {
check(cx, expr);
}
}
} else {
check(cx, body.value.peel_blocks());
}
} }
} }

View file

@ -101,7 +101,7 @@ fn check_lit(cx: &EarlyContext<'_>, lit: &Lit, span: Span, is_string: bool) {
// construct a replacement escape // construct a replacement escape
// the maximum value is \077, or \x3f, so u8 is sufficient here // the maximum value is \077, or \x3f, so u8 is sufficient here
if let Ok(n) = u8::from_str_radix(&contents[from + 1..to], 8) { if let Ok(n) = u8::from_str_radix(&contents[from + 1..to], 8) {
write!(&mut suggest_1, "\\x{:02x}", n).unwrap(); write!(suggest_1, "\\x{:02x}", n).unwrap();
} }
// append the null byte as \x00 and the following digits literally // append the null byte as \x00 and the following digits literally

View file

@ -1,30 +1,36 @@
//! Checks for usage of `&Vec[_]` and `&String`. //! Checks for usage of `&Vec[_]` and `&String`.
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
use clippy_utils::ptr::get_spans;
use clippy_utils::source::snippet_opt; use clippy_utils::source::snippet_opt;
use clippy_utils::ty::walk_ptrs_hir_ty; use clippy_utils::ty::expr_sig;
use clippy_utils::{expr_path_res, is_lint_allowed, match_any_diagnostic_items, paths}; use clippy_utils::{
expr_path_res, get_expr_use_or_unification_node, is_lint_allowed, match_any_diagnostic_items, path_to_local, paths,
};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::def::Res; use rustc_hir::def_id::DefId;
use rustc_hir::hir_id::HirIdMap;
use rustc_hir::intravisit::{walk_expr, Visitor};
use rustc_hir::{ use rustc_hir::{
BinOpKind, BodyId, Expr, ExprKind, FnDecl, FnRetTy, GenericArg, Impl, ImplItem, ImplItemKind, Item, ItemKind, self as hir, AnonConst, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnDecl, FnRetTy, GenericArg,
Lifetime, MutTy, Mutability, Node, PathSegment, QPath, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, ImplItemKind, ItemKind, Lifetime, LifetimeName, Mutability, Node, Param, ParamName, PatKind, QPath, TraitFn,
TraitItem, TraitItemKind, TyKind,
}; };
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::nested_filter;
use rustc_middle::ty::{self, AssocItems, AssocKind, Ty};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span; use rustc_span::source_map::Span;
use rustc_span::symbol::Symbol; use rustc_span::symbol::Symbol;
use rustc_span::{sym, MultiSpan}; use rustc_span::{sym, MultiSpan};
use std::borrow::Cow; use std::fmt;
use std::iter;
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
/// This lint checks for function arguments of type `&String` /// This lint checks for function arguments of type `&String`, `&Vec`,
/// or `&Vec` unless the references are mutable. It will also suggest you /// `&PathBuf`, and `Cow<_>`. It will also suggest you replace `.clone()` calls
/// replace `.clone()` calls with the appropriate `.to_owned()`/`to_string()` /// with the appropriate `.to_owned()`/`to_string()` calls.
/// calls.
/// ///
/// ### Why is this bad? /// ### Why is this bad?
/// Requiring the argument to be of the specific size /// Requiring the argument to be of the specific size
@ -32,28 +38,7 @@ declare_clippy_lint! {
/// or `&str` usually suffice and can be obtained from other types, too. /// or `&str` usually suffice and can be obtained from other types, too.
/// ///
/// ### Known problems /// ### Known problems
/// The lint does not follow data. So if you have an /// There may be `fn(&Vec)`-typed references pointing to your function.
/// argument `x` and write `let y = x; y.clone()` the lint will not suggest
/// changing that `.clone()` to `.to_owned()`.
///
/// Other functions called from this function taking a `&String` or `&Vec`
/// 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<Vec<T>>`. 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<u8>) -> bool {
/// let vec_of_vecs: Vec<Vec<u8>> = 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 /// 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 /// suggestions. You then have the choice to undo your changes or change the
/// type of the reference. /// type of the reference.
@ -155,32 +140,86 @@ declare_clippy_lint! {
declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF, INVALID_NULL_PTR_USAGE]); declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF, INVALID_NULL_PTR_USAGE]);
impl<'tcx> LateLintPass<'tcx> for Ptr { impl<'tcx> LateLintPass<'tcx> for Ptr {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
if let ItemKind::Fn(ref sig, _, body_id) = item.kind {
check_fn(cx, sig.decl, Some(body_id));
}
}
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) {
if let ImplItemKind::Fn(ref sig, body_id) = item.kind {
let parent_item = cx.tcx.hir().get_parent_item(item.hir_id());
if let Some(Node::Item(it)) = cx.tcx.hir().find_by_def_id(parent_item) {
if let ItemKind::Impl(Impl { of_trait: Some(_), .. }) = it.kind {
return; // ignore trait impls
}
}
check_fn(cx, sig.decl, Some(body_id));
}
}
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
if let TraitItemKind::Fn(ref sig, ref trait_method) = item.kind { if let TraitItemKind::Fn(sig, trait_method) = &item.kind {
let body_id = if let TraitFn::Provided(b) = *trait_method { if matches!(trait_method, TraitFn::Provided(_)) {
Some(b) // Handled by check body.
} else { return;
None }
};
check_fn(cx, sig.decl, body_id); check_mut_from_ref(cx, sig.decl);
for arg in check_fn_args(
cx,
cx.tcx.fn_sig(item.def_id).skip_binder().inputs(),
sig.decl.inputs,
&[],
) {
span_lint_and_sugg(
cx,
PTR_ARG,
arg.span,
&arg.build_msg(),
"change this to",
format!("{}{}", arg.ref_prefix, arg.deref_ty.display(cx)),
Applicability::Unspecified,
);
}
}
}
fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
let hir = cx.tcx.hir();
let mut parents = hir.parent_iter(body.value.hir_id);
let (item_id, decl) = match parents.next() {
Some((_, Node::Item(i))) => {
if let ItemKind::Fn(sig, ..) = &i.kind {
(i.def_id, sig.decl)
} else {
return;
}
},
Some((_, Node::ImplItem(i))) => {
if !matches!(parents.next(),
Some((_, Node::Item(i))) if matches!(&i.kind, ItemKind::Impl(i) if i.of_trait.is_none())
) {
return;
}
if let ImplItemKind::Fn(sig, _) = &i.kind {
(i.def_id, sig.decl)
} else {
return;
}
},
Some((_, Node::TraitItem(i))) => {
if let TraitItemKind::Fn(sig, _) = &i.kind {
(i.def_id, sig.decl)
} else {
return;
}
},
_ => return,
};
check_mut_from_ref(cx, decl);
let sig = cx.tcx.fn_sig(item_id).skip_binder();
let lint_args: Vec<_> = check_fn_args(cx, sig.inputs(), decl.inputs, body.params).collect();
let results = check_ptr_arg_usage(cx, body, &lint_args);
for (result, args) in results.iter().zip(lint_args.iter()).filter(|(r, _)| !r.skip) {
span_lint_and_then(cx, PTR_ARG, args.span, &args.build_msg(), |diag| {
diag.multipart_suggestion(
"change this to",
iter::once((args.span, format!("{}{}", args.ref_prefix, args.deref_ty.display(cx))))
.chain(result.replacements.iter().map(|r| {
(
r.expr_span,
format!("{}{}", snippet_opt(cx, r.self_span).unwrap(), r.replacement),
)
}))
.collect(),
Applicability::Unspecified,
);
});
} }
} }
@ -247,154 +286,206 @@ fn check_invalid_ptr_usage<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
} }
} }
#[allow(clippy::too_many_lines)] #[derive(Default)]
fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, opt_body_id: Option<BodyId>) { struct PtrArgResult {
let body = opt_body_id.map(|id| cx.tcx.hir().body(id)); skip: bool,
replacements: Vec<PtrArgReplacement>,
}
for (idx, arg) in decl.inputs.iter().enumerate() { struct PtrArgReplacement {
// Honor the allow attribute on parameters. See issue 5644. expr_span: Span,
if let Some(body) = &body { self_span: Span,
if is_lint_allowed(cx, PTR_ARG, body.params[idx].hir_id) { replacement: &'static str,
continue; }
}
struct PtrArg<'tcx> {
idx: usize,
span: Span,
ty_did: DefId,
ty_name: Symbol,
method_renames: &'static [(&'static str, &'static str)],
ref_prefix: RefPrefix,
deref_ty: DerefTy<'tcx>,
deref_assoc_items: Option<(DefId, &'tcx AssocItems<'tcx>)>,
}
impl PtrArg<'_> {
fn build_msg(&self) -> String {
format!(
"writing `&{}{}` instead of `&{}{}` involves a new object where a slice will do",
self.ref_prefix.mutability.prefix_str(),
self.ty_name,
self.ref_prefix.mutability.prefix_str(),
self.deref_ty.argless_str(),
)
}
}
struct RefPrefix {
lt: LifetimeName,
mutability: Mutability,
}
impl fmt::Display for RefPrefix {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use fmt::Write;
f.write_char('&')?;
match self.lt {
LifetimeName::Param(ParamName::Plain(name)) => {
name.fmt(f)?;
f.write_char(' ')?;
},
LifetimeName::Underscore => f.write_str("'_ ")?,
LifetimeName::Static => f.write_str("'static ")?,
_ => (),
} }
f.write_str(self.mutability.prefix_str())
}
}
let (item_name, path) = if_chain! { struct DerefTyDisplay<'a, 'tcx>(&'a LateContext<'tcx>, &'a DerefTy<'tcx>);
if let TyKind::Rptr(_, MutTy { ty, mutbl: Mutability::Not }) = arg.kind; impl fmt::Display for DerefTyDisplay<'_, '_> {
if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind; fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Res::Def(_, did) = path.res; use std::fmt::Write;
if let Some(item_name) = cx.tcx.get_diagnostic_name(did); match self.1 {
then { DerefTy::Str => f.write_str("str"),
(item_name, path) DerefTy::Path => f.write_str("Path"),
} else { DerefTy::Slice(hir_ty, ty) => {
continue f.write_char('[')?;
} match hir_ty.and_then(|s| snippet_opt(self.0, s)) {
}; Some(s) => f.write_str(&s)?,
None => ty.fmt(f)?,
}
f.write_char(']')
},
}
}
}
match item_name { enum DerefTy<'tcx> {
sym::Vec => { Str,
if let Some(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_owned()")]) { Path,
span_lint_and_then( Slice(Option<Span>, Ty<'tcx>),
cx, }
PTR_ARG, impl<'tcx> DerefTy<'tcx> {
arg.span, fn argless_str(&self) -> &'static str {
"writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used \ match *self {
with non-Vec-based slices", Self::Str => "str",
|diag| { Self::Path => "Path",
if let Some(ref snippet) = get_only_generic_arg_snippet(cx, arg) { Self::Slice(..) => "[_]",
diag.span_suggestion(
arg.span,
"change this to",
format!("&[{}]", snippet),
Applicability::Unspecified,
);
}
for (clonespan, suggestion) in spans {
diag.span_suggestion(
clonespan,
&snippet_opt(cx, clonespan).map_or("change the call to".into(), |x| {
Cow::Owned(format!("change `{}` to", x))
}),
suggestion.into(),
Applicability::Unspecified,
);
}
},
);
}
},
sym::String => {
if let Some(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_string()"), ("as_str", "")]) {
span_lint_and_then(
cx,
PTR_ARG,
arg.span,
"writing `&String` instead of `&str` involves a new object where a slice will do",
|diag| {
diag.span_suggestion(arg.span, "change this to", "&str".into(), Applicability::Unspecified);
for (clonespan, suggestion) in spans {
diag.span_suggestion_short(
clonespan,
&snippet_opt(cx, clonespan).map_or("change the call to".into(), |x| {
Cow::Owned(format!("change `{}` to", x))
}),
suggestion.into(),
Applicability::Unspecified,
);
}
},
);
}
},
sym::PathBuf => {
if let Some(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_path_buf()"), ("as_path", "")]) {
span_lint_and_then(
cx,
PTR_ARG,
arg.span,
"writing `&PathBuf` instead of `&Path` involves a new object where a slice will do",
|diag| {
diag.span_suggestion(
arg.span,
"change this to",
"&Path".into(),
Applicability::Unspecified,
);
for (clonespan, suggestion) in spans {
diag.span_suggestion_short(
clonespan,
&snippet_opt(cx, clonespan).map_or("change the call to".into(), |x| {
Cow::Owned(format!("change `{}` to", x))
}),
suggestion.into(),
Applicability::Unspecified,
);
}
},
);
}
},
sym::Cow => {
if_chain! {
if let [ref bx] = *path.segments;
if let Some(params) = bx.args;
if !params.parenthesized;
if let Some(inner) = params.args.iter().find_map(|arg| match arg {
GenericArg::Type(ty) => Some(ty),
_ => None,
});
let replacement = snippet_opt(cx, inner.span);
if let Some(r) = replacement;
then {
span_lint_and_sugg(
cx,
PTR_ARG,
arg.span,
"using a reference to `Cow` is not recommended",
"change this to",
"&".to_owned() + &r,
Applicability::Unspecified,
);
}
}
},
_ => {},
} }
} }
fn display<'a>(&'a self, cx: &'a LateContext<'tcx>) -> DerefTyDisplay<'a, 'tcx> {
DerefTyDisplay(cx, self)
}
}
fn check_fn_args<'cx, 'tcx: 'cx>(
cx: &'cx LateContext<'tcx>,
tys: &'tcx [Ty<'_>],
hir_tys: &'tcx [hir::Ty<'_>],
params: &'tcx [Param<'_>],
) -> impl Iterator<Item = PtrArg<'tcx>> + 'cx {
tys.iter()
.zip(hir_tys.iter())
.enumerate()
.filter_map(|(i, (ty, hir_ty))| {
if_chain! {
if let ty::Ref(_, ty, mutability) = *ty.kind();
if let ty::Adt(adt, substs) = *ty.kind();
if let TyKind::Rptr(lt, ref ty) = hir_ty.kind;
if let TyKind::Path(QPath::Resolved(None, path)) = ty.ty.kind;
// Check that the name as typed matches the actual name of the type.
// e.g. `fn foo(_: &Foo)` shouldn't trigger the lint when `Foo` is an alias for `Vec`
if let [.., name] = path.segments;
if cx.tcx.item_name(adt.did) == name.ident.name;
if !is_lint_allowed(cx, PTR_ARG, hir_ty.hir_id);
if params.get(i).map_or(true, |p| !is_lint_allowed(cx, PTR_ARG, p.hir_id));
then {
let (method_renames, deref_ty, deref_impl_id) = match cx.tcx.get_diagnostic_name(adt.did) {
Some(sym::Vec) => (
[("clone", ".to_owned()")].as_slice(),
DerefTy::Slice(
name.args
.and_then(|args| args.args.first())
.and_then(|arg| if let GenericArg::Type(ty) = arg {
Some(ty.span)
} else {
None
}),
substs.type_at(0),
),
cx.tcx.lang_items().slice_impl()
),
Some(sym::String) => (
[("clone", ".to_owned()"), ("as_str", "")].as_slice(),
DerefTy::Str,
cx.tcx.lang_items().str_impl()
),
Some(sym::PathBuf) => (
[("clone", ".to_path_buf()"), ("as_path", "")].as_slice(),
DerefTy::Path,
None,
),
Some(sym::Cow) => {
let ty_name = name.args
.and_then(|args| {
args.args.iter().find_map(|a| match a {
GenericArg::Type(x) => Some(x),
_ => None,
})
})
.and_then(|arg| snippet_opt(cx, arg.span))
.unwrap_or_else(|| substs.type_at(1).to_string());
span_lint_and_sugg(
cx,
PTR_ARG,
hir_ty.span,
"using a reference to `Cow` is not recommended",
"change this to",
format!("&{}{}", mutability.prefix_str(), ty_name),
Applicability::Unspecified,
);
return None;
},
_ => return None,
};
return Some(PtrArg {
idx: i,
span: hir_ty.span,
ty_did: adt.did,
ty_name: name.ident.name,
method_renames,
ref_prefix: RefPrefix {
lt: lt.name,
mutability,
},
deref_ty,
deref_assoc_items: deref_impl_id.map(|id| (id, cx.tcx.associated_items(id))),
});
}
}
None
})
}
fn check_mut_from_ref(cx: &LateContext<'_>, decl: &FnDecl<'_>) {
if let FnRetTy::Return(ty) = decl.output { if let FnRetTy::Return(ty) = decl.output {
if let Some((out, Mutability::Mut, _)) = get_rptr_lm(ty) { if let Some((out, Mutability::Mut, _)) = get_rptr_lm(ty) {
let mut immutables = vec![]; let mut immutables = vec![];
for (_, ref mutbl, ref argspan) in decl for (_, mutbl, argspan) in decl
.inputs .inputs
.iter() .iter()
.filter_map(get_rptr_lm) .filter_map(get_rptr_lm)
.filter(|&(lt, _, _)| lt.name == out.name) .filter(|&(lt, _, _)| lt.name == out.name)
{ {
if *mutbl == Mutability::Mut { if mutbl == Mutability::Mut {
return; return;
} }
immutables.push(*argspan); immutables.push(argspan);
} }
if immutables.is_empty() { if immutables.is_empty() {
return; return;
@ -413,24 +504,158 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, opt_body_id: Option<BodyId>
} }
} }
fn get_only_generic_arg_snippet(cx: &LateContext<'_>, arg: &Ty<'_>) -> Option<String> { #[allow(clippy::too_many_lines)]
if_chain! { fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: &[PtrArg<'tcx>]) -> Vec<PtrArgResult> {
if let TyKind::Path(QPath::Resolved(_, path)) = walk_ptrs_hir_ty(arg).kind; struct V<'cx, 'tcx> {
if let Some(&PathSegment{args: Some(parameters), ..}) = path.segments.last(); cx: &'cx LateContext<'tcx>,
let types: Vec<_> = parameters.args.iter().filter_map(|arg| match arg { /// Map from a local id to which argument it came from (index into `Self::args` and
GenericArg::Type(ty) => Some(ty), /// `Self::results`)
_ => None, bindings: HirIdMap<usize>,
}).collect(); /// The arguments being checked.
if types.len() == 1; args: &'cx [PtrArg<'tcx>],
then { /// The results for each argument (len should match args.len)
snippet_opt(cx, types[0].span) results: Vec<PtrArgResult>,
} else { /// The number of arguments which can't be linted. Used to return early.
None skip_count: usize,
}
impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
type NestedFilter = nested_filter::OnlyBodies;
fn nested_visit_map(&mut self) -> Self::Map {
self.cx.tcx.hir()
}
fn visit_anon_const(&mut self, _: &'tcx AnonConst) {}
fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
if self.skip_count == self.args.len() {
return;
}
// Check if this is local we care about
let args_idx = match path_to_local(e).and_then(|id| self.bindings.get(&id)) {
Some(&i) => i,
None => return walk_expr(self, e),
};
let args = &self.args[args_idx];
let result = &mut self.results[args_idx];
// Helper function to handle early returns.
let mut set_skip_flag = || {
if result.skip {
self.skip_count += 1;
}
result.skip = true;
};
match get_expr_use_or_unification_node(self.cx.tcx, e) {
Some((Node::Stmt(_), _)) => (),
Some((Node::Local(l), _)) => {
// Only trace simple bindings. e.g `let x = y;`
if let PatKind::Binding(BindingAnnotation::Unannotated, id, _, None) = l.pat.kind {
self.bindings.insert(id, args_idx);
} else {
set_skip_flag();
}
},
Some((Node::Expr(e), child_id)) => match e.kind {
ExprKind::Call(f, expr_args) => {
let i = expr_args.iter().position(|arg| arg.hir_id == child_id).unwrap_or(0);
if expr_sig(self.cx, f)
.map(|sig| sig.input(i).skip_binder().peel_refs())
.map_or(true, |ty| match *ty.kind() {
ty::Param(_) => true,
ty::Adt(def, _) => def.did == args.ty_did,
_ => false,
})
{
// Passed to a function taking the non-dereferenced type.
set_skip_flag();
}
},
ExprKind::MethodCall(name, expr_args @ [self_arg, ..], _) => {
let i = expr_args.iter().position(|arg| arg.hir_id == child_id).unwrap_or(0);
if i == 0 {
// Check if the method can be renamed.
let name = name.ident.as_str();
if let Some((_, replacement)) = args.method_renames.iter().find(|&&(x, _)| x == name) {
result.replacements.push(PtrArgReplacement {
expr_span: e.span,
self_span: self_arg.span,
replacement,
});
return;
}
}
let id = if let Some(x) = self.cx.typeck_results().type_dependent_def_id(e.hir_id) {
x
} else {
set_skip_flag();
return;
};
match *self.cx.tcx.fn_sig(id).skip_binder().inputs()[i].peel_refs().kind() {
ty::Param(_) => {
set_skip_flag();
},
// If the types match check for methods which exist on both types. e.g. `Vec::len` and
// `slice::len`
ty::Adt(def, _)
if def.did == args.ty_did
&& (i != 0
|| self.cx.tcx.trait_of_item(id).is_some()
|| !args.deref_assoc_items.map_or(false, |(id, items)| {
items
.find_by_name_and_kind(self.cx.tcx, name.ident, AssocKind::Fn, id)
.is_some()
})) =>
{
set_skip_flag();
},
_ => (),
}
},
// Indexing is fine for currently supported types.
ExprKind::Index(e, _) if e.hir_id == child_id => (),
_ => set_skip_flag(),
},
_ => set_skip_flag(),
}
} }
} }
let mut skip_count = 0;
let mut results = args.iter().map(|_| PtrArgResult::default()).collect::<Vec<_>>();
let mut v = V {
cx,
bindings: args
.iter()
.enumerate()
.filter_map(|(i, arg)| {
let param = &body.params[arg.idx];
match param.pat.kind {
PatKind::Binding(BindingAnnotation::Unannotated, id, _, None)
if !is_lint_allowed(cx, PTR_ARG, param.hir_id) =>
{
Some((id, i))
},
_ => {
skip_count += 1;
results[arg.idx].skip = true;
None
},
}
})
.collect(),
args,
results,
skip_count,
};
v.visit_expr(&body.value);
v.results
} }
fn get_rptr_lm<'tcx>(ty: &'tcx Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, Span)> { fn get_rptr_lm<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, Span)> {
if let TyKind::Rptr(ref lt, ref m) = ty.kind { if let TyKind::Rptr(ref lt, ref m) = ty.kind {
Some((lt, m.mutbl, ty.span)) Some((lt, m.mutbl, ty.span))
} else { } else {

View file

@ -342,7 +342,7 @@ fn check_range_zip_with_len(cx: &LateContext<'_>, path: &PathSegment<'_>, args:
// `.iter()` and `.len()` called on same `Path` // `.iter()` and `.len()` called on same `Path`
if let ExprKind::Path(QPath::Resolved(_, iter_path)) = iter_args[0].kind; if let ExprKind::Path(QPath::Resolved(_, iter_path)) = iter_args[0].kind;
if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_args[0].kind; if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_args[0].kind;
if SpanlessEq::new(cx).eq_path_segments(&iter_path.segments, &len_path.segments); if SpanlessEq::new(cx).eq_path_segments(iter_path.segments, len_path.segments);
then { then {
span_lint(cx, span_lint(cx,
RANGE_ZIP_WITH_LEN, RANGE_ZIP_WITH_LEN,

View file

@ -1,6 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::{snippet_opt, snippet_with_applicability}; use clippy_utils::source::{snippet_opt, snippet_with_applicability};
use clippy_utils::sugg::Sugg;
use if_chain::if_chain; use if_chain::if_chain;
use rustc_ast::ast::{Expr, ExprKind, Mutability, UnOp}; use rustc_ast::ast::{Expr, ExprKind, Mutability, UnOp};
use rustc_errors::Applicability; use rustc_errors::Applicability;
@ -104,59 +103,3 @@ impl EarlyLintPass for DerefAddrOf {
} }
} }
} }
declare_clippy_lint! {
/// ### What it does
/// Checks for references in expressions that use
/// auto dereference.
///
/// ### Why is this bad?
/// The reference is a no-op and is automatically
/// dereferenced by the compiler and makes the code less clear.
///
/// ### Example
/// ```rust
/// struct Point(u32, u32);
/// let point = Point(30, 20);
/// let x = (&point).0;
/// ```
/// Use instead:
/// ```rust
/// # struct Point(u32, u32);
/// # let point = Point(30, 20);
/// let x = point.0;
/// ```
#[clippy::version = "pre 1.29.0"]
pub REF_IN_DEREF,
complexity,
"Use of reference in auto dereference expression."
}
declare_lint_pass!(RefInDeref => [REF_IN_DEREF]);
impl EarlyLintPass for RefInDeref {
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) {
if_chain! {
if let ExprKind::Field(ref object, _) = e.kind;
if let ExprKind::Paren(ref parened) = object.kind;
if let ExprKind::AddrOf(_, _, ref inner) = parened.kind;
then {
let applicability = if inner.span.from_expansion() {
Applicability::MaybeIncorrect
} else {
Applicability::MachineApplicable
};
let sugg = Sugg::ast(cx, inner, "_").maybe_par();
span_lint_and_sugg(
cx,
REF_IN_DEREF,
object.span,
"creating a reference that is immediately dereferenced",
"try this",
sugg.to_string(),
applicability,
);
}
}
}
}

View file

@ -3,7 +3,7 @@ use clippy_utils::source::{snippet, snippet_with_applicability};
use clippy_utils::{SpanlessEq, SpanlessHash}; use clippy_utils::{SpanlessEq, SpanlessHash};
use core::hash::{Hash, Hasher}; use core::hash::{Hash, Hasher};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::unhash::UnhashMap; use rustc_data_structures::unhash::UnhashMap;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::def::Res; use rustc_hir::def::Res;
@ -91,7 +91,7 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds {
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'tcx>) { fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'tcx>) {
let Generics { where_clause, .. } = &item.generics; let Generics { where_clause, .. } = &item.generics;
let mut self_bounds_set = FxHashSet::default(); let mut self_bounds_map = FxHashMap::default();
for predicate in where_clause.predicates { for predicate in where_clause.predicates {
if_chain! { if_chain! {
@ -108,27 +108,29 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds {
) )
) = cx.tcx.hir().get_if_local(*def_id); ) = cx.tcx.hir().get_if_local(*def_id);
then { then {
if self_bounds_set.is_empty() { if self_bounds_map.is_empty() {
for bound in self_bounds.iter() { for bound in self_bounds.iter() {
let Some((self_res, _)) = get_trait_res_span_from_bound(bound) else { continue }; let Some((self_res, self_segments, _)) = get_trait_info_from_bound(bound) else { continue };
self_bounds_set.insert(self_res); self_bounds_map.insert(self_res, self_segments);
} }
} }
bound_predicate bound_predicate
.bounds .bounds
.iter() .iter()
.filter_map(get_trait_res_span_from_bound) .filter_map(get_trait_info_from_bound)
.for_each(|(trait_item_res, span)| { .for_each(|(trait_item_res, trait_item_segments, span)| {
if self_bounds_set.get(&trait_item_res).is_some() { if let Some(self_segments) = self_bounds_map.get(&trait_item_res) {
span_lint_and_help( if SpanlessEq::new(cx).eq_path_segments(self_segments, trait_item_segments) {
cx, span_lint_and_help(
TRAIT_DUPLICATION_IN_BOUNDS, cx,
span, TRAIT_DUPLICATION_IN_BOUNDS,
"this trait bound is already specified in trait declaration", span,
None, "this trait bound is already specified in trait declaration",
"consider removing this trait bound", None,
); "consider removing this trait bound",
);
}
} }
}); });
} }
@ -137,14 +139,6 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds {
} }
} }
fn get_trait_res_span_from_bound(bound: &GenericBound<'_>) -> Option<(Res, Span)> {
if let GenericBound::Trait(t, _) = bound {
Some((t.trait_ref.path.res, t.span))
} else {
None
}
}
impl TraitBounds { impl TraitBounds {
fn check_type_repetition<'tcx>(self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) { fn check_type_repetition<'tcx>(self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) {
struct SpanlessTy<'cx, 'tcx> { struct SpanlessTy<'cx, 'tcx> {
@ -231,7 +225,7 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
let res = param let res = param
.bounds .bounds
.iter() .iter()
.filter_map(get_trait_res_span_from_bound) .filter_map(get_trait_info_from_bound)
.collect::<Vec<_>>(); .collect::<Vec<_>>();
map.insert(*ident, res); map.insert(*ident, res);
} }
@ -245,10 +239,10 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
if let Some(segment) = segments.first(); if let Some(segment) = segments.first();
if let Some(trait_resolutions_direct) = map.get(&segment.ident); if let Some(trait_resolutions_direct) = map.get(&segment.ident);
then { then {
for (res_where, _) in bound_predicate.bounds.iter().filter_map(get_trait_res_span_from_bound) { for (res_where, _, _) in bound_predicate.bounds.iter().filter_map(get_trait_info_from_bound) {
if let Some((_, span_direct)) = trait_resolutions_direct if let Some((_, _, span_direct)) = trait_resolutions_direct
.iter() .iter()
.find(|(res_direct, _)| *res_direct == res_where) { .find(|(res_direct, _, _)| *res_direct == res_where) {
span_lint_and_help( span_lint_and_help(
cx, cx,
TRAIT_DUPLICATION_IN_BOUNDS, TRAIT_DUPLICATION_IN_BOUNDS,
@ -263,3 +257,11 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
} }
} }
} }
fn get_trait_info_from_bound<'a>(bound: &'a GenericBound<'_>) -> Option<(Res, &'a [PathSegment<'a>], Span)> {
if let GenericBound::Trait(t, _) = bound {
Some((t.trait_ref.path.res, t.trait_ref.path.segments, t.span))
} else {
None
}
}

View file

@ -98,10 +98,11 @@ fn get_args_to_check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Ve
if trait_pred.self_ty() == inp; if trait_pred.self_ty() == inp;
if let Some(return_ty_pred) = get_projection_pred(cx, generics, *trait_pred); if let Some(return_ty_pred) = get_projection_pred(cx, generics, *trait_pred);
then { then {
if ord_preds.iter().any(|ord| Some(ord.self_ty()) == if ord_preds.iter().any(|ord| Some(ord.self_ty()) == return_ty_pred.term.ty()) {
return_ty_pred.term.ty()) {
args_to_check.push((i, "Ord".to_string())); args_to_check.push((i, "Ord".to_string()));
} else if partial_ord_preds.iter().any(|pord| pord.self_ty() == return_ty_pred.term.ty().unwrap()) { } else if partial_ord_preds.iter().any(|pord| {
pord.self_ty() == return_ty_pred.term.ty().unwrap()
}) {
args_to_check.push((i, "PartialOrd".to_string())); args_to_check.push((i, "PartialOrd".to_string()));
} }
} }

View file

@ -291,7 +291,7 @@ fn transform_with_focus_on_idx(alternatives: &mut Vec<P<Pat>>, focus_idx: usize)
fn extend_with_struct_pat( fn extend_with_struct_pat(
qself1: &Option<ast::QSelf>, qself1: &Option<ast::QSelf>,
path1: &ast::Path, path1: &ast::Path,
fps1: &mut Vec<ast::PatField>, fps1: &mut [ast::PatField],
rest1: bool, rest1: bool,
start: usize, start: usize,
alternatives: &mut Vec<P<Pat>>, alternatives: &mut Vec<P<Pat>>,
@ -332,7 +332,7 @@ fn extend_with_struct_pat(
/// while also requiring `ps1[..n] ~ ps2[..n]` (pre) and `ps1[n + 1..] ~ ps2[n + 1..]` (post), /// while also requiring `ps1[..n] ~ ps2[..n]` (pre) and `ps1[n + 1..] ~ ps2[n + 1..]` (post),
/// where `~` denotes semantic equality. /// where `~` denotes semantic equality.
fn extend_with_matching_product( fn extend_with_matching_product(
targets: &mut Vec<P<Pat>>, targets: &mut [P<Pat>],
start: usize, start: usize,
alternatives: &mut Vec<P<Pat>>, alternatives: &mut Vec<P<Pat>>,
predicate: impl Fn(&PatKind, &[P<Pat>], usize) -> bool, predicate: impl Fn(&PatKind, &[P<Pat>], usize) -> bool,

View file

@ -156,7 +156,7 @@ define_Conf! {
/// ///
/// Suppress lints whenever the suggested change would cause breakage for other crates. /// Suppress lints whenever the suggested change would cause breakage for other crates.
(avoid_breaking_exported_api: bool = true), (avoid_breaking_exported_api: bool = true),
/// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE. /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS.
/// ///
/// The minimum rust version that the project supports /// The minimum rust version that the project supports
(msrv: Option<String> = None), (msrv: Option<String> = None),

View file

@ -23,6 +23,7 @@ use rustc_hir::{
UnOp, UnOp,
}; };
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
use rustc_middle::hir::nested_filter;
use rustc_middle::mir::interpret::ConstValue; use rustc_middle::mir::interpret::ConstValue;
use rustc_middle::ty; use rustc_middle::ty;
use rustc_semver::RustcVersion; use rustc_semver::RustcVersion;

View file

@ -19,7 +19,9 @@ use rustc_hir::{
self as hir, def::DefKind, intravisit, intravisit::Visitor, ExprKind, Item, ItemKind, Mutability, QPath, self as hir, def::DefKind, intravisit, intravisit::Visitor, ExprKind, Item, ItemKind, Mutability, QPath,
}; };
use rustc_lint::{CheckLintNameResult, LateContext, LateLintPass, LintContext, LintId}; use rustc_lint::{CheckLintNameResult, LateContext, LateLintPass, LintContext, LintId};
use rustc_middle::hir::nested_filter;
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::symbol::Ident;
use rustc_span::{sym, Loc, Span, Symbol}; use rustc_span::{sym, Loc, Span, Symbol};
use serde::{ser::SerializeStruct, Serialize, Serializer}; use serde::{ser::SerializeStruct, Serialize, Serializer};
use std::collections::BinaryHeap; use std::collections::BinaryHeap;
@ -578,9 +580,11 @@ fn get_lint_group_and_level_or_lint(
lint_name: &str, lint_name: &str,
item: &Item<'_>, item: &Item<'_>,
) -> Option<(String, &'static str)> { ) -> Option<(String, &'static str)> {
let result = cx let result = cx.lint_store.check_lint_name(
.lint_store lint_name,
.check_lint_name(lint_name, Some(sym::clippy), &[]); Some(sym::clippy),
&[Ident::with_dummy_span(sym::clippy)].into_iter().collect(),
);
if let CheckLintNameResult::Tool(Ok(lint_lst)) = result { if let CheckLintNameResult::Tool(Ok(lint_lst)) = result {
if let Some(group) = get_lint_group(cx, lint_lst[0]) { if let Some(group) = get_lint_group(cx, lint_lst[0]) {
if EXCLUDED_LINT_GROUPS.contains(&group.as_str()) { if EXCLUDED_LINT_GROUPS.contains(&group.as_str()) {

View file

@ -646,11 +646,11 @@ pub fn eq_generic_bound(l: &GenericBound, r: &GenericBound) -> bool {
} }
fn eq_term(l: &Term, r: &Term) -> bool { fn eq_term(l: &Term, r: &Term) -> bool {
match (l, r) { match (l, r) {
(Term::Ty(l), Term::Ty(r)) => eq_ty(l,r), (Term::Ty(l), Term::Ty(r)) => eq_ty(l, r),
(Term::Const(l), Term::Const(r)) => eq_anon_const(l,r), (Term::Const(l), Term::Const(r)) => eq_anon_const(l, r),
_ => false, _ => false,
} }
} }
pub fn eq_assoc_constraint(l: &AssocConstraint, r: &AssocConstraint) -> bool { pub fn eq_assoc_constraint(l: &AssocConstraint, r: &AssocConstraint) -> bool {

View file

@ -1827,7 +1827,8 @@ pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool
} }
/// Gets the node where an expression is either used, or it's type is unified with another branch. /// Gets the node where an expression is either used, or it's type is unified with another branch.
pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<Node<'tcx>> { /// Returns both the node and the `HirId` of the closest child node.
pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<(Node<'tcx>, HirId)> {
let mut child_id = expr.hir_id; let mut child_id = expr.hir_id;
let mut iter = tcx.hir().parent_iter(child_id); let mut iter = tcx.hir().parent_iter(child_id);
loop { loop {
@ -1839,9 +1840,9 @@ pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>
ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => child_id = expr.hir_id, ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => child_id = expr.hir_id,
ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = expr.hir_id, ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = expr.hir_id,
ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => break None, ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => break None,
_ => break Some(Node::Expr(expr)), _ => break Some((Node::Expr(expr), child_id)),
}, },
Some((_, node)) => break Some(node), Some((_, node)) => break Some((node, child_id)),
} }
} }
} }
@ -1850,18 +1851,21 @@ pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>
pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
!matches!( !matches!(
get_expr_use_or_unification_node(tcx, expr), get_expr_use_or_unification_node(tcx, expr),
None | Some(Node::Stmt(Stmt { None | Some((
kind: StmtKind::Expr(_) Node::Stmt(Stmt {
| StmtKind::Semi(_) kind: StmtKind::Expr(_)
| StmtKind::Local(Local { | StmtKind::Semi(_)
pat: Pat { | StmtKind::Local(Local {
kind: PatKind::Wild, pat: Pat {
kind: PatKind::Wild,
..
},
.. ..
}, }),
.. ..
}), }),
.. _
})) ))
) )
} }

View file

@ -51,7 +51,14 @@ impl<'a> NumericLiteral<'a> {
} }
pub fn from_lit_kind(src: &'a str, lit_kind: &LitKind) -> Option<NumericLiteral<'a>> { pub fn from_lit_kind(src: &'a str, lit_kind: &LitKind) -> Option<NumericLiteral<'a>> {
if lit_kind.is_numeric() && src.chars().next().map_or(false, |c| c.is_digit(10)) { let unsigned_src = src.strip_prefix('-').map_or(src, |s| s);
if lit_kind.is_numeric()
&& unsigned_src
.trim_start()
.chars()
.next()
.map_or(false, |c| c.is_digit(10))
{
let (unsuffixed, suffix) = split_suffix(src, lit_kind); let (unsuffixed, suffix) = split_suffix(src, lit_kind);
let float = matches!(lit_kind, LitKind::Float(..)); let float = matches!(lit_kind, LitKind::Float(..));
Some(NumericLiteral::new(unsuffixed, suffix, float)) Some(NumericLiteral::new(unsuffixed, suffix, float))

View file

@ -386,7 +386,7 @@ fn binop_to_string(op: AssocOp, lhs: &str, rhs: &str) -> String {
} }
/// Return `true` if `sugg` is enclosed in parenthesis. /// Return `true` if `sugg` is enclosed in parenthesis.
fn has_enclosing_paren(sugg: impl AsRef<str>) -> bool { pub fn has_enclosing_paren(sugg: impl AsRef<str>) -> bool {
let mut chars = sugg.as_ref().chars(); let mut chars = sugg.as_ref().chars();
if chars.next() == Some('(') { if chars.next() == Some('(') {
let mut depth = 1; let mut depth = 1;

View file

@ -5,19 +5,22 @@
use rustc_ast::ast::Mutability; use rustc_ast::ast::Mutability;
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
use rustc_hir::{TyKind, Unsafety}; use rustc_hir::{Expr, TyKind, Unsafety};
use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst};
use rustc_middle::ty::{self, AdtDef, IntTy, Predicate, Ty, TyCtxt, TypeFoldable, UintTy}; use rustc_middle::ty::{
self, AdtDef, Binder, FnSig, IntTy, Predicate, PredicateKind, Ty, TyCtxt, TypeFoldable, UintTy,
};
use rustc_span::symbol::Ident; use rustc_span::symbol::Ident;
use rustc_span::{sym, Span, Symbol, DUMMY_SP}; use rustc_span::{sym, Span, Symbol, DUMMY_SP};
use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::query::normalize::AtExt; use rustc_trait_selection::traits::query::normalize::AtExt;
use std::iter; use std::iter;
use crate::{match_def_path, must_use_attr}; use crate::{expr_path_res, match_def_path, must_use_attr};
// Checks if the given type implements copy. // Checks if the given type implements copy.
pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
@ -410,3 +413,105 @@ pub fn all_predicates_of(tcx: TyCtxt<'_>, id: DefId) -> impl Iterator<Item = &(P
}) })
.flatten() .flatten()
} }
/// A signature for a function like type.
#[derive(Clone, Copy)]
pub enum ExprFnSig<'tcx> {
Sig(Binder<'tcx, FnSig<'tcx>>),
Closure(Binder<'tcx, FnSig<'tcx>>),
Trait(Binder<'tcx, Ty<'tcx>>, Option<Binder<'tcx, Ty<'tcx>>>),
}
impl<'tcx> ExprFnSig<'tcx> {
/// Gets the argument type at the given offset.
pub fn input(self, i: usize) -> Binder<'tcx, Ty<'tcx>> {
match self {
Self::Sig(sig) => sig.input(i),
Self::Closure(sig) => sig.input(0).map_bound(|ty| ty.tuple_element_ty(i).unwrap()),
Self::Trait(inputs, _) => inputs.map_bound(|ty| ty.tuple_element_ty(i).unwrap()),
}
}
/// Gets the result type, if one could be found. Note that the result type of a trait may not be
/// specified.
pub fn output(self) -> Option<Binder<'tcx, Ty<'tcx>>> {
match self {
Self::Sig(sig) | Self::Closure(sig) => Some(sig.output()),
Self::Trait(_, output) => output,
}
}
}
/// If the expression is function like, get the signature for it.
pub fn expr_sig<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<ExprFnSig<'tcx>> {
if let Res::Def(DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn, id) = expr_path_res(cx, expr) {
Some(ExprFnSig::Sig(cx.tcx.fn_sig(id)))
} else {
let ty = cx.typeck_results().expr_ty_adjusted(expr).peel_refs();
match *ty.kind() {
ty::Closure(_, subs) => Some(ExprFnSig::Closure(subs.as_closure().sig())),
ty::FnDef(id, subs) => Some(ExprFnSig::Sig(cx.tcx.fn_sig(id).subst(cx.tcx, subs))),
ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig)),
ty::Dynamic(bounds, _) => {
let lang_items = cx.tcx.lang_items();
match bounds.principal() {
Some(bound)
if Some(bound.def_id()) == lang_items.fn_trait()
|| Some(bound.def_id()) == lang_items.fn_once_trait()
|| Some(bound.def_id()) == lang_items.fn_mut_trait() =>
{
let output = bounds
.projection_bounds()
.find(|p| lang_items.fn_once_output().map_or(false, |id| id == p.item_def_id()))
.map(|p| p.map_bound(|p| p.term.ty().expect("return type was a const")));
Some(ExprFnSig::Trait(bound.map_bound(|b| b.substs.type_at(0)), output))
},
_ => None,
}
},
ty::Param(_) | ty::Projection(..) => {
let mut inputs = None;
let mut output = None;
let lang_items = cx.tcx.lang_items();
for (pred, _) in all_predicates_of(cx.tcx, cx.typeck_results().hir_owner.to_def_id()) {
let mut is_input = false;
if let Some(ty) = pred
.kind()
.map_bound(|pred| match pred {
PredicateKind::Trait(p)
if (lang_items.fn_trait() == Some(p.def_id())
|| lang_items.fn_mut_trait() == Some(p.def_id())
|| lang_items.fn_once_trait() == Some(p.def_id()))
&& p.self_ty() == ty =>
{
is_input = true;
Some(p.trait_ref.substs.type_at(1))
},
PredicateKind::Projection(p)
if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output()
&& p.projection_ty.self_ty() == ty =>
{
is_input = false;
p.term.ty()
},
_ => None,
})
.transpose()
{
if is_input && inputs.is_none() {
inputs = Some(ty);
} else if !is_input && output.is_none() {
output = Some(ty);
} else {
// Multiple different fn trait impls. Is this even allowed?
return None;
}
}
}
inputs.map(|ty| ExprFnSig::Trait(ty, output))
},
_ => None,
}
}
}

View file

@ -331,11 +331,10 @@ pub fn main() {
// - IF Clippy is run on the main crate, not on deps (`!cap_lints_allow`) THEN // - IF Clippy is run on the main crate, not on deps (`!cap_lints_allow`) THEN
// - IF `--no-deps` is not set (`!no_deps`) OR // - IF `--no-deps` is not set (`!no_deps`) OR
// - IF `--no-deps` is set and Clippy is run on the specified primary package // - IF `--no-deps` is set and Clippy is run on the specified primary package
let clippy_tests_set = env::var("__CLIPPY_INTERNAL_TESTS").map_or(false, |val| val == "true");
let cap_lints_allow = arg_value(&orig_args, "--cap-lints", |val| val == "allow").is_some(); let cap_lints_allow = arg_value(&orig_args, "--cap-lints", |val| val == "allow").is_some();
let in_primary_package = env::var("CARGO_PRIMARY_PACKAGE").is_ok(); let in_primary_package = env::var("CARGO_PRIMARY_PACKAGE").is_ok();
let clippy_enabled = clippy_tests_set || (!cap_lints_allow && (!no_deps || in_primary_package)); let clippy_enabled = !cap_lints_allow && (!no_deps || in_primary_package);
if clippy_enabled { if clippy_enabled {
args.extend(clippy_args); args.extend(clippy_args);
} }

View file

@ -328,15 +328,9 @@ fn run_ui_cargo(config: &mut compiletest::Config) {
} }
} }
fn prepare_env() {
set_var("CLIPPY_DISABLE_DOCS_LINKS", "true");
set_var("__CLIPPY_INTERNAL_TESTS", "true");
//set_var("RUST_BACKTRACE", "0");
}
#[test] #[test]
fn compile_test() { fn compile_test() {
prepare_env(); set_var("CLIPPY_DISABLE_DOCS_LINKS", "true");
let mut config = default_config(); let mut config = default_config();
run_ui(&mut config); run_ui(&mut config);
run_ui_test(&mut config); run_ui_test(&mut config);

View file

@ -1,6 +1,7 @@
#![allow(clippy::redundant_clone)] #![allow(clippy::redundant_clone, clippy::unnecessary_operation)]
#![warn(clippy::manual_non_exhaustive)] #![warn(clippy::manual_non_exhaustive, clippy::borrow_as_ptr, clippy::manual_bits)]
use std::mem::{size_of, size_of_val};
use std::ops::Deref; use std::ops::Deref;
mod enums { mod enums {
@ -68,6 +69,24 @@ fn check_index_refutable_slice() {
} }
} }
fn map_clone_suggest_copied() {
// This should still trigger the lint but suggest `cloned()` instead of `copied()`
let _: Option<u64> = Some(&16).map(|b| *b);
}
fn borrow_as_ptr() {
let val = 1;
let _p = &val as *const i32;
let mut val_mut = 1;
let _p_mut = &mut val_mut as *mut i32;
}
fn manual_bits() {
size_of::<i8>() * 8;
size_of_val(&0u32) * 8;
}
fn main() { fn main() {
option_as_ref_deref(); option_as_ref_deref();
match_like_matches(); match_like_matches();
@ -75,4 +94,5 @@ fn main() {
match_same_arms2(); match_same_arms2();
manual_strip_msrv(); manual_strip_msrv();
check_index_refutable_slice(); check_index_refutable_slice();
borrow_as_ptr();
} }

View file

@ -0,0 +1,10 @@
error: you are using an explicit closure for copying elements
--> $DIR/min_rust_version.rs:74:26
|
LL | let _: Option<u64> = Some(&16).map(|b| *b);
| ^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `Some(&16).cloned()`
|
= note: `-D clippy::map-clone` implied by `-D warnings`
error: aborting due to previous error

View file

@ -1,9 +1,5 @@
#![warn(clippy::borrow_interior_mutable_const)] #![warn(clippy::borrow_interior_mutable_const)]
#![allow( #![allow(clippy::declare_interior_mutable_const, clippy::needless_borrow)]
clippy::declare_interior_mutable_const,
clippy::ref_in_deref,
clippy::needless_borrow
)]
#![allow(const_item_mutation)] #![allow(const_item_mutation)]
use std::borrow::Cow; use std::borrow::Cow;

View file

@ -1,5 +1,5 @@
error: a `const` item with interior mutability should not be borrowed error: a `const` item with interior mutability should not be borrowed
--> $DIR/others.rs:58:5 --> $DIR/others.rs:54:5
| |
LL | ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability 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 = 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 error: a `const` item with interior mutability should not be borrowed
--> $DIR/others.rs:59:16 --> $DIR/others.rs:55:16
| |
LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutability 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 = 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 error: a `const` item with interior mutability should not be borrowed
--> $DIR/others.rs:62:22 --> $DIR/others.rs:58:22
| |
LL | let _once_ref = &ONCE_INIT; //~ ERROR interior mutability 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 = 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 error: a `const` item with interior mutability should not be borrowed
--> $DIR/others.rs:63:25 --> $DIR/others.rs:59:25
| |
LL | let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability 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 = 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 error: a `const` item with interior mutability should not be borrowed
--> $DIR/others.rs:64:27 --> $DIR/others.rs:60:27
| |
LL | let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability 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 = 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 error: a `const` item with interior mutability should not be borrowed
--> $DIR/others.rs:65:26 --> $DIR/others.rs:61:26
| |
LL | let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability 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 = 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 error: a `const` item with interior mutability should not be borrowed
--> $DIR/others.rs:76:14 --> $DIR/others.rs:72:14
| |
LL | let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability 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 = 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 error: a `const` item with interior mutability should not be borrowed
--> $DIR/others.rs:77:14 --> $DIR/others.rs:73:14
| |
LL | let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability 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 = 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 error: a `const` item with interior mutability should not be borrowed
--> $DIR/others.rs:78:19 --> $DIR/others.rs:74:19
| |
LL | let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability 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 = 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 error: a `const` item with interior mutability should not be borrowed
--> $DIR/others.rs:79:14 --> $DIR/others.rs:75:14
| |
LL | let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability 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 = 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 error: a `const` item with interior mutability should not be borrowed
--> $DIR/others.rs:80:13 --> $DIR/others.rs:76:13
| |
LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mutability 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 = 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 error: a `const` item with interior mutability should not be borrowed
--> $DIR/others.rs:86:13 --> $DIR/others.rs:82:13
| |
LL | let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability 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 = 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 error: a `const` item with interior mutability should not be borrowed
--> $DIR/others.rs:91:5 --> $DIR/others.rs:87:5
| |
LL | CELL.set(2); //~ ERROR interior mutability 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 = 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 error: a `const` item with interior mutability should not be borrowed
--> $DIR/others.rs:92:16 --> $DIR/others.rs:88:16
| |
LL | assert_eq!(CELL.get(), 6); //~ ERROR interior mutability LL | assert_eq!(CELL.get(), 6); //~ ERROR interior mutability
| ^^^^ | ^^^^

View file

@ -1,3 +1,5 @@
#![allow(clippy::needless_borrow)]
#[deny(clippy::naive_bytecount)] #[deny(clippy::naive_bytecount)]
fn main() { fn main() {
let x = vec![0_u8; 16]; let x = vec![0_u8; 16];

View file

@ -1,23 +1,23 @@
error: you appear to be counting bytes the naive way error: you appear to be counting bytes the naive way
--> $DIR/bytecount.rs:5:13 --> $DIR/bytecount.rs:7:13
| |
LL | let _ = x.iter().filter(|&&a| a == 0).count(); // naive byte count LL | let _ = x.iter().filter(|&&a| a == 0).count(); // naive byte count
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using the bytecount crate: `bytecount::count(x, 0)` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using the bytecount crate: `bytecount::count(x, 0)`
| |
note: the lint level is defined here note: the lint level is defined here
--> $DIR/bytecount.rs:1:8 --> $DIR/bytecount.rs:3:8
| |
LL | #[deny(clippy::naive_bytecount)] LL | #[deny(clippy::naive_bytecount)]
| ^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^
error: you appear to be counting bytes the naive way error: you appear to be counting bytes the naive way
--> $DIR/bytecount.rs:7:13 --> $DIR/bytecount.rs:9:13
| |
LL | let _ = (&x[..]).iter().filter(|&a| *a == 0).count(); // naive byte count LL | let _ = (&x[..]).iter().filter(|&a| *a == 0).count(); // naive byte count
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using the bytecount crate: `bytecount::count((&x[..]), 0)` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using the bytecount crate: `bytecount::count((&x[..]), 0)`
error: you appear to be counting bytes the naive way error: you appear to be counting bytes the naive way
--> $DIR/bytecount.rs:19:13 --> $DIR/bytecount.rs:21:13
| |
LL | let _ = x.iter().filter(|a| b + 1 == **a).count(); // naive byte count LL | let _ = x.iter().filter(|a| b + 1 == **a).count(); // naive byte count
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using the bytecount crate: `bytecount::count(x, b + 1)` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using the bytecount crate: `bytecount::count(x, b + 1)`

View file

@ -7,7 +7,8 @@
clippy::no_effect, clippy::no_effect,
clippy::unnecessary_operation, clippy::unnecessary_operation,
clippy::vec_init_then_push, clippy::vec_init_then_push,
clippy::toplevel_ref_arg clippy::toplevel_ref_arg,
clippy::needless_borrow
)] )]
use std::cell::RefCell; use std::cell::RefCell;

View file

@ -7,7 +7,8 @@
clippy::no_effect, clippy::no_effect,
clippy::unnecessary_operation, clippy::unnecessary_operation,
clippy::vec_init_then_push, clippy::vec_init_then_push,
clippy::toplevel_ref_arg clippy::toplevel_ref_arg,
clippy::needless_borrow
)] )]
use std::cell::RefCell; use std::cell::RefCell;

View file

@ -1,5 +1,5 @@
error: using `clone` on type `i32` which implements the `Copy` trait error: using `clone` on type `i32` which implements the `Copy` trait
--> $DIR/clone_on_copy.rs:24:5 --> $DIR/clone_on_copy.rs:25:5
| |
LL | 42.clone(); LL | 42.clone();
| ^^^^^^^^^^ help: try removing the `clone` call: `42` | ^^^^^^^^^^ help: try removing the `clone` call: `42`
@ -7,43 +7,43 @@ LL | 42.clone();
= note: `-D clippy::clone-on-copy` implied by `-D warnings` = note: `-D clippy::clone-on-copy` implied by `-D warnings`
error: using `clone` on type `i32` which implements the `Copy` trait error: using `clone` on type `i32` which implements the `Copy` trait
--> $DIR/clone_on_copy.rs:28:5 --> $DIR/clone_on_copy.rs:29:5
| |
LL | (&42).clone(); LL | (&42).clone();
| ^^^^^^^^^^^^^ help: try dereferencing it: `*(&42)` | ^^^^^^^^^^^^^ help: try dereferencing it: `*(&42)`
error: using `clone` on type `i32` which implements the `Copy` trait error: using `clone` on type `i32` which implements the `Copy` trait
--> $DIR/clone_on_copy.rs:31:5 --> $DIR/clone_on_copy.rs:32:5
| |
LL | rc.borrow().clone(); LL | rc.borrow().clone();
| ^^^^^^^^^^^^^^^^^^^ help: try dereferencing it: `*rc.borrow()` | ^^^^^^^^^^^^^^^^^^^ help: try dereferencing it: `*rc.borrow()`
error: using `clone` on type `u32` which implements the `Copy` trait error: using `clone` on type `u32` which implements the `Copy` trait
--> $DIR/clone_on_copy.rs:34:5 --> $DIR/clone_on_copy.rs:35:5
| |
LL | x.clone().rotate_left(1); LL | x.clone().rotate_left(1);
| ^^^^^^^^^ help: try removing the `clone` call: `x` | ^^^^^^^^^ help: try removing the `clone` call: `x`
error: using `clone` on type `i32` which implements the `Copy` trait error: using `clone` on type `i32` which implements the `Copy` trait
--> $DIR/clone_on_copy.rs:48:5 --> $DIR/clone_on_copy.rs:49:5
| |
LL | m!(42).clone(); LL | m!(42).clone();
| ^^^^^^^^^^^^^^ help: try removing the `clone` call: `m!(42)` | ^^^^^^^^^^^^^^ help: try removing the `clone` call: `m!(42)`
error: using `clone` on type `[u32; 2]` which implements the `Copy` trait error: using `clone` on type `[u32; 2]` which implements the `Copy` trait
--> $DIR/clone_on_copy.rs:58:5 --> $DIR/clone_on_copy.rs:59:5
| |
LL | x.clone()[0]; LL | x.clone()[0];
| ^^^^^^^^^ help: try dereferencing it: `(*x)` | ^^^^^^^^^ help: try dereferencing it: `(*x)`
error: using `clone` on type `char` which implements the `Copy` trait error: using `clone` on type `char` which implements the `Copy` trait
--> $DIR/clone_on_copy.rs:68:14 --> $DIR/clone_on_copy.rs:69:14
| |
LL | is_ascii('z'.clone()); LL | is_ascii('z'.clone());
| ^^^^^^^^^^^ help: try removing the `clone` call: `'z'` | ^^^^^^^^^^^ help: try removing the `clone` call: `'z'`
error: using `clone` on type `i32` which implements the `Copy` trait error: using `clone` on type `i32` which implements the `Copy` trait
--> $DIR/clone_on_copy.rs:72:14 --> $DIR/clone_on_copy.rs:73:14
| |
LL | vec.push(42.clone()); LL | vec.push(42.clone());
| ^^^^^^^^^^ help: try removing the `clone` call: `42` | ^^^^^^^^^^ help: try removing the `clone` call: `42`

View file

@ -1,5 +1,5 @@
// run-rustfix // run-rustfix
#![allow(dead_code)] #![allow(dead_code, clippy::needless_borrow)]
#![warn(clippy::duration_subsec)] #![warn(clippy::duration_subsec)]
use std::time::Duration; use std::time::Duration;

View file

@ -1,5 +1,5 @@
// run-rustfix // run-rustfix
#![allow(dead_code)] #![allow(dead_code, clippy::needless_borrow)]
#![warn(clippy::duration_subsec)] #![warn(clippy::duration_subsec)]
use std::time::Duration; use std::time::Duration;

View file

@ -151,4 +151,11 @@ enum North {
NoRight, NoRight,
} }
// #8324
enum Phase {
PreLookup,
Lookup,
PostLookup,
}
fn main() {} fn main() {}

View file

@ -5,13 +5,10 @@
clippy::no_effect, clippy::no_effect,
clippy::redundant_closure_call, clippy::redundant_closure_call,
clippy::needless_pass_by_value, clippy::needless_pass_by_value,
clippy::option_map_unit_fn clippy::option_map_unit_fn,
)]
#![warn(
clippy::redundant_closure,
clippy::redundant_closure_for_method_calls,
clippy::needless_borrow clippy::needless_borrow
)] )]
#![warn(clippy::redundant_closure, clippy::redundant_closure_for_method_calls)]
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
@ -34,7 +31,7 @@ fn main() {
Some(1).map(closure_mac!()); // don't lint closure in macro expansion Some(1).map(closure_mac!()); // don't lint closure in macro expansion
let _: Option<Vec<u8>> = true.then(std::vec::Vec::new); // special case vec! let _: Option<Vec<u8>> = true.then(std::vec::Vec::new); // special case vec!
let d = Some(1u8).map(|a| foo(foo2(a))); //is adjusted? let d = Some(1u8).map(|a| foo(foo2(a))); //is adjusted?
all(&[1, 2, 3], &2, below); //is adjusted all(&[1, 2, 3], &&2, below); //is adjusted
unsafe { unsafe {
Some(1u8).map(|a| unsafe_fn(a)); // unsafe fn Some(1u8).map(|a| unsafe_fn(a)); // unsafe fn
} }

View file

@ -5,13 +5,10 @@
clippy::no_effect, clippy::no_effect,
clippy::redundant_closure_call, clippy::redundant_closure_call,
clippy::needless_pass_by_value, clippy::needless_pass_by_value,
clippy::option_map_unit_fn clippy::option_map_unit_fn,
)]
#![warn(
clippy::redundant_closure,
clippy::redundant_closure_for_method_calls,
clippy::needless_borrow clippy::needless_borrow
)] )]
#![warn(clippy::redundant_closure, clippy::redundant_closure_for_method_calls)]
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};

View file

@ -1,5 +1,5 @@
error: redundant closure error: redundant closure
--> $DIR/eta.rs:31:27 --> $DIR/eta.rs:28:27
| |
LL | let a = Some(1u8).map(|a| foo(a)); LL | let a = Some(1u8).map(|a| foo(a));
| ^^^^^^^^^^ help: replace the closure with the function itself: `foo` | ^^^^^^^^^^ help: replace the closure with the function itself: `foo`
@ -7,45 +7,37 @@ LL | let a = Some(1u8).map(|a| foo(a));
= note: `-D clippy::redundant-closure` implied by `-D warnings` = note: `-D clippy::redundant-closure` implied by `-D warnings`
error: redundant closure error: redundant closure
--> $DIR/eta.rs:35:40 --> $DIR/eta.rs:32:40
| |
LL | let _: Option<Vec<u8>> = true.then(|| vec![]); // special case vec! LL | let _: Option<Vec<u8>> = true.then(|| vec![]); // special case vec!
| ^^^^^^^^^ help: replace the closure with `Vec::new`: `std::vec::Vec::new` | ^^^^^^^^^ help: replace the closure with `Vec::new`: `std::vec::Vec::new`
error: redundant closure error: redundant closure
--> $DIR/eta.rs:36:35 --> $DIR/eta.rs:33:35
| |
LL | let d = Some(1u8).map(|a| foo((|b| foo2(b))(a))); //is adjusted? LL | let d = Some(1u8).map(|a| foo((|b| foo2(b))(a))); //is adjusted?
| ^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo2` | ^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo2`
error: this expression borrows a reference (`&u8`) that is immediately dereferenced by the compiler
--> $DIR/eta.rs:37:21
|
LL | all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted
| ^^^ help: change this to: `&2`
|
= note: `-D clippy::needless-borrow` implied by `-D warnings`
error: redundant closure error: redundant closure
--> $DIR/eta.rs:37:26 --> $DIR/eta.rs:34:26
| |
LL | all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted LL | all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted
| ^^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `below` | ^^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `below`
error: redundant closure error: redundant closure
--> $DIR/eta.rs:43:27 --> $DIR/eta.rs:40:27
| |
LL | let e = Some(1u8).map(|a| divergent(a)); LL | let e = Some(1u8).map(|a| divergent(a));
| ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `divergent` | ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `divergent`
error: redundant closure error: redundant closure
--> $DIR/eta.rs:44:27 --> $DIR/eta.rs:41:27
| |
LL | let e = Some(1u8).map(|a| generic(a)); LL | let e = Some(1u8).map(|a| generic(a));
| ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `generic` | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `generic`
error: redundant closure error: redundant closure
--> $DIR/eta.rs:90:51 --> $DIR/eta.rs:87:51
| |
LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo()); LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo());
| ^^^^^^^^^^^ help: replace the closure with the method itself: `TestStruct::foo` | ^^^^^^^^^^^ help: replace the closure with the method itself: `TestStruct::foo`
@ -53,82 +45,82 @@ LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo());
= note: `-D clippy::redundant-closure-for-method-calls` implied by `-D warnings` = note: `-D clippy::redundant-closure-for-method-calls` implied by `-D warnings`
error: redundant closure error: redundant closure
--> $DIR/eta.rs:91:51 --> $DIR/eta.rs:88:51
| |
LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo()); LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo());
| ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `TestTrait::trait_foo` | ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `TestTrait::trait_foo`
error: redundant closure error: redundant closure
--> $DIR/eta.rs:93:42 --> $DIR/eta.rs:90:42
| |
LL | let e = Some(&mut vec![1, 2, 3]).map(|v| v.clear()); LL | let e = Some(&mut vec![1, 2, 3]).map(|v| v.clear());
| ^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::vec::Vec::clear` | ^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::vec::Vec::clear`
error: redundant closure error: redundant closure
--> $DIR/eta.rs:97:29 --> $DIR/eta.rs:94:29
| |
LL | let e = Some("str").map(|s| s.to_string()); LL | let e = Some("str").map(|s| s.to_string());
| ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::string::ToString::to_string` | ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::string::ToString::to_string`
error: redundant closure error: redundant closure
--> $DIR/eta.rs:98:27 --> $DIR/eta.rs:95:27
| |
LL | let e = Some('a').map(|s| s.to_uppercase()); LL | let e = Some('a').map(|s| s.to_uppercase());
| ^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_uppercase` | ^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_uppercase`
error: redundant closure error: redundant closure
--> $DIR/eta.rs:100:65 --> $DIR/eta.rs:97:65
| |
LL | let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect(); LL | let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_ascii_uppercase` | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_ascii_uppercase`
error: redundant closure error: redundant closure
--> $DIR/eta.rs:163:22 --> $DIR/eta.rs:160:22
| |
LL | requires_fn_once(|| x()); LL | requires_fn_once(|| x());
| ^^^^^^ help: replace the closure with the function itself: `x` | ^^^^^^ help: replace the closure with the function itself: `x`
error: redundant closure error: redundant closure
--> $DIR/eta.rs:170:27 --> $DIR/eta.rs:167:27
| |
LL | let a = Some(1u8).map(|a| foo_ptr(a)); LL | let a = Some(1u8).map(|a| foo_ptr(a));
| ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo_ptr` | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo_ptr`
error: redundant closure error: redundant closure
--> $DIR/eta.rs:175:27 --> $DIR/eta.rs:172:27
| |
LL | let a = Some(1u8).map(|a| closure(a)); LL | let a = Some(1u8).map(|a| closure(a));
| ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `closure` | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `closure`
error: redundant closure error: redundant closure
--> $DIR/eta.rs:207:28 --> $DIR/eta.rs:204:28
| |
LL | x.into_iter().for_each(|x| add_to_res(x)); LL | x.into_iter().for_each(|x| add_to_res(x));
| ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res` | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res`
error: redundant closure error: redundant closure
--> $DIR/eta.rs:208:28 --> $DIR/eta.rs:205:28
| |
LL | y.into_iter().for_each(|x| add_to_res(x)); LL | y.into_iter().for_each(|x| add_to_res(x));
| ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res` | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res`
error: redundant closure error: redundant closure
--> $DIR/eta.rs:209:28 --> $DIR/eta.rs:206:28
| |
LL | z.into_iter().for_each(|x| add_to_res(x)); LL | z.into_iter().for_each(|x| add_to_res(x));
| ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `add_to_res` | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `add_to_res`
error: redundant closure error: redundant closure
--> $DIR/eta.rs:216:21 --> $DIR/eta.rs:213:21
| |
LL | Some(1).map(|n| closure(n)); LL | Some(1).map(|n| closure(n));
| ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut closure` | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut closure`
error: redundant closure error: redundant closure
--> $DIR/eta.rs:235:21 --> $DIR/eta.rs:232:21
| |
LL | map_str_to_path(|s| s.as_ref()); LL | map_str_to_path(|s| s.as_ref());
| ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::convert::AsRef::as_ref` | ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::convert::AsRef::as_ref`
error: aborting due to 21 previous errors error: aborting due to 20 previous errors

View file

@ -1,6 +1,6 @@
// run-rustfix // run-rustfix
#![allow(unused_variables, clippy::clone_double_ref)] #![allow(unused_variables, clippy::clone_double_ref, clippy::needless_borrow)]
#![warn(clippy::explicit_deref_methods)] #![warn(clippy::explicit_deref_methods)]
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};

View file

@ -1,6 +1,6 @@
// run-rustfix // run-rustfix
#![allow(unused_variables, clippy::clone_double_ref)] #![allow(unused_variables, clippy::clone_double_ref, clippy::needless_borrow)]
#![warn(clippy::explicit_deref_methods)] #![warn(clippy::explicit_deref_methods)]
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};

View file

@ -23,7 +23,12 @@ impl Unrelated {
clippy::iter_next_loop, clippy::iter_next_loop,
clippy::for_kv_map clippy::for_kv_map
)] )]
#[allow(clippy::linkedlist, clippy::unnecessary_mut_passed, clippy::similar_names)] #[allow(
clippy::linkedlist,
clippy::unnecessary_mut_passed,
clippy::similar_names,
clippy::needless_borrow
)]
#[allow(unused_variables)] #[allow(unused_variables)]
fn main() { fn main() {
let mut vec = vec![1, 2, 3, 4]; let mut vec = vec![1, 2, 3, 4];

View file

@ -23,7 +23,12 @@ impl Unrelated {
clippy::iter_next_loop, clippy::iter_next_loop,
clippy::for_kv_map clippy::for_kv_map
)] )]
#[allow(clippy::linkedlist, clippy::unnecessary_mut_passed, clippy::similar_names)] #[allow(
clippy::linkedlist,
clippy::unnecessary_mut_passed,
clippy::similar_names,
clippy::needless_borrow
)]
#[allow(unused_variables)] #[allow(unused_variables)]
fn main() { fn main() {
let mut vec = vec![1, 2, 3, 4]; let mut vec = vec![1, 2, 3, 4];

View file

@ -1,5 +1,5 @@
error: it is more concise to loop over references to containers instead of using explicit iteration methods error: it is more concise to loop over references to containers instead of using explicit iteration methods
--> $DIR/for_loop_fixable.rs:38:15 --> $DIR/for_loop_fixable.rs:43:15
| |
LL | for _v in vec.iter() {} LL | for _v in vec.iter() {}
| ^^^^^^^^^^ help: to write this more concisely, try: `&vec` | ^^^^^^^^^^ help: to write this more concisely, try: `&vec`
@ -7,13 +7,13 @@ LL | for _v in vec.iter() {}
= note: `-D clippy::explicit-iter-loop` implied by `-D warnings` = note: `-D clippy::explicit-iter-loop` implied by `-D warnings`
error: it is more concise to loop over references to containers instead of using explicit iteration methods error: it is more concise to loop over references to containers instead of using explicit iteration methods
--> $DIR/for_loop_fixable.rs:40:15 --> $DIR/for_loop_fixable.rs:45:15
| |
LL | for _v in vec.iter_mut() {} LL | for _v in vec.iter_mut() {}
| ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&mut vec` | ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&mut vec`
error: it is more concise to loop over containers instead of using explicit iteration methods error: it is more concise to loop over containers instead of using explicit iteration methods
--> $DIR/for_loop_fixable.rs:43:15 --> $DIR/for_loop_fixable.rs:48:15
| |
LL | for _v in out_vec.into_iter() {} LL | for _v in out_vec.into_iter() {}
| ^^^^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `out_vec` | ^^^^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `out_vec`
@ -21,73 +21,73 @@ LL | for _v in out_vec.into_iter() {}
= note: `-D clippy::explicit-into-iter-loop` implied by `-D warnings` = note: `-D clippy::explicit-into-iter-loop` implied by `-D warnings`
error: it is more concise to loop over references to containers instead of using explicit iteration methods error: it is more concise to loop over references to containers instead of using explicit iteration methods
--> $DIR/for_loop_fixable.rs:48:15 --> $DIR/for_loop_fixable.rs:53:15
| |
LL | for _v in [1, 2, 3].iter() {} LL | for _v in [1, 2, 3].iter() {}
| ^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[1, 2, 3]` | ^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[1, 2, 3]`
error: it is more concise to loop over references to containers instead of using explicit iteration methods error: it is more concise to loop over references to containers instead of using explicit iteration methods
--> $DIR/for_loop_fixable.rs:52:15 --> $DIR/for_loop_fixable.rs:57:15
| |
LL | for _v in [0; 32].iter() {} LL | for _v in [0; 32].iter() {}
| ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[0; 32]` | ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[0; 32]`
error: it is more concise to loop over references to containers instead of using explicit iteration methods error: it is more concise to loop over references to containers instead of using explicit iteration methods
--> $DIR/for_loop_fixable.rs:57:15 --> $DIR/for_loop_fixable.rs:62:15
| |
LL | for _v in ll.iter() {} LL | for _v in ll.iter() {}
| ^^^^^^^^^ help: to write this more concisely, try: `&ll` | ^^^^^^^^^ help: to write this more concisely, try: `&ll`
error: it is more concise to loop over references to containers instead of using explicit iteration methods error: it is more concise to loop over references to containers instead of using explicit iteration methods
--> $DIR/for_loop_fixable.rs:60:15 --> $DIR/for_loop_fixable.rs:65:15
| |
LL | for _v in vd.iter() {} LL | for _v in vd.iter() {}
| ^^^^^^^^^ help: to write this more concisely, try: `&vd` | ^^^^^^^^^ help: to write this more concisely, try: `&vd`
error: it is more concise to loop over references to containers instead of using explicit iteration methods error: it is more concise to loop over references to containers instead of using explicit iteration methods
--> $DIR/for_loop_fixable.rs:63:15 --> $DIR/for_loop_fixable.rs:68:15
| |
LL | for _v in bh.iter() {} LL | for _v in bh.iter() {}
| ^^^^^^^^^ help: to write this more concisely, try: `&bh` | ^^^^^^^^^ help: to write this more concisely, try: `&bh`
error: it is more concise to loop over references to containers instead of using explicit iteration methods error: it is more concise to loop over references to containers instead of using explicit iteration methods
--> $DIR/for_loop_fixable.rs:66:15 --> $DIR/for_loop_fixable.rs:71:15
| |
LL | for _v in hm.iter() {} LL | for _v in hm.iter() {}
| ^^^^^^^^^ help: to write this more concisely, try: `&hm` | ^^^^^^^^^ help: to write this more concisely, try: `&hm`
error: it is more concise to loop over references to containers instead of using explicit iteration methods error: it is more concise to loop over references to containers instead of using explicit iteration methods
--> $DIR/for_loop_fixable.rs:69:15 --> $DIR/for_loop_fixable.rs:74:15
| |
LL | for _v in bt.iter() {} LL | for _v in bt.iter() {}
| ^^^^^^^^^ help: to write this more concisely, try: `&bt` | ^^^^^^^^^ help: to write this more concisely, try: `&bt`
error: it is more concise to loop over references to containers instead of using explicit iteration methods error: it is more concise to loop over references to containers instead of using explicit iteration methods
--> $DIR/for_loop_fixable.rs:72:15 --> $DIR/for_loop_fixable.rs:77:15
| |
LL | for _v in hs.iter() {} LL | for _v in hs.iter() {}
| ^^^^^^^^^ help: to write this more concisely, try: `&hs` | ^^^^^^^^^ help: to write this more concisely, try: `&hs`
error: it is more concise to loop over references to containers instead of using explicit iteration methods error: it is more concise to loop over references to containers instead of using explicit iteration methods
--> $DIR/for_loop_fixable.rs:75:15 --> $DIR/for_loop_fixable.rs:80:15
| |
LL | for _v in bs.iter() {} LL | for _v in bs.iter() {}
| ^^^^^^^^^ help: to write this more concisely, try: `&bs` | ^^^^^^^^^ help: to write this more concisely, try: `&bs`
error: it is more concise to loop over containers instead of using explicit iteration methods error: it is more concise to loop over containers instead of using explicit iteration methods
--> $DIR/for_loop_fixable.rs:250:18 --> $DIR/for_loop_fixable.rs:255:18
| |
LL | for i in iterator.into_iter() { LL | for i in iterator.into_iter() {
| ^^^^^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `iterator` | ^^^^^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `iterator`
error: it is more concise to loop over references to containers instead of using explicit iteration methods error: it is more concise to loop over references to containers instead of using explicit iteration methods
--> $DIR/for_loop_fixable.rs:270:18 --> $DIR/for_loop_fixable.rs:275:18
| |
LL | for _ in t.into_iter() {} LL | for _ in t.into_iter() {}
| ^^^^^^^^^^^^^ help: to write this more concisely, try: `&t` | ^^^^^^^^^^^^^ help: to write this more concisely, try: `&t`
error: it is more concise to loop over containers instead of using explicit iteration methods error: it is more concise to loop over containers instead of using explicit iteration methods
--> $DIR/for_loop_fixable.rs:272:18 --> $DIR/for_loop_fixable.rs:277:18
| |
LL | for _ in r.into_iter() {} LL | for _ in r.into_iter() {}
| ^^^^^^^^^^^^^ help: to write this more concisely, try: `r` | ^^^^^^^^^^^^^ help: to write this more concisely, try: `r`

View file

@ -1,6 +1,11 @@
// run-rustfix // run-rustfix
#![allow(clippy::print_literal, clippy::redundant_clone, clippy::to_string_in_format_args)] #![allow(
clippy::print_literal,
clippy::redundant_clone,
clippy::to_string_in_format_args,
clippy::needless_borrow
)]
#![warn(clippy::useless_format)] #![warn(clippy::useless_format)]
struct Foo(pub String); struct Foo(pub String);

View file

@ -1,6 +1,11 @@
// run-rustfix // run-rustfix
#![allow(clippy::print_literal, clippy::redundant_clone, clippy::to_string_in_format_args)] #![allow(
clippy::print_literal,
clippy::redundant_clone,
clippy::to_string_in_format_args,
clippy::needless_borrow
)]
#![warn(clippy::useless_format)] #![warn(clippy::useless_format)]
struct Foo(pub String); struct Foo(pub String);

View file

@ -1,5 +1,5 @@
error: useless use of `format!` error: useless use of `format!`
--> $DIR/format.rs:13:5 --> $DIR/format.rs:18:5
| |
LL | format!("foo"); LL | format!("foo");
| ^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()` | ^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()`
@ -7,19 +7,19 @@ LL | format!("foo");
= note: `-D clippy::useless-format` implied by `-D warnings` = note: `-D clippy::useless-format` implied by `-D warnings`
error: useless use of `format!` error: useless use of `format!`
--> $DIR/format.rs:14:5 --> $DIR/format.rs:19:5
| |
LL | format!("{{}}"); LL | format!("{{}}");
| ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{}".to_string()` | ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{}".to_string()`
error: useless use of `format!` error: useless use of `format!`
--> $DIR/format.rs:15:5 --> $DIR/format.rs:20:5
| |
LL | format!("{{}} abc {{}}"); LL | format!("{{}} abc {{}}");
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{} abc {}".to_string()` | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{} abc {}".to_string()`
error: useless use of `format!` error: useless use of `format!`
--> $DIR/format.rs:16:5 --> $DIR/format.rs:21:5
| |
LL | / format!( LL | / format!(
LL | | r##"foo {{}} LL | | r##"foo {{}}
@ -34,79 +34,79 @@ LL ~ " bar"##.to_string();
| |
error: useless use of `format!` error: useless use of `format!`
--> $DIR/format.rs:21:13 --> $DIR/format.rs:26:13
| |
LL | let _ = format!(""); LL | let _ = format!("");
| ^^^^^^^^^^^ help: consider using `String::new()`: `String::new()` | ^^^^^^^^^^^ help: consider using `String::new()`: `String::new()`
error: useless use of `format!` error: useless use of `format!`
--> $DIR/format.rs:23:5 --> $DIR/format.rs:28:5
| |
LL | format!("{}", "foo"); LL | format!("{}", "foo");
| ^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()` | ^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()`
error: useless use of `format!` error: useless use of `format!`
--> $DIR/format.rs:27:5 --> $DIR/format.rs:32:5
| |
LL | format!("{:+}", "foo"); // Warn when the format makes no difference. LL | format!("{:+}", "foo"); // Warn when the format makes no difference.
| ^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()` | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()`
error: useless use of `format!` error: useless use of `format!`
--> $DIR/format.rs:28:5 --> $DIR/format.rs:33:5
| |
LL | format!("{:<}", "foo"); // Warn when the format makes no difference. LL | format!("{:<}", "foo"); // Warn when the format makes no difference.
| ^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()` | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()`
error: useless use of `format!` error: useless use of `format!`
--> $DIR/format.rs:33:5 --> $DIR/format.rs:38:5
| |
LL | format!("{}", arg); LL | format!("{}", arg);
| ^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string()` | ^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string()`
error: useless use of `format!` error: useless use of `format!`
--> $DIR/format.rs:37:5 --> $DIR/format.rs:42:5
| |
LL | format!("{:+}", arg); // Warn when the format makes no difference. LL | format!("{:+}", arg); // Warn when the format makes no difference.
| ^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string()` | ^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string()`
error: useless use of `format!` error: useless use of `format!`
--> $DIR/format.rs:38:5 --> $DIR/format.rs:43:5
| |
LL | format!("{:<}", arg); // Warn when the format makes no difference. LL | format!("{:<}", arg); // Warn when the format makes no difference.
| ^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string()` | ^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string()`
error: useless use of `format!` error: useless use of `format!`
--> $DIR/format.rs:65:5 --> $DIR/format.rs:70:5
| |
LL | format!("{}", 42.to_string()); LL | format!("{}", 42.to_string());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `42.to_string()` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `42.to_string()`
error: useless use of `format!` error: useless use of `format!`
--> $DIR/format.rs:67:5 --> $DIR/format.rs:72:5
| |
LL | format!("{}", x.display().to_string()); LL | format!("{}", x.display().to_string());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.display().to_string()` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.display().to_string()`
error: useless use of `format!` error: useless use of `format!`
--> $DIR/format.rs:71:18 --> $DIR/format.rs:76:18
| |
LL | let _ = Some(format!("{}", a + "bar")); LL | let _ = Some(format!("{}", a + "bar"));
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `a + "bar"` | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `a + "bar"`
error: useless use of `format!` error: useless use of `format!`
--> $DIR/format.rs:75:22 --> $DIR/format.rs:80:22
| |
LL | let _s: String = format!("{}", &*v.join("/n")); LL | let _s: String = format!("{}", &*v.join("/n"));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `(&*v.join("/n")).to_string()` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `(&*v.join("/n")).to_string()`
error: useless use of `format!` error: useless use of `format!`
--> $DIR/format.rs:81:13 --> $DIR/format.rs:86:13
| |
LL | let _ = format!("{x}"); LL | let _ = format!("{x}");
| ^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()` | ^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()`
error: useless use of `format!` error: useless use of `format!`
--> $DIR/format.rs:83:13 --> $DIR/format.rs:88:13
| |
LL | let _ = format!("{y}", y = x); LL | let _ = format!("{y}", y = x);
| ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()` | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()`

View file

@ -45,6 +45,33 @@ fn main() {
let b = &mut b; let b = &mut b;
x(b); x(b);
} }
// Issue #8191
let mut x = 5;
let mut x = &mut x;
mut_ref(x);
mut_ref(x);
let y: &mut i32 = x;
let y: &mut i32 = x;
let y = match 0 {
// Don't lint. Removing the borrow would move 'x'
0 => &mut x,
_ => &mut *x,
};
*x = 5;
let s = String::new();
let _ = s.len();
let _ = s.capacity();
let _ = s.capacity();
let x = (1, 2);
let _ = x.0;
let x = &x as *const (i32, i32);
let _ = unsafe { (*x).0 };
} }
#[allow(clippy::needless_borrowed_reference)] #[allow(clippy::needless_borrowed_reference)]

View file

@ -45,6 +45,33 @@ fn main() {
let b = &mut b; let b = &mut b;
x(&b); x(&b);
} }
// Issue #8191
let mut x = 5;
let mut x = &mut x;
mut_ref(&mut x);
mut_ref(&mut &mut x);
let y: &mut i32 = &mut x;
let y: &mut i32 = &mut &mut x;
let y = match 0 {
// Don't lint. Removing the borrow would move 'x'
0 => &mut x,
_ => &mut *x,
};
*x = 5;
let s = String::new();
let _ = (&s).len();
let _ = (&s).capacity();
let _ = (&&s).capacity();
let x = (1, 2);
let _ = (&x).0;
let x = &x as *const (i32, i32);
let _ = unsafe { (&*x).0 };
} }
#[allow(clippy::needless_borrowed_reference)] #[allow(clippy::needless_borrowed_reference)]

View file

@ -1,4 +1,4 @@
error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler error: this expression creates a reference which is immediately dereferenced by the compiler
--> $DIR/needless_borrow.rs:9:15 --> $DIR/needless_borrow.rs:9:15
| |
LL | let _ = x(&&a); // warn LL | let _ = x(&&a); // warn
@ -6,59 +6,113 @@ LL | let _ = x(&&a); // warn
| |
= note: `-D clippy::needless-borrow` implied by `-D warnings` = note: `-D clippy::needless-borrow` implied by `-D warnings`
error: this expression borrows a reference (`&mut i32`) that is immediately dereferenced by the compiler error: this expression creates a reference which is immediately dereferenced by the compiler
--> $DIR/needless_borrow.rs:13:13 --> $DIR/needless_borrow.rs:13:13
| |
LL | mut_ref(&mut &mut b); // warn LL | mut_ref(&mut &mut b); // warn
| ^^^^^^^^^^^ help: change this to: `&mut b` | ^^^^^^^^^^^ help: change this to: `&mut b`
error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler error: this expression creates a reference which is immediately dereferenced by the compiler
--> $DIR/needless_borrow.rs:25:13 --> $DIR/needless_borrow.rs:25:13
| |
LL | &&a LL | &&a
| ^^^ help: change this to: `&a` | ^^^ help: change this to: `&a`
error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler error: this expression creates a reference which is immediately dereferenced by the compiler
--> $DIR/needless_borrow.rs:27:15 --> $DIR/needless_borrow.rs:27:15
| |
LL | 46 => &&a, LL | 46 => &&a,
| ^^^ help: change this to: `&a` | ^^^ help: change this to: `&a`
error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler error: this expression creates a reference which is immediately dereferenced by the compiler
--> $DIR/needless_borrow.rs:33:27 --> $DIR/needless_borrow.rs:33:27
| |
LL | break &ref_a; LL | break &ref_a;
| ^^^^^^ help: change this to: `ref_a` | ^^^^^^ help: change this to: `ref_a`
error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler error: this expression creates a reference which is immediately dereferenced by the compiler
--> $DIR/needless_borrow.rs:40:15 --> $DIR/needless_borrow.rs:40:15
| |
LL | let _ = x(&&&a); LL | let _ = x(&&&a);
| ^^^^ help: change this to: `&a` | ^^^^ help: change this to: `&a`
error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler error: this expression creates a reference which is immediately dereferenced by the compiler
--> $DIR/needless_borrow.rs:41:15 --> $DIR/needless_borrow.rs:41:15
| |
LL | let _ = x(&mut &&a); LL | let _ = x(&mut &&a);
| ^^^^^^^^ help: change this to: `&a` | ^^^^^^^^ help: change this to: `&a`
error: this expression borrows a reference (`&mut i32`) that is immediately dereferenced by the compiler error: this expression creates a reference which is immediately dereferenced by the compiler
--> $DIR/needless_borrow.rs:42:15 --> $DIR/needless_borrow.rs:42:15
| |
LL | let _ = x(&&&mut b); LL | let _ = x(&&&mut b);
| ^^^^^^^^ help: change this to: `&mut b` | ^^^^^^^^ help: change this to: `&mut b`
error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler error: this expression creates a reference which is immediately dereferenced by the compiler
--> $DIR/needless_borrow.rs:43:15 --> $DIR/needless_borrow.rs:43:15
| |
LL | let _ = x(&&ref_a); LL | let _ = x(&&ref_a);
| ^^^^^^^ help: change this to: `ref_a` | ^^^^^^^ help: change this to: `ref_a`
error: this expression borrows a reference (`&mut i32`) that is immediately dereferenced by the compiler error: this expression creates a reference which is immediately dereferenced by the compiler
--> $DIR/needless_borrow.rs:46:11 --> $DIR/needless_borrow.rs:46:11
| |
LL | x(&b); LL | x(&b);
| ^^ help: change this to: `b` | ^^ help: change this to: `b`
error: aborting due to 10 previous errors error: this expression creates a reference which is immediately dereferenced by the compiler
--> $DIR/needless_borrow.rs:53:13
|
LL | mut_ref(&mut x);
| ^^^^^^ help: change this to: `x`
error: this expression creates a reference which is immediately dereferenced by the compiler
--> $DIR/needless_borrow.rs:54:13
|
LL | mut_ref(&mut &mut x);
| ^^^^^^^^^^^ help: change this to: `x`
error: this expression creates a reference which is immediately dereferenced by the compiler
--> $DIR/needless_borrow.rs:55:23
|
LL | let y: &mut i32 = &mut x;
| ^^^^^^ help: change this to: `x`
error: this expression creates a reference which is immediately dereferenced by the compiler
--> $DIR/needless_borrow.rs:56:23
|
LL | let y: &mut i32 = &mut &mut x;
| ^^^^^^^^^^^ help: change this to: `x`
error: this expression borrows a value the compiler would automatically borrow
--> $DIR/needless_borrow.rs:67:13
|
LL | let _ = (&s).len();
| ^^^^ help: change this to: `s`
error: this expression borrows a value the compiler would automatically borrow
--> $DIR/needless_borrow.rs:68:13
|
LL | let _ = (&s).capacity();
| ^^^^ help: change this to: `s`
error: this expression creates a reference which is immediately dereferenced by the compiler
--> $DIR/needless_borrow.rs:69:13
|
LL | let _ = (&&s).capacity();
| ^^^^^ help: change this to: `s`
error: this expression borrows a value the compiler would automatically borrow
--> $DIR/needless_borrow.rs:72:13
|
LL | let _ = (&x).0;
| ^^^^ help: change this to: `x`
error: this expression borrows a value the compiler would automatically borrow
--> $DIR/needless_borrow.rs:74:22
|
LL | let _ = unsafe { (&*x).0 };
| ^^^^^ help: change this to: `(*x)`
error: aborting due to 19 previous errors

View file

@ -1,5 +1,11 @@
#![warn(clippy::needless_lifetimes)] #![warn(clippy::needless_lifetimes)]
#![allow(dead_code, clippy::needless_pass_by_value, clippy::unnecessary_wraps, dyn_drop)] #![allow(
dead_code,
clippy::boxed_local,
clippy::needless_pass_by_value,
clippy::unnecessary_wraps,
dyn_drop
)]
fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {}
@ -369,4 +375,47 @@ mod issue6159 {
} }
} }
mod issue7296 {
use std::rc::Rc;
use std::sync::Arc;
struct Foo;
impl Foo {
fn implicit<'a>(&'a self) -> &'a () {
&()
}
fn implicit_mut<'a>(&'a mut self) -> &'a () {
&()
}
fn explicit<'a>(self: &'a Arc<Self>) -> &'a () {
&()
}
fn explicit_mut<'a>(self: &'a mut Rc<Self>) -> &'a () {
&()
}
fn lifetime_elsewhere<'a>(self: Box<Self>, here: &'a ()) -> &'a () {
&()
}
}
trait Bar {
fn implicit<'a>(&'a self) -> &'a ();
fn implicit_provided<'a>(&'a self) -> &'a () {
&()
}
fn explicit<'a>(self: &'a Arc<Self>) -> &'a ();
fn explicit_provided<'a>(self: &'a Arc<Self>) -> &'a () {
&()
}
fn lifetime_elsewhere<'a>(self: Box<Self>, here: &'a ()) -> &'a ();
fn lifetime_elsewhere_provided<'a>(self: Box<Self>, here: &'a ()) -> &'a () {
&()
}
}
}
fn main() {} fn main() {}

View file

@ -1,5 +1,5 @@
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:4:1 --> $DIR/needless_lifetimes.rs:10:1
| |
LL | fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} LL | fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -7,148 +7,190 @@ LL | fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {}
= note: `-D clippy::needless-lifetimes` implied by `-D warnings` = note: `-D clippy::needless-lifetimes` implied by `-D warnings`
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:6:1 --> $DIR/needless_lifetimes.rs:12:1
| |
LL | fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {} LL | fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:16:1 --> $DIR/needless_lifetimes.rs:22:1
| |
LL | fn in_and_out<'a>(x: &'a u8, _y: u8) -> &'a u8 { LL | fn in_and_out<'a>(x: &'a u8, _y: u8) -> &'a u8 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:50:1 --> $DIR/needless_lifetimes.rs:56:1
| |
LL | fn deep_reference_3<'a>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> { LL | fn deep_reference_3<'a>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:55:1 --> $DIR/needless_lifetimes.rs:61:1
| |
LL | fn where_clause_without_lt<'a, T>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> LL | fn where_clause_without_lt<'a, T>(x: &'a u8, _y: u8) -> Result<&'a u8, ()>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:67:1 --> $DIR/needless_lifetimes.rs:73:1
| |
LL | fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {} LL | fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:91:1 --> $DIR/needless_lifetimes.rs:97:1
| |
LL | fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> LL | fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:121:5 --> $DIR/needless_lifetimes.rs:127:5
| |
LL | fn self_and_out<'s>(&'s self) -> &'s u8 { LL | fn self_and_out<'s>(&'s self) -> &'s u8 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:130:5 --> $DIR/needless_lifetimes.rs:136:5
| |
LL | fn distinct_self_and_in<'s, 't>(&'s self, _x: &'t u8) {} LL | fn distinct_self_and_in<'s, 't>(&'s self, _x: &'t u8) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:149:1 --> $DIR/needless_lifetimes.rs:155:1
| |
LL | fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str { LL | fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:179:1 --> $DIR/needless_lifetimes.rs:185:1
| |
LL | fn trait_obj_elided2<'a>(_arg: &'a dyn Drop) -> &'a str { LL | fn trait_obj_elided2<'a>(_arg: &'a dyn Drop) -> &'a str {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:185:1 --> $DIR/needless_lifetimes.rs:191:1
| |
LL | fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str { LL | fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:204:1 --> $DIR/needless_lifetimes.rs:210:1
| |
LL | fn named_input_elided_output<'a>(_arg: &'a str) -> &str { LL | fn named_input_elided_output<'a>(_arg: &'a str) -> &str {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:212:1 --> $DIR/needless_lifetimes.rs:218:1
| |
LL | fn trait_bound_ok<'a, T: WithLifetime<'static>>(_: &'a u8, _: T) { LL | fn trait_bound_ok<'a, T: WithLifetime<'static>>(_: &'a u8, _: T) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:248:1 --> $DIR/needless_lifetimes.rs:254:1
| |
LL | fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> { LL | fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:255:9 --> $DIR/needless_lifetimes.rs:261:9
| |
LL | fn needless_lt<'a>(x: &'a u8) {} LL | fn needless_lt<'a>(x: &'a u8) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:259:9 --> $DIR/needless_lifetimes.rs:265:9
| |
LL | fn needless_lt<'a>(_x: &'a u8) {} LL | fn needless_lt<'a>(_x: &'a u8) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:272:9 --> $DIR/needless_lifetimes.rs:278:9
| |
LL | fn baz<'a>(&'a self) -> impl Foo + 'a { LL | fn baz<'a>(&'a self) -> impl Foo + 'a {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:301:5 --> $DIR/needless_lifetimes.rs:307:5
| |
LL | fn impl_trait_elidable_nested_named_lifetimes<'a>(i: &'a i32, f: impl for<'b> Fn(&'b i32) -> &'b i32) -> &'a i32 { LL | fn impl_trait_elidable_nested_named_lifetimes<'a>(i: &'a i32, f: impl for<'b> Fn(&'b i32) -> &'b i32) -> &'a i32 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:304:5 --> $DIR/needless_lifetimes.rs:310:5
| |
LL | fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 { LL | fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:313:5 --> $DIR/needless_lifetimes.rs:319:5
| |
LL | fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 { LL | fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:325:5 --> $DIR/needless_lifetimes.rs:331:5
| |
LL | fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32 LL | fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:340:5 --> $DIR/needless_lifetimes.rs:346:5
| |
LL | fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 { LL | fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:353:5 --> $DIR/needless_lifetimes.rs:359:5
| |
LL | fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 { LL | fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:356:5 --> $DIR/needless_lifetimes.rs:362:5
| |
LL | fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) { LL | fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 25 previous errors error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:384:9
|
LL | fn implicit<'a>(&'a self) -> &'a () {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:387:9
|
LL | fn implicit_mut<'a>(&'a mut self) -> &'a () {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:398:9
|
LL | fn lifetime_elsewhere<'a>(self: Box<Self>, here: &'a ()) -> &'a () {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:404:9
|
LL | fn implicit<'a>(&'a self) -> &'a ();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:405:9
|
LL | fn implicit_provided<'a>(&'a self) -> &'a () {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:414:9
|
LL | fn lifetime_elsewhere<'a>(self: Box<Self>, here: &'a ()) -> &'a ();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:415:9
|
LL | fn lifetime_elsewhere_provided<'a>(self: Box<Self>, here: &'a ()) -> &'a () {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 32 previous errors

View file

@ -125,3 +125,16 @@ pub fn test2() {
let x = Some(3); let x = Some(3);
let _x = some_and_qmark_in_macro!(x?); let _x = some_and_qmark_in_macro!(x?);
} }
async fn async_option_bad(to: TO) -> Option<usize> {
let _ = Some(3);
to.magic
}
async fn async_deref_ref(s: Option<&String>) -> Option<&str> {
Some(s?)
}
async fn async_result_bad(s: TR) -> Result<usize, bool> {
s.magic
}

View file

@ -125,3 +125,16 @@ pub fn test2() {
let x = Some(3); let x = Some(3);
let _x = some_and_qmark_in_macro!(x?); let _x = some_and_qmark_in_macro!(x?);
} }
async fn async_option_bad(to: TO) -> Option<usize> {
let _ = Some(3);
Some(to.magic?)
}
async fn async_deref_ref(s: Option<&String>) -> Option<&str> {
Some(s?)
}
async fn async_result_bad(s: TR) -> Result<usize, bool> {
Ok(s.magic?)
}

View file

@ -77,5 +77,17 @@ LL | let _x = some_and_qmark_in_macro!(x?);
| |
= note: this error originates in the macro `some_and_qmark_in_macro` (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in the macro `some_and_qmark_in_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 12 previous errors error: question mark operator is useless here
--> $DIR/needless_question_mark.rs:131:5
|
LL | Some(to.magic?)
| ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic`
error: question mark operator is useless here
--> $DIR/needless_question_mark.rs:139:5
|
LL | Ok(s.magic?)
| ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `s.magic`
error: aborting due to 14 previous errors

View file

@ -1,7 +1,7 @@
#![allow(unused_variables, clippy::blacklisted_name)] #![allow(unused_variables, clippy::blacklisted_name)]
#![warn(clippy::op_ref)] #![warn(clippy::op_ref)]
use std::collections::HashSet; use std::collections::HashSet;
use std::ops::BitAnd; use std::ops::{BitAnd, Mul};
fn main() { fn main() {
let tracked_fds: HashSet<i32> = HashSet::new(); let tracked_fds: HashSet<i32> = HashSet::new();
@ -55,3 +55,40 @@ fn main() {
let y = Y(2); let y = Y(2);
let z = x & &y; let z = x & &y;
} }
#[derive(Clone, Copy)]
struct A(i32);
#[derive(Clone, Copy)]
struct B(i32);
impl Mul<&A> for B {
type Output = i32;
fn mul(self, rhs: &A) -> Self::Output {
self.0 * rhs.0
}
}
impl Mul<A> for B {
type Output = i32;
fn mul(self, rhs: A) -> Self::Output {
// Should not lint because removing the reference would lead to unconditional recursion
self * &rhs
}
}
impl Mul<&A> for A {
type Output = i32;
fn mul(self, rhs: &A) -> Self::Output {
self.0 * rhs.0
}
}
impl Mul<A> for A {
type Output = i32;
fn mul(self, rhs: A) -> Self::Output {
let one = B(1);
let two = 2;
let three = 3;
let _ = one * &self;
let _ = two + &three;
// Removing the reference would lead to unconditional recursion
self * &rhs
}
}

View file

@ -18,5 +18,21 @@ LL | let z = x & &y;
| | | |
| help: use the right value directly: `y` | help: use the right value directly: `y`
error: aborting due to 2 previous errors error: taken reference of right operand
--> $DIR/op_ref.rs:89:17
|
LL | let _ = one * &self;
| ^^^^^^-----
| |
| help: use the right value directly: `self`
error: taken reference of right operand
--> $DIR/op_ref.rs:90:17
|
LL | let _ = two + &three;
| ^^^^^^------
| |
| help: use the right value directly: `three`
error: aborting due to 4 previous errors

View file

@ -9,7 +9,6 @@ fn do_vec(x: &Vec<i64>) {
} }
fn do_vec_mut(x: &mut Vec<i64>) { fn do_vec_mut(x: &mut Vec<i64>) {
// no error here
//Nothing here //Nothing here
} }
@ -18,7 +17,6 @@ fn do_str(x: &String) {
} }
fn do_str_mut(x: &mut String) { fn do_str_mut(x: &mut String) {
// no error here
//Nothing here either //Nothing here either
} }
@ -27,7 +25,6 @@ fn do_path(x: &PathBuf) {
} }
fn do_path_mut(x: &mut PathBuf) { fn do_path_mut(x: &mut PathBuf) {
// no error here
//Nothing here either //Nothing here either
} }
@ -52,7 +49,7 @@ fn cloned(x: &Vec<u8>) -> Vec<u8> {
let e = x.clone(); let e = x.clone();
let f = e.clone(); // OK let f = e.clone(); // OK
let g = x; let g = x;
let h = g.clone(); // Alas, we cannot reliably detect this without following data. let h = g.clone();
let i = (e).clone(); let i = (e).clone();
x.clone() x.clone()
} }
@ -156,6 +153,30 @@ mod issue6509 {
} }
} }
fn mut_vec_slice_methods(v: &mut Vec<u32>) {
v.copy_within(1..5, 10);
}
fn mut_vec_vec_methods(v: &mut Vec<u32>) {
v.clear();
}
fn vec_contains(v: &Vec<u32>) -> bool {
[vec![], vec![0]].as_slice().contains(v)
}
fn fn_requires_vec(v: &Vec<u32>) -> bool {
vec_contains(v)
}
fn impl_fn_requires_vec(v: &Vec<u32>, f: impl Fn(&Vec<u32>)) {
f(v);
}
fn dyn_fn_requires_vec(v: &Vec<u32>, f: &dyn Fn(&Vec<u32>)) {
f(v);
}
// No error for types behind an alias (#7699) // No error for types behind an alias (#7699)
type A = Vec<u8>; type A = Vec<u8>;
fn aliased(a: &A) {} fn aliased(a: &A) {}

View file

@ -1,4 +1,4 @@
error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices error: writing `&Vec` instead of `&[_]` involves a new object where a slice will do
--> $DIR/ptr_arg.rs:7:14 --> $DIR/ptr_arg.rs:7:14
| |
LL | fn do_vec(x: &Vec<i64>) { LL | fn do_vec(x: &Vec<i64>) {
@ -6,170 +6,154 @@ LL | fn do_vec(x: &Vec<i64>) {
| |
= note: `-D clippy::ptr-arg` implied by `-D warnings` = note: `-D clippy::ptr-arg` implied by `-D warnings`
error: writing `&mut Vec` instead of `&mut [_]` involves a new object where a slice will do
--> $DIR/ptr_arg.rs:11:18
|
LL | fn do_vec_mut(x: &mut Vec<i64>) {
| ^^^^^^^^^^^^^ help: change this to: `&mut [i64]`
error: writing `&String` instead of `&str` involves a new object where a slice will do error: writing `&String` instead of `&str` involves a new object where a slice will do
--> $DIR/ptr_arg.rs:16:14 --> $DIR/ptr_arg.rs:15:14
| |
LL | fn do_str(x: &String) { LL | fn do_str(x: &String) {
| ^^^^^^^ help: change this to: `&str` | ^^^^^^^ help: change this to: `&str`
error: writing `&mut String` instead of `&mut str` involves a new object where a slice will do
--> $DIR/ptr_arg.rs:19:18
|
LL | fn do_str_mut(x: &mut String) {
| ^^^^^^^^^^^ help: change this to: `&mut str`
error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do
--> $DIR/ptr_arg.rs:25:15 --> $DIR/ptr_arg.rs:23:15
| |
LL | fn do_path(x: &PathBuf) { LL | fn do_path(x: &PathBuf) {
| ^^^^^^^^ help: change this to: `&Path` | ^^^^^^^^ help: change this to: `&Path`
error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices error: writing `&mut PathBuf` instead of `&mut Path` involves a new object where a slice will do
--> $DIR/ptr_arg.rs:38:18 --> $DIR/ptr_arg.rs:27:19
|
LL | fn do_path_mut(x: &mut PathBuf) {
| ^^^^^^^^^^^^ help: change this to: `&mut Path`
error: writing `&Vec` instead of `&[_]` involves a new object where a slice will do
--> $DIR/ptr_arg.rs:35:18
| |
LL | fn do_vec(x: &Vec<i64>); LL | fn do_vec(x: &Vec<i64>);
| ^^^^^^^^^ help: change this to: `&[i64]` | ^^^^^^^^^ help: change this to: `&[i64]`
error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices error: writing `&Vec` instead of `&[_]` involves a new object where a slice will do
--> $DIR/ptr_arg.rs:51:14 --> $DIR/ptr_arg.rs:48:14
| |
LL | fn cloned(x: &Vec<u8>) -> Vec<u8> { LL | fn cloned(x: &Vec<u8>) -> Vec<u8> {
| ^^^^^^^^ | ^^^^^^^^
| |
help: change this to help: change this to
| |
LL | fn cloned(x: &[u8]) -> Vec<u8> { LL ~ fn cloned(x: &[u8]) -> Vec<u8> {
| ~~~~~ LL ~ let e = x.to_owned();
help: change `x.clone()` to LL | let f = e.clone(); // OK
| LL | let g = x;
LL | let e = x.to_owned(); LL ~ let h = g.to_owned();
| ~~~~~~~~~~~~ LL | let i = (e).clone();
help: change `x.clone()` to ...
|
LL | x.to_owned()
|
error: writing `&String` instead of `&str` involves a new object where a slice will do error: writing `&String` instead of `&str` involves a new object where a slice will do
--> $DIR/ptr_arg.rs:60:18 --> $DIR/ptr_arg.rs:57:18
| |
LL | fn str_cloned(x: &String) -> String { LL | fn str_cloned(x: &String) -> String {
| ^^^^^^^ | ^^^^^^^
| |
help: change this to help: change this to
| |
LL | fn str_cloned(x: &str) -> String { LL ~ fn str_cloned(x: &str) -> String {
| ~~~~ LL ~ let a = x.to_owned();
help: change `x.clone()` to LL ~ let b = x.to_owned();
| LL | let c = b.clone();
LL | let a = x.to_string(); LL | let d = a.clone().clone().clone();
| ~~~~~~~~~~~~~ LL ~ x.to_owned()
help: change `x.clone()` to
|
LL | let b = x.to_string();
| ~~~~~~~~~~~~~
help: change `x.clone()` to
|
LL | x.to_string()
| |
error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do
--> $DIR/ptr_arg.rs:68:19 --> $DIR/ptr_arg.rs:65:19
| |
LL | fn path_cloned(x: &PathBuf) -> PathBuf { LL | fn path_cloned(x: &PathBuf) -> PathBuf {
| ^^^^^^^^ | ^^^^^^^^
| |
help: change this to help: change this to
| |
LL | fn path_cloned(x: &Path) -> PathBuf { LL ~ fn path_cloned(x: &Path) -> PathBuf {
| ~~~~~ LL ~ let a = x.to_path_buf();
help: change `x.clone()` to LL ~ let b = x.to_path_buf();
| LL | let c = b.clone();
LL | let a = x.to_path_buf(); LL | let d = a.clone().clone().clone();
| ~~~~~~~~~~~~~~~ LL ~ x.to_path_buf()
help: change `x.clone()` to
|
LL | let b = x.to_path_buf();
| ~~~~~~~~~~~~~~~
help: change `x.clone()` to
|
LL | x.to_path_buf()
| |
error: writing `&String` instead of `&str` involves a new object where a slice will do error: writing `&String` instead of `&str` involves a new object where a slice will do
--> $DIR/ptr_arg.rs:76:44 --> $DIR/ptr_arg.rs:73:44
| |
LL | fn false_positive_capacity(x: &Vec<u8>, y: &String) { LL | fn false_positive_capacity(x: &Vec<u8>, y: &String) {
| ^^^^^^^ | ^^^^^^^
| |
help: change this to help: change this to
| |
LL | fn false_positive_capacity(x: &Vec<u8>, y: &str) { LL ~ fn false_positive_capacity(x: &Vec<u8>, y: &str) {
| ~~~~ LL | let a = x.capacity();
help: change `y.clone()` to LL ~ let b = y.to_owned();
LL ~ let c = y;
| |
LL | let b = y.to_string();
| ~~~~~~~~~~~~~
help: change `y.as_str()` to
|
LL | let c = y;
| ~
error: using a reference to `Cow` is not recommended error: using a reference to `Cow` is not recommended
--> $DIR/ptr_arg.rs:90:25 --> $DIR/ptr_arg.rs:87:25
| |
LL | fn test_cow_with_ref(c: &Cow<[i32]>) {} LL | fn test_cow_with_ref(c: &Cow<[i32]>) {}
| ^^^^^^^^^^^ help: change this to: `&[i32]` | ^^^^^^^^^^^ help: change this to: `&[i32]`
error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices error: writing `&Vec` instead of `&[_]` involves a new object where a slice will do
--> $DIR/ptr_arg.rs:143:21 --> $DIR/ptr_arg.rs:140:21
| |
LL | fn foo_vec(vec: &Vec<u8>) { LL | fn foo_vec(vec: &Vec<u8>) {
| ^^^^^^^^ | ^^^^^^^^
| |
help: change this to help: change this to
| |
LL | fn foo_vec(vec: &[u8]) { LL ~ fn foo_vec(vec: &[u8]) {
| ~~~~~ LL ~ let _ = vec.to_owned().pop();
help: change `vec.clone()` to LL ~ let _ = vec.to_owned().clone();
| |
LL | let _ = vec.to_owned().pop();
| ~~~~~~~~~~~~~~
help: change `vec.clone()` to
|
LL | let _ = vec.to_owned().clone();
| ~~~~~~~~~~~~~~
error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do
--> $DIR/ptr_arg.rs:148:23 --> $DIR/ptr_arg.rs:145:23
| |
LL | fn foo_path(path: &PathBuf) { LL | fn foo_path(path: &PathBuf) {
| ^^^^^^^^ | ^^^^^^^^
| |
help: change this to help: change this to
| |
LL | fn foo_path(path: &Path) { LL ~ fn foo_path(path: &Path) {
| ~~~~~ LL ~ let _ = path.to_path_buf().pop();
help: change `path.clone()` to LL ~ let _ = path.to_path_buf().clone();
| |
LL | let _ = path.to_path_buf().pop();
| ~~~~~~~~~~~~~~~~~~
help: change `path.clone()` to
|
LL | let _ = path.to_path_buf().clone();
| ~~~~~~~~~~~~~~~~~~
error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do
--> $DIR/ptr_arg.rs:153:21 --> $DIR/ptr_arg.rs:150:21
| |
LL | fn foo_str(str: &PathBuf) { LL | fn foo_str(str: &PathBuf) {
| ^^^^^^^^ | ^^^^^^^^
| |
help: change this to help: change this to
| |
LL | fn foo_str(str: &Path) { LL ~ fn foo_str(str: &Path) {
| ~~~~~ LL ~ let _ = str.to_path_buf().pop();
help: change `str.clone()` to LL ~ let _ = str.to_path_buf().clone();
| |
LL | let _ = str.to_path_buf().pop();
| ~~~~~~~~~~~~~~~~~ error: writing `&mut Vec` instead of `&mut [_]` involves a new object where a slice will do
help: change `str.clone()` to --> $DIR/ptr_arg.rs:156:29
| |
LL | let _ = str.to_path_buf().clone(); LL | fn mut_vec_slice_methods(v: &mut Vec<u32>) {
| ~~~~~~~~~~~~~~~~~ | ^^^^^^^^^^^^^ help: change this to: `&mut [u32]`
error: aborting due to 12 previous errors error: aborting due to 16 previous errors

View file

@ -81,7 +81,7 @@ const fn issue6067() {
None::<()>.is_none(); None::<()>.is_none();
} }
#[allow(clippy::deref_addrof, dead_code)] #[allow(clippy::deref_addrof, dead_code, clippy::needless_borrow)]
fn issue7921() { fn issue7921() {
if (&None::<()>).is_none() {} if (&None::<()>).is_none() {}
if (&None::<()>).is_none() {} if (&None::<()>).is_none() {}

View file

@ -96,7 +96,7 @@ const fn issue6067() {
}; };
} }
#[allow(clippy::deref_addrof, dead_code)] #[allow(clippy::deref_addrof, dead_code, clippy::needless_borrow)]
fn issue7921() { fn issue7921() {
if let None = *(&None::<()>) {} if let None = *(&None::<()>) {}
if let None = *&None::<()> {} if let None = *&None::<()> {}

View file

@ -54,6 +54,7 @@
#![warn(clippy::match_result_ok)] #![warn(clippy::match_result_ok)]
#![warn(clippy::disallowed_types)] #![warn(clippy::disallowed_types)]
#![warn(clippy::disallowed_methods)] #![warn(clippy::disallowed_methods)]
#![warn(clippy::needless_borrow)]
// uplifted lints // uplifted lints
#![warn(invalid_value)] #![warn(invalid_value)]
#![warn(array_into_iter)] #![warn(array_into_iter)]

View file

@ -54,6 +54,7 @@
#![warn(clippy::if_let_some_result)] #![warn(clippy::if_let_some_result)]
#![warn(clippy::disallowed_type)] #![warn(clippy::disallowed_type)]
#![warn(clippy::disallowed_method)] #![warn(clippy::disallowed_method)]
#![warn(clippy::ref_in_deref)]
// uplifted lints // uplifted lints
#![warn(clippy::invalid_ref)] #![warn(clippy::invalid_ref)]
#![warn(clippy::into_iter_on_array)] #![warn(clippy::into_iter_on_array)]

View file

@ -138,59 +138,65 @@ error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_
LL | #![warn(clippy::disallowed_method)] LL | #![warn(clippy::disallowed_method)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods`
error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow`
--> $DIR/rename.rs:57:9
|
LL | #![warn(clippy::ref_in_deref)]
| ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow`
error: lint `clippy::invalid_ref` has been renamed to `invalid_value` error: lint `clippy::invalid_ref` has been renamed to `invalid_value`
--> $DIR/rename.rs:58:9 --> $DIR/rename.rs:59:9
| |
LL | #![warn(clippy::invalid_ref)] LL | #![warn(clippy::invalid_ref)]
| ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value`
error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter`
--> $DIR/rename.rs:59:9 --> $DIR/rename.rs:60:9
| |
LL | #![warn(clippy::into_iter_on_array)] LL | #![warn(clippy::into_iter_on_array)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter`
error: lint `clippy::unused_label` has been renamed to `unused_labels` error: lint `clippy::unused_label` has been renamed to `unused_labels`
--> $DIR/rename.rs:60:9 --> $DIR/rename.rs:61:9
| |
LL | #![warn(clippy::unused_label)] LL | #![warn(clippy::unused_label)]
| ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels`
error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` error: lint `clippy::drop_bounds` has been renamed to `drop_bounds`
--> $DIR/rename.rs:61:9 --> $DIR/rename.rs:62:9
| |
LL | #![warn(clippy::drop_bounds)] LL | #![warn(clippy::drop_bounds)]
| ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds`
error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr` error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr`
--> $DIR/rename.rs:62:9 --> $DIR/rename.rs:63:9
| |
LL | #![warn(clippy::temporary_cstring_as_ptr)] LL | #![warn(clippy::temporary_cstring_as_ptr)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr`
error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` error: lint `clippy::panic_params` has been renamed to `non_fmt_panics`
--> $DIR/rename.rs:63:9 --> $DIR/rename.rs:64:9
| |
LL | #![warn(clippy::panic_params)] LL | #![warn(clippy::panic_params)]
| ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics`
error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints`
--> $DIR/rename.rs:64:9 --> $DIR/rename.rs:65:9
| |
LL | #![warn(clippy::unknown_clippy_lints)] LL | #![warn(clippy::unknown_clippy_lints)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints`
error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering`
--> $DIR/rename.rs:65:9 --> $DIR/rename.rs:66:9
| |
LL | #![warn(clippy::invalid_atomic_ordering)] LL | #![warn(clippy::invalid_atomic_ordering)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering`
error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums`
--> $DIR/rename.rs:66:9 --> $DIR/rename.rs:67:9
| |
LL | #![warn(clippy::mem_discriminant_non_enum)] LL | #![warn(clippy::mem_discriminant_non_enum)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums`
error: aborting due to 32 previous errors error: aborting due to 33 previous errors

View file

@ -189,7 +189,7 @@ mod issue7392 {
let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_none(); let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_none();
} }
fn test_string_1(s: &String) -> bool { fn test_string_1(s: &str) -> bool {
s.is_empty() s.is_empty()
} }

View file

@ -251,14 +251,6 @@ error: called `is_none()` after searching an `Iterator` with `find`
LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_none(); LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_none();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y)` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y)`
error: writing `&String` instead of `&str` involves a new object where a slice will do
--> $DIR/search_is_some_fixable_none.rs:192:25
|
LL | fn test_string_1(s: &String) -> bool {
| ^^^^^^^ help: change this to: `&str`
|
= note: `-D clippy::ptr-arg` implied by `-D warnings`
error: called `is_none()` after searching an `Iterator` with `find` error: called `is_none()` after searching an `Iterator` with `find`
--> $DIR/search_is_some_fixable_none.rs:208:17 --> $DIR/search_is_some_fixable_none.rs:208:17
| |
@ -289,5 +281,5 @@ error: called `is_none()` after searching an `Iterator` with `find`
LL | let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_none(); LL | let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_none();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|fp| test_u32_2(*fp.field))` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|fp| test_u32_2(*fp.field))`
error: aborting due to 44 previous errors error: aborting due to 43 previous errors

View file

@ -188,7 +188,7 @@ mod issue7392 {
let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_some(); let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_some();
} }
fn test_string_1(s: &String) -> bool { fn test_string_1(s: &str) -> bool {
s.is_empty() s.is_empty()
} }

View file

@ -234,14 +234,6 @@ error: called `is_some()` after searching an `Iterator` with `find`
LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_some(); LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_some();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|(&x, y)| x == *y)` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|(&x, y)| x == *y)`
error: writing `&String` instead of `&str` involves a new object where a slice will do
--> $DIR/search_is_some_fixable_some.rs:191:25
|
LL | fn test_string_1(s: &String) -> bool {
| ^^^^^^^ help: change this to: `&str`
|
= note: `-D clippy::ptr-arg` implied by `-D warnings`
error: called `is_some()` after searching an `Iterator` with `find` error: called `is_some()` after searching an `Iterator` with `find`
--> $DIR/search_is_some_fixable_some.rs:207:26 --> $DIR/search_is_some_fixable_some.rs:207:26
| |
@ -272,5 +264,5 @@ error: called `is_some()` after searching an `Iterator` with `find`
LL | let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_some(); LL | let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_some();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|fp| test_u32_2(*fp.field))` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|fp| test_u32_2(*fp.field))`
error: aborting due to 44 previous errors error: aborting due to 43 previous errors

View file

@ -53,7 +53,7 @@ fn resize_vector() {
vec1.resize(10, 0); vec1.resize(10, 0);
} }
fn do_stuff(vec: &mut Vec<u8>) {} fn do_stuff(vec: &mut [u8]) {}
fn extend_vector_with_manipulations_between() { fn extend_vector_with_manipulations_between() {
let len = 300; let len = 300;

View file

@ -1,5 +1,4 @@
#![warn(clippy::temporary_assignment)] #![warn(clippy::temporary_assignment)]
#![allow(const_item_mutation)]
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};

View file

@ -1,5 +1,5 @@
error: assignment to temporary error: assignment to temporary
--> $DIR/temporary_assignment.rs:48:5 --> $DIR/temporary_assignment.rs:47:5
| |
LL | Struct { field: 0 }.field = 1; 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` = note: `-D clippy::temporary-assignment` implied by `-D warnings`
error: assignment to temporary error: assignment to temporary
--> $DIR/temporary_assignment.rs:49:5 --> $DIR/temporary_assignment.rs:48:5
| |
LL | / MultiStruct { LL | / MultiStruct {
LL | | structure: Struct { field: 0 }, LL | | structure: Struct { field: 0 },
@ -17,13 +17,13 @@ LL | | .field = 1;
| |______________^ | |______________^
error: assignment to temporary error: assignment to temporary
--> $DIR/temporary_assignment.rs:54:5 --> $DIR/temporary_assignment.rs:53:5
| |
LL | ArrayStruct { array: [0] }.array[0] = 1; LL | ArrayStruct { array: [0] }.array[0] = 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: assignment to temporary error: assignment to temporary
--> $DIR/temporary_assignment.rs:55:5 --> $DIR/temporary_assignment.rs:54:5
| |
LL | (0, 0).0 = 1; LL | (0, 0).0 = 1;
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^

View file

@ -1,5 +1,6 @@
#![deny(clippy::trait_duplication_in_bounds)] #![deny(clippy::trait_duplication_in_bounds)]
use std::collections::BTreeMap;
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z) fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
@ -73,4 +74,25 @@ impl U for Life {
fn f() {} fn f() {}
} }
// should not warn
trait Iter: Iterator {
fn into_group_btreemap<K, V>(self) -> BTreeMap<K, Vec<V>>
where
Self: Iterator<Item = (K, V)> + Sized,
K: Ord + Eq,
{
unimplemented!();
}
}
struct Foo {}
trait FooIter: Iterator<Item = Foo> {
fn bar()
where
Self: Iterator<Item = Foo>,
{
}
}
fn main() {} fn main() {}

View file

@ -1,5 +1,5 @@
error: this trait bound is already specified in the where clause error: this trait bound is already specified in the where clause
--> $DIR/trait_duplication_in_bounds.rs:5:15 --> $DIR/trait_duplication_in_bounds.rs:6:15
| |
LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z) LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
| ^^^^^ | ^^^^^
@ -12,7 +12,7 @@ LL | #![deny(clippy::trait_duplication_in_bounds)]
= help: consider removing this trait bound = help: consider removing this trait bound
error: this trait bound is already specified in the where clause error: this trait bound is already specified in the where clause
--> $DIR/trait_duplication_in_bounds.rs:5:23 --> $DIR/trait_duplication_in_bounds.rs:6:23
| |
LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z) LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
| ^^^^^^^ | ^^^^^^^
@ -20,7 +20,7 @@ LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
= help: consider removing this trait bound = help: consider removing this trait bound
error: this trait bound is already specified in trait declaration error: this trait bound is already specified in trait declaration
--> $DIR/trait_duplication_in_bounds.rs:34:15 --> $DIR/trait_duplication_in_bounds.rs:35:15
| |
LL | Self: Default; LL | Self: Default;
| ^^^^^^^ | ^^^^^^^
@ -28,7 +28,7 @@ LL | Self: Default;
= help: consider removing this trait bound = help: consider removing this trait bound
error: this trait bound is already specified in trait declaration error: this trait bound is already specified in trait declaration
--> $DIR/trait_duplication_in_bounds.rs:48:15 --> $DIR/trait_duplication_in_bounds.rs:49:15
| |
LL | Self: Default + Clone; LL | Self: Default + Clone;
| ^^^^^^^ | ^^^^^^^
@ -36,7 +36,7 @@ LL | Self: Default + Clone;
= help: consider removing this trait bound = help: consider removing this trait bound
error: this trait bound is already specified in trait declaration error: this trait bound is already specified in trait declaration
--> $DIR/trait_duplication_in_bounds.rs:54:15 --> $DIR/trait_duplication_in_bounds.rs:55:15
| |
LL | Self: Default + Clone; LL | Self: Default + Clone;
| ^^^^^^^ | ^^^^^^^
@ -44,7 +44,7 @@ LL | Self: Default + Clone;
= help: consider removing this trait bound = help: consider removing this trait bound
error: this trait bound is already specified in trait declaration error: this trait bound is already specified in trait declaration
--> $DIR/trait_duplication_in_bounds.rs:54:25 --> $DIR/trait_duplication_in_bounds.rs:55:25
| |
LL | Self: Default + Clone; LL | Self: Default + Clone;
| ^^^^^ | ^^^^^
@ -52,12 +52,20 @@ LL | Self: Default + Clone;
= help: consider removing this trait bound = help: consider removing this trait bound
error: this trait bound is already specified in trait declaration error: this trait bound is already specified in trait declaration
--> $DIR/trait_duplication_in_bounds.rs:57:15 --> $DIR/trait_duplication_in_bounds.rs:58:15
| |
LL | Self: Default; LL | Self: Default;
| ^^^^^^^ | ^^^^^^^
| |
= help: consider removing this trait bound = help: consider removing this trait bound
error: aborting due to 7 previous errors error: this trait bound is already specified in trait declaration
--> $DIR/trait_duplication_in_bounds.rs:93:15
|
LL | Self: Iterator<Item = Foo>,
| ^^^^^^^^^^^^^^^^^^^^
|
= help: consider removing this trait bound
error: aborting due to 8 previous errors

View file

@ -1,6 +1,7 @@
#![warn(clippy::unnecessary_cast)] #![warn(clippy::unnecessary_cast)]
#![allow(clippy::no_effect)] #![allow(clippy::no_effect)]
#[rustfmt::skip]
fn main() { fn main() {
// Test cast_unnecessary // Test cast_unnecessary
1i32 as i32; 1i32 as i32;
@ -8,6 +9,12 @@ fn main() {
false as bool; false as bool;
&1i32 as &i32; &1i32 as &i32;
-1_i32 as i32;
- 1_i32 as i32;
-1f32 as f32;
1_i32 as i32;
1_f32 as f32;
// macro version // macro version
macro_rules! foo { macro_rules! foo {
($a:ident, $b:ident) => { ($a:ident, $b:ident) => {

View file

@ -1,5 +1,5 @@
error: casting integer literal to `i32` is unnecessary error: casting integer literal to `i32` is unnecessary
--> $DIR/unnecessary_cast.rs:6:5 --> $DIR/unnecessary_cast.rs:7:5
| |
LL | 1i32 as i32; LL | 1i32 as i32;
| ^^^^^^^^^^^ help: try: `1_i32` | ^^^^^^^^^^^ help: try: `1_i32`
@ -7,16 +7,46 @@ LL | 1i32 as i32;
= note: `-D clippy::unnecessary-cast` implied by `-D warnings` = note: `-D clippy::unnecessary-cast` implied by `-D warnings`
error: casting float literal to `f32` is unnecessary error: casting float literal to `f32` is unnecessary
--> $DIR/unnecessary_cast.rs:7:5 --> $DIR/unnecessary_cast.rs:8:5
| |
LL | 1f32 as f32; LL | 1f32 as f32;
| ^^^^^^^^^^^ help: try: `1_f32` | ^^^^^^^^^^^ help: try: `1_f32`
error: casting to the same type is unnecessary (`bool` -> `bool`) error: casting to the same type is unnecessary (`bool` -> `bool`)
--> $DIR/unnecessary_cast.rs:8:5 --> $DIR/unnecessary_cast.rs:9:5
| |
LL | false as bool; LL | false as bool;
| ^^^^^^^^^^^^^ help: try: `false` | ^^^^^^^^^^^^^ help: try: `false`
error: aborting due to 3 previous errors error: casting integer literal to `i32` is unnecessary
--> $DIR/unnecessary_cast.rs:12:5
|
LL | -1_i32 as i32;
| ^^^^^^^^^^^^^ help: try: `-1_i32`
error: casting integer literal to `i32` is unnecessary
--> $DIR/unnecessary_cast.rs:13:5
|
LL | - 1_i32 as i32;
| ^^^^^^^^^^^^^^ help: try: `- 1_i32`
error: casting float literal to `f32` is unnecessary
--> $DIR/unnecessary_cast.rs:14:5
|
LL | -1f32 as f32;
| ^^^^^^^^^^^^ help: try: `-1_f32`
error: casting integer literal to `i32` is unnecessary
--> $DIR/unnecessary_cast.rs:15:5
|
LL | 1_i32 as i32;
| ^^^^^^^^^^^^ help: try: `1_i32`
error: casting float literal to `f32` is unnecessary
--> $DIR/unnecessary_cast.rs:16:5
|
LL | 1_f32 as f32;
| ^^^^^^^^^^^^ help: try: `1_f32`
error: aborting due to 8 previous errors

View file

@ -1,23 +0,0 @@
// run-rustfix
#![feature(stmt_expr_attributes)]
#![allow(unused_variables, dead_code)]
struct Outer {
inner: u32,
}
#[deny(clippy::ref_in_deref)]
fn main() {
let outer = Outer { inner: 0 };
let inner = outer.inner;
}
struct Apple;
impl Apple {
fn hello(&self) {}
}
struct Package(pub *const Apple);
fn foobar(package: *const Package) {
unsafe { &*(*package).0 }.hello();
}

View file

@ -1,23 +0,0 @@
// run-rustfix
#![feature(stmt_expr_attributes)]
#![allow(unused_variables, dead_code)]
struct Outer {
inner: u32,
}
#[deny(clippy::ref_in_deref)]
fn main() {
let outer = Outer { inner: 0 };
let inner = (&outer).inner;
}
struct Apple;
impl Apple {
fn hello(&self) {}
}
struct Package(pub *const Apple);
fn foobar(package: *const Package) {
unsafe { &*(&*package).0 }.hello();
}

View file

@ -1,22 +0,0 @@
error: creating a reference that is immediately dereferenced
--> $DIR/unnecessary_ref.rs:13:17
|
LL | let inner = (&outer).inner;
| ^^^^^^^^ help: try this: `outer`
|
note: the lint level is defined here
--> $DIR/unnecessary_ref.rs:10:8
|
LL | #[deny(clippy::ref_in_deref)]
| ^^^^^^^^^^^^^^^^^^^^
error: creating a reference that is immediately dereferenced
--> $DIR/unnecessary_ref.rs:22:16
|
LL | unsafe { &*(&*package).0 }.hello();
| ^^^^^^^^^^^ help: try this: `(*package)`
|
= note: `-D clippy::ref-in-deref` implied by `-D warnings`
error: aborting due to 2 previous errors

View file

@ -67,7 +67,7 @@ fn not_ok() {
foo_rslice(mrrrrrslice); foo_rslice(mrrrrrslice);
foo_rslice(mrrrrrslice); foo_rslice(mrrrrrslice);
} }
#[allow(unused_parens, clippy::double_parens)] #[allow(unused_parens, clippy::double_parens, clippy::needless_borrow)]
foo_rrrrmr((&&&&MoreRef)); foo_rrrrmr((&&&&MoreRef));
generic_not_ok(mrslice); generic_not_ok(mrslice);

View file

@ -67,7 +67,7 @@ fn not_ok() {
foo_rslice(mrrrrrslice.as_ref()); foo_rslice(mrrrrrslice.as_ref());
foo_rslice(mrrrrrslice); foo_rslice(mrrrrrslice);
} }
#[allow(unused_parens, clippy::double_parens)] #[allow(unused_parens, clippy::double_parens, clippy::needless_borrow)]
foo_rrrrmr((&&&&MoreRef).as_ref()); foo_rrrrmr((&&&&MoreRef).as_ref());
generic_not_ok(mrslice); generic_not_ok(mrslice);

View file

@ -7,37 +7,37 @@ fn main() {
let mut v = Vec::new(); let mut v = Vec::new();
// these should be fine // these should be fine
write!(&mut v, "Hello"); write!(v, "Hello");
writeln!(&mut v, "Hello"); writeln!(v, "Hello");
let world = "world"; let world = "world";
writeln!(&mut v, "Hello {}", world); writeln!(v, "Hello {}", world);
writeln!(&mut v, "Hello {world}", world = world); writeln!(v, "Hello {world}", world = world);
writeln!(&mut v, "3 in hex is {:X}", 3); writeln!(v, "3 in hex is {:X}", 3);
writeln!(&mut v, "2 + 1 = {:.4}", 3); writeln!(v, "2 + 1 = {:.4}", 3);
writeln!(&mut v, "2 + 1 = {:5.4}", 3); writeln!(v, "2 + 1 = {:5.4}", 3);
writeln!(&mut v, "Debug test {:?}", "hello, world"); writeln!(v, "Debug test {:?}", "hello, world");
writeln!(&mut v, "{0:8} {1:>8}", "hello", "world"); writeln!(v, "{0:8} {1:>8}", "hello", "world");
writeln!(&mut v, "{1:8} {0:>8}", "hello", "world"); writeln!(v, "{1:8} {0:>8}", "hello", "world");
writeln!(&mut v, "{foo:8} {bar:>8}", foo = "hello", bar = "world"); writeln!(v, "{foo:8} {bar:>8}", foo = "hello", bar = "world");
writeln!(&mut v, "{bar:8} {foo:>8}", foo = "hello", bar = "world"); writeln!(v, "{bar:8} {foo:>8}", foo = "hello", bar = "world");
writeln!(&mut v, "{number:>width$}", number = 1, width = 6); writeln!(v, "{number:>width$}", number = 1, width = 6);
writeln!(&mut v, "{number:>0width$}", number = 1, width = 6); writeln!(v, "{number:>0width$}", number = 1, width = 6);
writeln!(&mut v, "{} of {:b} people know binary, the other half doesn't", 1, 2); writeln!(v, "{} of {:b} people know binary, the other half doesn't", 1, 2);
writeln!(&mut v, "10 / 4 is {}", 2.5); writeln!(v, "10 / 4 is {}", 2.5);
writeln!(&mut v, "2 + 1 = {}", 3); writeln!(v, "2 + 1 = {}", 3);
// these should throw warnings // these should throw warnings
write!(&mut v, "Hello {}", "world"); write!(v, "Hello {}", "world");
writeln!(&mut v, "Hello {} {}", world, "world"); writeln!(v, "Hello {} {}", world, "world");
writeln!(&mut v, "Hello {}", "world"); writeln!(v, "Hello {}", "world");
// positional args don't change the fact // positional args don't change the fact
// that we're using a literal -- this should // that we're using a literal -- this should
// throw a warning // throw a warning
writeln!(&mut v, "{0} {1}", "hello", "world"); writeln!(v, "{0} {1}", "hello", "world");
writeln!(&mut v, "{1} {0}", "hello", "world"); writeln!(v, "{1} {0}", "hello", "world");
// named args shouldn't change anything either // named args shouldn't change anything either
writeln!(&mut v, "{foo} {bar}", foo = "hello", bar = "world"); writeln!(v, "{foo} {bar}", foo = "hello", bar = "world");
writeln!(&mut v, "{bar} {foo}", foo = "hello", bar = "world"); writeln!(v, "{bar} {foo}", foo = "hello", bar = "world");
} }

View file

@ -1,134 +1,134 @@
error: literal with an empty format string error: literal with an empty format string
--> $DIR/write_literal.rs:30:32 --> $DIR/write_literal.rs:30:27
| |
LL | write!(&mut v, "Hello {}", "world"); LL | write!(v, "Hello {}", "world");
| ^^^^^^^ | ^^^^^^^
| |
= note: `-D clippy::write-literal` implied by `-D warnings` = note: `-D clippy::write-literal` implied by `-D warnings`
help: try this help: try this
| |
LL - write!(&mut v, "Hello {}", "world"); LL - write!(v, "Hello {}", "world");
LL + write!(&mut v, "Hello world"); LL + write!(v, "Hello world");
| |
error: literal with an empty format string error: literal with an empty format string
--> $DIR/write_literal.rs:31:44 --> $DIR/write_literal.rs:31:39
| |
LL | writeln!(&mut v, "Hello {} {}", world, "world"); LL | writeln!(v, "Hello {} {}", world, "world");
| ^^^^^^^ | ^^^^^^^
| |
help: try this help: try this
| |
LL - writeln!(&mut v, "Hello {} {}", world, "world"); LL - writeln!(v, "Hello {} {}", world, "world");
LL + writeln!(&mut v, "Hello {} world", world); LL + writeln!(v, "Hello {} world", world);
| |
error: literal with an empty format string error: literal with an empty format string
--> $DIR/write_literal.rs:32:34 --> $DIR/write_literal.rs:32:29
| |
LL | writeln!(&mut v, "Hello {}", "world"); LL | writeln!(v, "Hello {}", "world");
| ^^^^^^^ | ^^^^^^^
| |
help: try this help: try this
| |
LL - writeln!(&mut v, "Hello {}", "world"); LL - writeln!(v, "Hello {}", "world");
LL + writeln!(&mut v, "Hello world"); LL + writeln!(v, "Hello world");
| |
error: literal with an empty format string error: literal with an empty format string
--> $DIR/write_literal.rs:37:33 --> $DIR/write_literal.rs:37:28
| |
LL | writeln!(&mut v, "{0} {1}", "hello", "world"); LL | writeln!(v, "{0} {1}", "hello", "world");
| ^^^^^^^ | ^^^^^^^
| |
help: try this help: try this
| |
LL - writeln!(&mut v, "{0} {1}", "hello", "world"); LL - writeln!(v, "{0} {1}", "hello", "world");
LL + writeln!(&mut v, "hello {1}", "world"); LL + writeln!(v, "hello {1}", "world");
| |
error: literal with an empty format string error: literal with an empty format string
--> $DIR/write_literal.rs:37:42 --> $DIR/write_literal.rs:37:37
| |
LL | writeln!(&mut v, "{0} {1}", "hello", "world"); LL | writeln!(v, "{0} {1}", "hello", "world");
| ^^^^^^^ | ^^^^^^^
| |
help: try this help: try this
| |
LL - writeln!(&mut v, "{0} {1}", "hello", "world"); LL - writeln!(v, "{0} {1}", "hello", "world");
LL + writeln!(&mut v, "{0} world", "hello"); LL + writeln!(v, "{0} world", "hello");
| |
error: literal with an empty format string error: literal with an empty format string
--> $DIR/write_literal.rs:38:33 --> $DIR/write_literal.rs:38:28
| |
LL | writeln!(&mut v, "{1} {0}", "hello", "world"); LL | writeln!(v, "{1} {0}", "hello", "world");
| ^^^^^^^ | ^^^^^^^
| |
help: try this help: try this
| |
LL - writeln!(&mut v, "{1} {0}", "hello", "world"); LL - writeln!(v, "{1} {0}", "hello", "world");
LL + writeln!(&mut v, "{1} hello", "world"); LL + writeln!(v, "{1} hello", "world");
| |
error: literal with an empty format string error: literal with an empty format string
--> $DIR/write_literal.rs:38:42 --> $DIR/write_literal.rs:38:37
| |
LL | writeln!(&mut v, "{1} {0}", "hello", "world"); LL | writeln!(v, "{1} {0}", "hello", "world");
| ^^^^^^^ | ^^^^^^^
| |
help: try this help: try this
| |
LL - writeln!(&mut v, "{1} {0}", "hello", "world"); LL - writeln!(v, "{1} {0}", "hello", "world");
LL + writeln!(&mut v, "world {0}", "hello"); LL + writeln!(v, "world {0}", "hello");
| |
error: literal with an empty format string error: literal with an empty format string
--> $DIR/write_literal.rs:41:37 --> $DIR/write_literal.rs:41:32
| |
LL | writeln!(&mut v, "{foo} {bar}", foo = "hello", bar = "world"); LL | writeln!(v, "{foo} {bar}", foo = "hello", bar = "world");
| ^^^^^^^^^^^^^ | ^^^^^^^^^^^^^
| |
help: try this help: try this
| |
LL - writeln!(&mut v, "{foo} {bar}", foo = "hello", bar = "world"); LL - writeln!(v, "{foo} {bar}", foo = "hello", bar = "world");
LL + writeln!(&mut v, "hello {bar}", bar = "world"); LL + writeln!(v, "hello {bar}", bar = "world");
| |
error: literal with an empty format string error: literal with an empty format string
--> $DIR/write_literal.rs:41:52 --> $DIR/write_literal.rs:41:47
| |
LL | writeln!(&mut v, "{foo} {bar}", foo = "hello", bar = "world"); LL | writeln!(v, "{foo} {bar}", foo = "hello", bar = "world");
| ^^^^^^^^^^^^^ | ^^^^^^^^^^^^^
| |
help: try this help: try this
| |
LL - writeln!(&mut v, "{foo} {bar}", foo = "hello", bar = "world"); LL - writeln!(v, "{foo} {bar}", foo = "hello", bar = "world");
LL + writeln!(&mut v, "{foo} world", foo = "hello"); LL + writeln!(v, "{foo} world", foo = "hello");
| |
error: literal with an empty format string error: literal with an empty format string
--> $DIR/write_literal.rs:42:37 --> $DIR/write_literal.rs:42:32
| |
LL | writeln!(&mut v, "{bar} {foo}", foo = "hello", bar = "world"); LL | writeln!(v, "{bar} {foo}", foo = "hello", bar = "world");
| ^^^^^^^^^^^^^ | ^^^^^^^^^^^^^
| |
help: try this help: try this
| |
LL - writeln!(&mut v, "{bar} {foo}", foo = "hello", bar = "world"); LL - writeln!(v, "{bar} {foo}", foo = "hello", bar = "world");
LL + writeln!(&mut v, "{bar} hello", bar = "world"); LL + writeln!(v, "{bar} hello", bar = "world");
| |
error: literal with an empty format string error: literal with an empty format string
--> $DIR/write_literal.rs:42:52 --> $DIR/write_literal.rs:42:47
| |
LL | writeln!(&mut v, "{bar} {foo}", foo = "hello", bar = "world"); LL | writeln!(v, "{bar} {foo}", foo = "hello", bar = "world");
| ^^^^^^^^^^^^^ | ^^^^^^^^^^^^^
| |
help: try this help: try this
| |
LL - writeln!(&mut v, "{bar} {foo}", foo = "hello", bar = "world"); LL - writeln!(v, "{bar} {foo}", foo = "hello", bar = "world");
LL + writeln!(&mut v, "world {foo}", foo = "hello"); LL + writeln!(v, "world {foo}", foo = "hello");
| |
error: aborting due to 11 previous errors error: aborting due to 11 previous errors

View file

@ -6,20 +6,20 @@ use std::io::Write;
fn main() { fn main() {
let mut v = Vec::new(); let mut v = Vec::new();
writeln!(&mut v, "{}", "{hello}"); writeln!(v, "{}", "{hello}");
writeln!(&mut v, r"{}", r"{hello}"); writeln!(v, r"{}", r"{hello}");
writeln!(&mut v, "{}", '\''); writeln!(v, "{}", '\'');
writeln!(&mut v, "{}", '"'); writeln!(v, "{}", '"');
writeln!(&mut v, r"{}", '"'); // don't lint writeln!(v, r"{}", '"'); // don't lint
writeln!(&mut v, r"{}", '\''); writeln!(v, r"{}", '\'');
writeln!( writeln!(
&mut v, v,
"some {}", "some {}",
"hello \ "hello \
world!" world!"
); );
writeln!( writeln!(
&mut v, v,
"some {}\ "some {}\
{} \\ {}", {} \\ {}",
"1", "2", "3", "1", "2", "3",

Some files were not shown because too many files have changed in this diff Show more