[#6532](https://github.com/rust-lang/rust-clippy/pull/6532)
* [`single_match`] Suggest `if` over `if let` when possible
[#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)
* [`stable_sort_primitive`] Clarify error message
[#6611](https://github.com/rust-lang/rust-clippy/pull/6611)
[`iter_not_returning_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_not_returning_iterator
[`iter_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth
[`iter_nth_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth_zero
+[`iter_overeager_cloned`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_overeager_cloned
[`iter_skip_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_next
[`iterator_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iterator_step_by_zero
[`just_underscores_and_digits`]: https://rust-lang.github.io/rust-clippy/master/index.html#just_underscores_and_digits
[`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
[`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
[`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
-Copyright 2014-2021 The Rust Project Developers
+Copyright 2014-2022 The Rust Project Developers
Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
same "printed page" as the copyright notice for easier
identification within third-party archives.
-Copyright 2014-2021 The Rust Project Developers
+Copyright 2014-2022 The Rust Project Developers
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
MIT License
-Copyright (c) 2014-2021 The Rust Project Developers
+Copyright (c) 2014-2022 The Rust Project Developers
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
## License
-Copyright 2014-2021 The Rust Project Developers
+Copyright 2014-2022 The Rust Project Developers
Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
[https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0)> or the MIT license
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(|| {
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()
});
.args(["-Z", "no-codegen"])
.args(["--edition", "2021"])
.arg(filename)
- .env("__CLIPPY_INTERNAL_TESTS", "true")
.status()
.expect("failed to run cargo")
.code();
/// {`!=`, `>=`, `>`, `!=`, `>=`, `>`}) can be determined from the following
/// table:
///
- /// |Comparison |Bit Op|Example |is always|Formula |
- /// |------------|------|------------|---------|----------------------|
- /// |`==` or `!=`| `&` |`x & 2 == 3`|`false` |`c & m != c` |
- /// |`<` or `>=`| `&` |`x & 2 < 3` |`true` |`m < c` |
- /// |`>` or `<=`| `&` |`x & 1 > 1` |`false` |`m <= c` |
- /// |`==` or `!=`| `|` |`x | 1 == 0`|`false` |`c | m != c` |
- /// |`<` or `>=`| `|` |`x | 1 < 1` |`false` |`m >= c` |
- /// |`<=` or `>` | `|` |`x | 1 > 0` |`true` |`m > c` |
+ /// |Comparison |Bit Op|Example |is always|Formula |
+ /// |------------|------|-------------|---------|----------------------|
+ /// |`==` or `!=`| `&` |`x & 2 == 3` |`false` |`c & m != c` |
+ /// |`<` or `>=`| `&` |`x & 2 < 3` |`true` |`m < c` |
+ /// |`>` or `<=`| `&` |`x & 1 > 1` |`false` |`m <= c` |
+ /// |`==` or `!=`| `\|` |`x \| 1 == 0`|`false` |`c \| m != c` |
+ /// |`<` or `>=`| `\|` |`x \| 1 < 1` |`false` |`m >= c` |
+ /// |`<=` or `>` | `\|` |`x \| 1 > 0` |`true` |`m > c` |
///
/// ### Why is this bad?
/// If the bits that the comparison cares about are always
/// without changing the outcome. The basic structure can be seen in the
/// following table:
///
- /// |Comparison| Bit Op |Example |equals |
- /// |----------|---------|-----------|-------|
- /// |`>` / `<=`|`|` / `^`|`x | 2 > 3`|`x > 3`|
- /// |`<` / `>=`|`|` / `^`|`x ^ 1 < 4`|`x < 4`|
+ /// |Comparison| Bit Op |Example |equals |
+ /// |----------|----------|------------|-------|
+ /// |`>` / `<=`|`\|` / `^`|`x \| 2 > 3`|`x > 3`|
+ /// |`<` / `>=`|`\|` / `^`|`x ^ 1 < 4` |`x < 4`|
///
/// ### Why is this bad?
/// Not equally evil as [`bad_bit_mask`](#bad_bit_mask),
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, TyKind};
-use rustc_lint::{LateContext, LateLintPass};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
}
}
}
+
+ extract_msrv_attr!(LateContext);
}
lint_same_cond(cx, &conds);
lint_same_fns_in_if_cond(cx, &conds);
// Block duplication
- lint_same_then_else(cx, &blocks, conds.len() == blocks.len(), expr);
+ lint_same_then_else(cx, &conds, &blocks, conds.len() == blocks.len(), expr);
}
}
}
/// Implementation of `BRANCHES_SHARING_CODE` and `IF_SAME_THEN_ELSE` if the blocks are equal.
fn lint_same_then_else<'tcx>(
cx: &LateContext<'tcx>,
+ conds: &[&'tcx Expr<'_>],
blocks: &[&Block<'tcx>],
has_conditional_else: bool,
expr: &'tcx Expr<'_>,
// Check if each block has shared code
let has_expr = blocks[0].expr.is_some();
- let (start_eq, mut end_eq, expr_eq) = if let Some(block_eq) = scan_block_for_eq(cx, blocks) {
+ let (start_eq, mut end_eq, expr_eq) = if let Some(block_eq) = scan_block_for_eq(cx, conds, blocks) {
(block_eq.start_eq, block_eq.end_eq, block_eq.expr_eq)
} else {
return;
/// This function can also trigger the `IF_SAME_THEN_ELSE` in which case it'll return `None` to
/// abort any further processing and avoid duplicate lint triggers.
-fn scan_block_for_eq(cx: &LateContext<'_>, blocks: &[&Block<'_>]) -> Option<BlockEqual> {
+fn scan_block_for_eq(cx: &LateContext<'_>, conds: &[&Expr<'_>], blocks: &[&Block<'_>]) -> Option<BlockEqual> {
let mut start_eq = usize::MAX;
let mut end_eq = usize::MAX;
let mut expr_eq = true;
- let mut iter = blocks.windows(2);
- while let Some(&[win0, win1]) = iter.next() {
- let l_stmts = win0.stmts;
- let r_stmts = win1.stmts;
+ let mut iter = blocks.windows(2).enumerate();
+ while let Some((i, &[block0, block1])) = iter.next() {
+ let l_stmts = block0.stmts;
+ let r_stmts = block1.stmts;
// `SpanlessEq` now keeps track of the locals and is therefore context sensitive clippy#6752.
// The comparison therefore needs to be done in a way that builds the correct context.
it1.zip(it2)
.fold(0, |acc, (l, r)| if evaluator.eq_stmt(l, r) { acc + 1 } else { 0 })
};
- let block_expr_eq = both(&win0.expr, &win1.expr, |l, r| evaluator.eq_expr(l, r));
+ let block_expr_eq = both(&block0.expr, &block1.expr, |l, r| evaluator.eq_expr(l, r));
// IF_SAME_THEN_ELSE
if_chain! {
if block_expr_eq;
if l_stmts.len() == r_stmts.len();
if l_stmts.len() == current_start_eq;
- if !is_lint_allowed(cx, IF_SAME_THEN_ELSE, win0.hir_id);
- if !is_lint_allowed(cx, IF_SAME_THEN_ELSE, win1.hir_id);
+ // `conds` may have one last item than `blocks`.
+ // Any `i` from `blocks.windows(2)` will exist in `conds`, but `i+1` may not exist on the last iteration.
+ if !matches!(conds[i].kind, ExprKind::Let(..));
+ if !matches!(conds.get(i + 1).map(|e| &e.kind), Some(ExprKind::Let(..)));
+ if !is_lint_allowed(cx, IF_SAME_THEN_ELSE, block0.hir_id);
+ if !is_lint_allowed(cx, IF_SAME_THEN_ELSE, block1.hir_id);
then {
span_lint_and_note(
cx,
IF_SAME_THEN_ELSE,
- win0.span,
+ block0.span,
"this `if` has identical blocks",
- Some(win1.span),
+ Some(block1.span),
"same as this",
);
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::sugg::has_enclosing_paren;
use clippy_utils::ty::peel_mid_ty_refs;
use clippy_utils::{get_parent_expr, get_parent_node, is_lint_allowed, path_to_local};
use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX};
Pat, PatKind, UnOp,
};
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_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{symbol::sym, Span};
-use std::iter;
declare_clippy_lint! {
/// ### What it does
struct StateData {
/// Span of the top level expression
span: Span,
- /// The required mutability
- target_mut: Mutability,
}
enum State {
// The number of calls in a sequence which changed the referenced type
ty_changed_count: usize,
is_final_ufcs: bool,
+ /// The required mutability
+ target_mut: Mutability,
},
DerefedBorrow {
- count: u32,
+ count: usize,
+ required_precedence: i8,
+ msg: &'static str,
},
}
1
},
is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)),
- },
- StateData {
- span: expr.span,
target_mut,
},
+ StateData { span: expr.span },
));
},
RefOp::AddrOf => {
// Find the number of times the borrow is auto-derefed.
let mut iter = find_adjustments(cx.tcx, typeck, expr).iter();
- if let Some((i, adjust)) = iter.by_ref().enumerate().find_map(|(i, adjust)| {
- if !matches!(adjust.kind, Adjust::Deref(_)) {
- Some((i, adjust))
- } else if !adjust.target.is_ref() {
- // Add one to the number of references found.
- Some((i + 1, adjust))
+ let mut deref_count = 0usize;
+ let next_adjust = loop {
+ match iter.next() {
+ Some(adjust) => {
+ if !matches!(adjust.kind, Adjust::Deref(_)) {
+ 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 {
- None
- }
- }) {
- // 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,
- },
- ));
+ (2, 0, deref_msg)
}
+ } 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((
State::DerefMethod {
ty_changed_count: if deref_method_same_type(typeck.expr_ty(expr), typeck.expr_ty(sub_expr)) {
ty_changed_count + 1
},
is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)),
+ target_mut,
},
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),
}
}
+/// 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.
fn find_adjustments<'tcx>(
tcx: TyCtxt<'tcx>,
State::DerefMethod {
ty_changed_count,
is_final_ufcs,
+ target_mut,
} => {
let mut app = Applicability::MachineApplicable;
let (expr_str, expr_is_macro_call) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
};
let addr_of_str = if ty_changed_count < ref_count {
// 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 if data.target_mut == Mutability::Mut {
+ } else if target_mut == Mutability::Mut {
"&mut "
} else {
"&"
cx,
EXPLICIT_DEREF_METHODS,
data.span,
- match data.target_mut {
+ match target_mut {
Mutability::Not => "explicit `deref` method call",
Mutability::Mut => "explicit `deref_mut` method call",
},
app,
);
},
- State::DerefedBorrow { .. } => {
+ State::DerefedBorrow {
+ required_precedence,
+ msg,
+ ..
+ } => {
let mut app = Applicability::MachineApplicable;
let snip = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app).0;
span_lint_and_sugg(
cx,
NEEDLESS_BORROW,
data.span,
- &format!(
- "this expression borrows a reference (`{}`) that is immediately dereferenced by the compiler",
- cx.typeck_results().expr_ty(expr),
- ),
+ msg,
"change this to",
- snip.into(),
+ if required_precedence > expr.precedence().order() && !has_enclosing_paren(&snip) {
+ format!("({})", snip)
+ } else {
+ snip.into()
+ },
app,
);
},
let name = var.ident.name.as_str();
let variant_split = camel_case_split(name);
+ if variant_split.len() == 1 {
+ return;
+ }
pre = pre
.iter()
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::source::snippet;
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 if_chain::if_chain;
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_middle::ty::{self, TyS};
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
let rty = cx.typeck_results().expr_ty(r);
let lcpy = is_copy(cx, lty);
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
if (requires_ref || (lcpy && rcpy)) && implements_trait(cx, lty, trait_id, &[rty.into()]) {
span_lint_and_then(
// &foo == bar
(&ExprKind::AddrOf(BorrowKind::Ref, _, 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);
if (requires_ref || lcpy)
&& implements_trait(cx, lty, trait_id, &[cx.typeck_results().expr_ty(right).into()])
// foo == &bar
(_, &ExprKind::AddrOf(BorrowKind::Ref, _, 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);
if (requires_ref || rcpy)
&& implements_trait(cx, cx.typeck_results().expr_ty(left), trait_id, &[rty.into()])
}
}
}
+
+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
+ }
+ }
+}
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::kw;
-use rustc_span::{sym, Span};
+use rustc_span::{sym, BytePos, Span};
declare_clippy_lint! {
/// ### What it does
ExprKind::MethodCall(path, ..) => path.ident.name.as_str() == "to_string",
_ => false,
};
- let sugg = if is_new_string {
+ let sugg = if format_args.format_string_span.contains(value.span) {
+ // Implicit argument. e.g. `format!("{x}")` span points to `{x}`
+ let spdata = value.span.data();
+ let span = Span::new(
+ spdata.lo + BytePos(1),
+ spdata.hi - BytePos(1),
+ spdata.ctxt,
+ spdata.parent
+ );
+ let snip = snippet_with_applicability(cx, span, "..", &mut applicability);
+ if is_new_string {
+ snip.into()
+ } else {
+ format!("{snip}.to_string()")
+ }
+ } else if is_new_string {
snippet_with_applicability(cx, value.span, "..", &mut applicability).into_owned()
} else {
let sugg = Sugg::hir_with_applicability(cx, value, "<arg>", &mut applicability);
)
.and_then(|snip| {
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");
Span::new(pos, pos, item.span.ctxt(), item.span.parent())
LintId::of(methods::ITER_NEXT_SLICE),
LintId::of(methods::ITER_NTH),
LintId::of(methods::ITER_NTH_ZERO),
+ LintId::of(methods::ITER_OVEREAGER_CLONED),
LintId::of(methods::ITER_SKIP_NEXT),
LintId::of(methods::MANUAL_FILTER_MAP),
LintId::of(methods::MANUAL_FIND_MAP),
LintId::of(redundant_slicing::REDUNDANT_SLICING),
LintId::of(redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
LintId::of(reference::DEREF_ADDROF),
- LintId::of(reference::REF_IN_DEREF),
LintId::of(regex::INVALID_REGEX),
LintId::of(repeat_once::REPEAT_ONCE),
- LintId::of(return_self_not_must_use::RETURN_SELF_NOT_MUST_USE),
LintId::of(returns::LET_AND_RETURN),
LintId::of(returns::NEEDLESS_RETURN),
LintId::of(self_assignment::SELF_ASSIGNMENT),
LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL),
LintId::of(redundant_slicing::REDUNDANT_SLICING),
LintId::of(reference::DEREF_ADDROF),
- LintId::of(reference::REF_IN_DEREF),
LintId::of(repeat_once::REPEAT_ONCE),
LintId::of(strings::STRING_FROM_UTF8_AS_BYTES),
LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS),
methods::ITER_NEXT_SLICE,
methods::ITER_NTH,
methods::ITER_NTH_ZERO,
+ methods::ITER_OVEREAGER_CLONED,
methods::ITER_SKIP_NEXT,
methods::MANUAL_FILTER_MAP,
methods::MANUAL_FIND_MAP,
redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES,
ref_option_ref::REF_OPTION_REF,
reference::DEREF_ADDROF,
- reference::REF_IN_DEREF,
regex::INVALID_REGEX,
regex::TRIVIAL_REGEX,
repeat_once::REPEAT_ONCE,
LintId::of(ranges::RANGE_PLUS_ONE),
LintId::of(redundant_else::REDUNDANT_ELSE),
LintId::of(ref_option_ref::REF_OPTION_REF),
+ LintId::of(return_self_not_must_use::RETURN_SELF_NOT_MUST_USE),
LintId::of(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED),
LintId::of(strings::STRING_ADD_ASSIGN),
LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS),
LintId::of(methods::EXPECT_FUN_CALL),
LintId::of(methods::EXTEND_WITH_DRAIN),
LintId::of(methods::ITER_NTH),
+ LintId::of(methods::ITER_OVEREAGER_CLONED),
LintId::of(methods::MANUAL_STR_REPEAT),
LintId::of(methods::OR_FUN_CALL),
LintId::of(methods::SINGLE_CHAR_PATTERN),
LintId::of(methods::SUSPICIOUS_MAP),
LintId::of(mut_key::MUTABLE_KEY_TYPE),
LintId::of(octal_escapes::OCTAL_ESCAPES),
- LintId::of(return_self_not_must_use::RETURN_SELF_NOT_MUST_USE),
LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
])
store.register_late_pass(move || Box::new(needless_question_mark::NeedlessQuestionMark));
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_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(same_name_method::SameNameMethod));
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(shadow::Shadow::default()));
store.register_late_pass(|| Box::new(unit_types::UnitTypes));
store.register_late_pass(|| Box::new(mut_key::MutableKeyType));
store.register_late_pass(|| Box::new(modulo_arithmetic::ModuloArithmetic));
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_late_pass(|| Box::new(to_string_in_display::ToStringInDisplay::new()));
store.register_early_pass(|| Box::new(unsafe_removed_from_name::UnsafeNameRemoval));
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_method", "clippy::disallowed_methods");
+ ls.register_renamed("clippy::ref_in_deref", "clippy::needless_borrow");
// uplifted lints
ls.register_renamed("clippy::invalid_ref", "invalid_value");
use rustc_middle::hir::map::Map;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span;
-use rustc_span::symbol::{kw, Symbol};
+use rustc_span::symbol::{kw, Ident, Symbol};
declare_clippy_lint! {
/// ### What it does
impl<'tcx> LateLintPass<'tcx> for Lifetimes {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
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);
}
}
cx,
sig.decl,
Some(id),
+ None,
&item.generics,
item.span,
report_extra_lifetimes,
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
if let TraitItemKind::Fn(ref sig, ref body) = item.kind {
- let body = match *body {
- TraitFn::Required(_) => None,
- TraitFn::Provided(id) => Some(id),
+ let (body, trait_sig) = match *body {
+ TraitFn::Required(sig) => (None, Some(sig)),
+ 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);
}
}
}
cx: &LateContext<'tcx>,
decl: &'tcx FnDecl<'_>,
body: Option<BodyId>,
+ trait_sig: Option<&[Ident]>,
generics: &'tcx Generics<'_>,
span: Span,
report_extra_lifetimes: bool,
}
}
}
- if could_use_elision(cx, decl, body, generics.params) {
+ if could_use_elision(cx, decl, body, trait_sig, generics.params) {
span_lint(
cx,
NEEDLESS_LIFETIMES,
}
}
+// 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>(
cx: &LateContext<'tcx>,
func: &'tcx FnDecl<'_>,
body: Option<BodyId>,
+ trait_sig: Option<&[Ident]>,
named_generics: &'tcx [GenericParam<'_>],
) -> bool {
// There are two scenarios where elision works:
let input_lts = input_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 {
+ 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 {
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 {
return false;
}
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind, GenericArg, QPath};
-use rustc_lint::{LateContext, LateLintPass};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::ty::{self, Ty};
use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
}
}
}
+
+ extract_msrv_attr!(LateContext);
}
fn get_one_size_of_ty<'tcx>(
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
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 rustc_errors::Applicability;
use rustc_hir as hir;
-use rustc_lint::{LateContext, LateLintPass};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::mir::Mutability;
use rustc_middle::ty;
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::{sym, Span};
"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 {
fn check_expr(&mut self, cx: &LateContext<'_>, e: &hir::Expr<'_>) {
hir::BindingAnnotation::Unannotated, .., name, None
) = inner.kind {
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::ExprKind::Unary(hir::UnOp::Deref, inner) => {
if ident_eq(name, inner) {
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);
}
}
},
if let ty::Ref(_, ty, mutability) = obj_ty.kind() {
if matches!(mutability, Mutability::Not) {
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 {
lint_needless_cloning(cx, e.span, args[0].span);
}
}
}
+
+ extract_msrv_attr!(LateContext);
}
fn ident_eq(name: Ident, path: &hir::Expr<'_>) -> bool {
);
}
-fn lint(cx: &LateContext<'_>, replace: Span, root: Span, copied: bool) {
- let mut applicability = Applicability::MachineApplicable;
- if copied {
- span_lint_and_sugg(
- cx,
- MAP_CLONE,
- replace,
- "you are using an explicit closure for copying elements",
- "consider calling the dedicated `copied` method",
- format!(
- "{}.copied()",
- snippet_with_applicability(cx, root, "..", &mut applicability)
- ),
- applicability,
- );
- } else {
+impl MapClone {
+ fn lint_explicit_closure(&self, cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool) {
+ 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(
cx,
MAP_CLONE,
replace,
- "you are using an explicit closure for cloning elements",
- "consider calling the dedicated `cloned` method",
+ message,
+ &format!("consider calling the dedicated `{}` method", sugg_method),
format!(
- "{}.cloned()",
- snippet_with_applicability(cx, root, "..", &mut applicability)
+ "{}.{}()",
+ snippet_with_applicability(cx, root, "..", &mut applicability),
+ sugg_method,
),
applicability,
);
use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::{Span, Spanned};
-use rustc_span::sym;
+use rustc_span::{sym, symbol::kw};
use std::cmp::{max, Ordering};
use std::collections::hash_map::Entry;
let path_str = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false));
if path_str == "Err" {
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 {
// Looking for unused bindings (i.e.: `_e`)
for pat in inner.iter() {
if let PatKind::Binding(_, id, ident, None) = pat.kind {
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;
}
}
span_lint_and_note(cx,
MATCH_WILD_ERR_ARM,
arm.pat.span,
- &format!("`Err({})` matches all errors", &ident_bind_name),
+ &format!("`Err({})` matches all errors", ident_bind_name),
None,
"match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable",
);
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::{get_iterator_item_ty, is_copy};
+use itertools::Itertools;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+use rustc_middle::ty;
+use std::ops::Not;
+
+use super::ITER_OVEREAGER_CLONED;
+use crate::redundant_clone::REDUNDANT_CLONE;
+
+/// lint overeager use of `cloned()` for `Iterator`s
+pub(super) fn check<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &'tcx hir::Expr<'_>,
+ recv: &'tcx hir::Expr<'_>,
+ name: &str,
+ map_arg: &[hir::Expr<'_>],
+) {
+ // Check if it's iterator and get type associated with `Item`.
+ let inner_ty = match get_iterator_item_ty(cx, cx.typeck_results().expr_ty_adjusted(recv)) {
+ Some(ty) => ty,
+ _ => return,
+ };
+
+ match inner_ty.kind() {
+ ty::Ref(_, ty, _) if !is_copy(cx, ty) => {},
+ _ => return,
+ };
+
+ let (lint, preserve_cloned) = match name {
+ "count" => (REDUNDANT_CLONE, false),
+ _ => (ITER_OVEREAGER_CLONED, true),
+ };
+ let wildcard_params = map_arg.is_empty().not().then(|| "...").unwrap_or_default();
+ let msg = format!(
+ "called `cloned().{}({})` on an `Iterator`. It may be more efficient to call `{}({}){}` instead",
+ name,
+ wildcard_params,
+ name,
+ wildcard_params,
+ preserve_cloned.then(|| ".cloned()").unwrap_or_default(),
+ );
+
+ span_lint_and_sugg(
+ cx,
+ lint,
+ expr.span,
+ &msg,
+ "try this",
+ format!(
+ "{}.{}({}){}",
+ snippet(cx, recv.span, ".."),
+ name,
+ map_arg.iter().map(|a| snippet(cx, a.span, "..")).join(", "),
+ preserve_cloned.then(|| ".cloned()").unwrap_or_default(),
+ ),
+ Applicability::MachineApplicable,
+ );
+}
mod iter_next_slice;
mod iter_nth;
mod iter_nth_zero;
+mod iter_overeager_cloned;
mod iter_skip_next;
mod iterator_step_by_zero;
mod manual_saturating_arithmetic;
"used `cloned` where `copied` could be used instead"
}
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `_.cloned().<func>()` where call to `.cloned()` can be postponed.
+ ///
+ /// ### Why is this bad?
+ /// It's often inefficient to clone all elements of an iterator, when eventually, only some
+ /// of them will be consumed.
+ ///
+ /// ### Examples
+ /// ```rust
+ /// # let vec = vec!["string".to_string()];
+ ///
+ /// // Bad
+ /// vec.iter().cloned().take(10);
+ ///
+ /// // Good
+ /// vec.iter().take(10).cloned();
+ ///
+ /// // Bad
+ /// vec.iter().cloned().last();
+ ///
+ /// // Good
+ /// vec.iter().last().cloned();
+ ///
+ /// ```
+ /// ### Known Problems
+ /// This `lint` removes the side of effect of cloning items in the iterator.
+ /// A code that relies on that side-effect could fail.
+ ///
+ #[clippy::version = "1.59.0"]
+ pub ITER_OVEREAGER_CLONED,
+ perf,
+ "using `cloned()` early with `Iterator::iter()` can lead to some performance inefficiencies"
+}
+
declare_clippy_lint! {
/// ### What it does
/// Checks for usages of `Iterator::flat_map()` where `filter_map()` could be
CLONE_ON_COPY,
CLONE_ON_REF_PTR,
CLONE_DOUBLE_REF,
+ ITER_OVEREAGER_CLONED,
CLONED_INSTEAD_OF_COPIED,
FLAT_MAP_OPTION,
INEFFICIENT_TO_STRING,
},
_ => {},
},
- ("count", []) => match method_call(recv) {
- Some((name @ ("into_iter" | "iter" | "iter_mut"), [recv2], _)) => {
- iter_count::check(cx, expr, recv2, name);
+ (name @ "count", args @ []) => match method_call(recv) {
+ Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args),
+ Some((name2 @ ("into_iter" | "iter" | "iter_mut"), [recv2], _)) => {
+ iter_count::check(cx, expr, recv2, name2);
},
Some(("map", [_, arg], _)) => suspicious_map::check(cx, expr, recv, arg),
_ => {},
flat_map_identity::check(cx, expr, arg, span);
flat_map_option::check(cx, expr, arg, span);
},
- ("flatten", []) => {
- if let Some(("map", [recv, map_arg], _)) = method_call(recv) {
- map_flatten::check(cx, expr, recv, map_arg);
- }
+ (name @ "flatten", args @ []) => match method_call(recv) {
+ Some(("map", [recv, map_arg], _)) => map_flatten::check(cx, expr, recv, map_arg),
+ Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args),
+ _ => {},
},
("fold", [init, acc]) => unnecessary_fold::check(cx, expr, init, acc, span),
("for_each", [_]) => {
("is_file", []) => filetype_is_file::check(cx, expr, recv),
("is_none", []) => check_is_some_is_none(cx, expr, recv, false),
("is_some", []) => check_is_some_is_none(cx, expr, recv, true),
+ ("last", args @ []) | ("skip", args @ [_]) => {
+ if let Some((name2, [recv2, args2 @ ..], _span2)) = method_call(recv) {
+ if let ("cloned", []) = (name2, args2) {
+ iter_overeager_cloned::check(cx, expr, recv2, name, args);
+ }
+ }
+ },
("map", [m_arg]) => {
if let Some((name, [recv2, args @ ..], span2)) = method_call(recv) {
match (name, args) {
map_identity::check(cx, expr, recv, m_arg, span);
},
("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map),
- ("next", []) => {
- if let Some((name, [recv, args @ ..], _)) = method_call(recv) {
- match (name, args) {
- ("filter", [arg]) => filter_next::check(cx, expr, recv, arg),
- ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv, arg, msrv),
- ("iter", []) => iter_next_slice::check(cx, expr, recv),
- ("skip", [arg]) => iter_skip_next::check(cx, expr, recv, arg),
+ (name @ "next", args @ []) => {
+ if let Some((name2, [recv2, args2 @ ..], _)) = method_call(recv) {
+ match (name2, args2) {
+ ("cloned", []) => iter_overeager_cloned::check(cx, expr, recv2, name, args),
+ ("filter", [arg]) => filter_next::check(cx, expr, recv2, arg),
+ ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv2, arg, msrv),
+ ("iter", []) => iter_next_slice::check(cx, expr, recv2),
+ ("skip", [arg]) => iter_skip_next::check(cx, expr, recv2, arg),
("skip_while", [_]) => skip_while_next::check(cx, expr),
_ => {},
}
}
},
- ("nth", [n_arg]) => match method_call(recv) {
+ ("nth", args @ [n_arg]) => match method_call(recv) {
Some(("bytes", [recv2], _)) => bytes_nth::check(cx, expr, recv2, n_arg),
+ Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args),
Some(("iter", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false),
Some(("iter_mut", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true),
_ => iter_nth_zero::check(cx, expr, recv, n_arg),
}
},
("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg),
+ ("take", args @ [_arg]) => {
+ if let Some((name2, [recv2, args2 @ ..], _span2)) = method_call(recv) {
+ if let ("cloned", []) = (name2, args2) {
+ iter_overeager_cloned::check(cx, expr, recv2, name, args);
+ }
+ }
+ },
("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => {
implicit_clone::check(cx, name, expr, recv);
},
use clippy_utils::ty::{implements_trait, match_type};
use clippy_utils::{contains_return, is_trait_item, last_path_segment, paths};
use if_chain::if_chain;
+use rustc_errors::emitter::MAX_SUGGESTION_HIGHLIGHT_LINES;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
args: &'tcx [hir::Expr<'_>],
) {
/// Checks for `unwrap_or(T::new())` or `unwrap_or(T::default())`.
+ #[allow(clippy::too_many_arguments)]
fn check_unwrap_or_default(
cx: &LateContext<'_>,
name: &str,
arg: &hir::Expr<'_>,
or_has_args: bool,
span: Span,
+ method_span: Span,
) -> bool {
let is_default_default = || is_trait_item(cx, fun, sym::Default);
then {
let mut applicability = Applicability::MachineApplicable;
+ let hint = "unwrap_or_default()";
+ let mut sugg_span = span;
+
+ let mut sugg: String = format!(
+ "{}.{}",
+ snippet_with_applicability(cx, self_expr.span, "..", &mut applicability),
+ hint
+ );
+
+ if sugg.lines().count() > MAX_SUGGESTION_HIGHLIGHT_LINES {
+ sugg_span = method_span.with_hi(span.hi());
+ sugg = hint.to_string();
+ }
+
span_lint_and_sugg(
cx,
OR_FUN_CALL,
- span,
+ sugg_span,
&format!("use of `{}` followed by a call to `{}`", name, path),
"try this",
- format!(
- "{}.unwrap_or_default()",
- snippet_with_applicability(cx, self_expr.span, "..", &mut applicability)
- ),
+ sugg,
applicability,
);
match inner_arg.kind {
hir::ExprKind::Call(fun, or_args) => {
let or_has_args = !or_args.is_empty();
- if !check_unwrap_or_default(cx, name, fun, self_arg, arg, or_has_args, expr.span) {
+ if !check_unwrap_or_default(cx, name, fun, self_arg, arg, or_has_args, expr.span, method_span) {
let fun_span = if or_has_args { None } else { Some(fun.span) };
check_general_case(cx, name, method_span, self_arg, arg, expr.span, fun_span);
}
matches!(&cx.typeck_results().expr_ty(expr).peel_refs().kind(), ty::Array(_, _))
}
+#[allow(clippy::too_many_lines)]
fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) {
#[derive(Default)]
struct EqImpl {
hint = expr_snip;
} else {
span = expr.span.to(other.span);
+
+ let cmp_span = if other.span < expr.span {
+ other.span.between(expr.span)
+ } else {
+ expr.span.between(other.span)
+ };
if eq_impl.ty_eq_other {
- hint = format!("{} == {}", expr_snip, snippet(cx, other.span, ".."));
+ hint = format!(
+ "{}{}{}",
+ expr_snip,
+ snippet(cx, cmp_span, ".."),
+ snippet(cx, other.span, "..")
+ );
} else {
- hint = format!("{} == {}", snippet(cx, other.span, ".."), expr_snip);
+ hint = format!(
+ "{}{}{}",
+ snippet(cx, other.span, ".."),
+ snippet(cx, cmp_span, ".."),
+ expr_snip
+ );
}
}
use if_chain::if_chain;
use rustc_errors::Applicability;
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_middle::ty::TyS;
use rustc_session::{declare_lint_pass, declare_tool_lint};
}
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());
+ }
}
}
// construct a replacement escape
// 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) {
- 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
//! Checks for usage of `&Vec[_]` and `&String`.
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::ty::walk_ptrs_hir_ty;
-use clippy_utils::{expr_path_res, is_lint_allowed, match_any_diagnostic_items, paths};
+use clippy_utils::ty::expr_sig;
+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 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, NestedVisitorMap, Visitor};
use rustc_hir::{
- BinOpKind, BodyId, Expr, ExprKind, FnDecl, FnRetTy, GenericArg, Impl, ImplItem, ImplItemKind, Item, ItemKind,
- Lifetime, MutTy, Mutability, Node, PathSegment, QPath, TraitFn, TraitItem, TraitItemKind, Ty, TyKind,
+ self as hir, AnonConst, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnDecl, FnRetTy, GenericArg,
+ ImplItemKind, ItemKind, Lifetime, LifetimeName, Mutability, Node, Param, ParamName, PatKind, QPath, TraitFn,
+ TraitItem, TraitItemKind, TyKind,
};
use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::hir::map::Map;
+use rustc_middle::ty::{self, AssocItems, AssocKind, Ty};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span;
use rustc_span::symbol::Symbol;
use rustc_span::{sym, MultiSpan};
-use std::borrow::Cow;
+use std::fmt;
+use std::iter;
declare_clippy_lint! {
/// ### What it does
- /// This lint checks for function arguments of type `&String`
- /// or `&Vec` unless the references are mutable. It will also suggest you
- /// replace `.clone()` calls with the appropriate `.to_owned()`/`to_string()`
- /// calls.
+ /// This lint checks for function arguments of type `&String`, `&Vec`,
+ /// `&PathBuf`, and `Cow<_>`. It will also suggest you replace `.clone()` calls
+ /// with the appropriate `.to_owned()`/`to_string()` calls.
///
/// ### Why is this bad?
/// Requiring the argument to be of the specific size
/// or `&str` usually suffice and can be obtained from other types, too.
///
/// ### Known problems
- /// The lint does not follow data. So if you have an
- /// 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.
+ /// There may be `fn(&Vec)`-typed references pointing to your function.
/// If you have them, you will get a compiler error after applying this lint's
/// suggestions. You then have the choice to undo your changes or change the
/// type of the reference.
declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF, INVALID_NULL_PTR_USAGE]);
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_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
+ if let TraitItemKind::Fn(sig, trait_method) = &item.kind {
+ if matches!(trait_method, TraitFn::Provided(_)) {
+ // Handled by check body.
+ return;
+ }
- 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(parent_item) {
- if let ItemKind::Impl(Impl { of_trait: Some(_), .. }) = it.kind {
- return; // ignore trait impls
- }
+ 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,
+ );
}
- check_fn(cx, sig.decl, Some(body_id));
}
}
- fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
- if let TraitItemKind::Fn(ref sig, ref trait_method) = item.kind {
- let body_id = if let TraitFn::Provided(b) = *trait_method {
- Some(b)
- } else {
- None
- };
- check_fn(cx, sig.decl, body_id);
+ 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,
+ );
+ });
}
}
}
}
-#[allow(clippy::too_many_lines)]
-fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, opt_body_id: Option<BodyId>) {
- let body = opt_body_id.map(|id| cx.tcx.hir().body(id));
-
- for (idx, arg) in decl.inputs.iter().enumerate() {
- // Honor the allow attribute on parameters. See issue 5644.
- if let Some(body) = &body {
- if is_lint_allowed(cx, PTR_ARG, body.params[idx].hir_id) {
- continue;
- }
- }
+#[derive(Default)]
+struct PtrArgResult {
+ skip: bool,
+ replacements: Vec<PtrArgReplacement>,
+}
- let (item_name, path) = if_chain! {
- if let TyKind::Rptr(_, MutTy { ty, mutbl: Mutability::Not }) = arg.kind;
- if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind;
- if let Res::Def(_, did) = path.res;
- if let Some(item_name) = cx.tcx.get_diagnostic_name(did);
- then {
- (item_name, path)
- } else {
- continue
- }
- };
+struct PtrArgReplacement {
+ expr_span: Span,
+ self_span: Span,
+ replacement: &'static str,
+}
- match item_name {
- sym::Vec => {
- if let Some(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_owned()")]) {
- span_lint_and_then(
- cx,
- PTR_ARG,
- arg.span,
- "writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used \
- with non-Vec-based slices",
- |diag| {
- if let Some(ref snippet) = get_only_generic_arg_snippet(cx, arg) {
- 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,
- );
- }
- },
- );
- }
+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(' ')?;
},
- 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,
- );
- }
- },
- );
+ LifetimeName::Underscore => f.write_str("'_ ")?,
+ LifetimeName::Static => f.write_str("'static ")?,
+ _ => (),
+ }
+ f.write_str(self.mutability.prefix_str())
+ }
+}
+
+struct DerefTyDisplay<'a, 'tcx>(&'a LateContext<'tcx>, &'a DerefTy<'tcx>);
+impl fmt::Display for DerefTyDisplay<'_, '_> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ use std::fmt::Write;
+ match self.1 {
+ DerefTy::Str => f.write_str("str"),
+ DerefTy::Path => f.write_str("Path"),
+ DerefTy::Slice(hir_ty, ty) => {
+ 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(']')
},
- 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,
+ }
+ }
+}
+
+enum DerefTy<'tcx> {
+ Str,
+ Path,
+ Slice(Option<Span>, Ty<'tcx>),
+}
+impl<'tcx> DerefTy<'tcx> {
+ fn argless_str(&self) -> &'static str {
+ match *self {
+ Self::Str => "str",
+ Self::Path => "Path",
+ Self::Slice(..) => "[_]",
+ }
+ }
+
+ 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",
- "&Path".into(),
+ format!("&{}{}", mutability.prefix_str(), ty_name),
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,
- );
- }
+ return None;
},
- );
- }
- },
- 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,
+ _ => 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))),
});
- 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,
- );
- }
}
- },
- _ => {},
- }
- }
+ }
+ None
+ })
+}
+fn check_mut_from_ref(cx: &LateContext<'_>, decl: &FnDecl<'_>) {
if let FnRetTy::Return(ty) = decl.output {
if let Some((out, Mutability::Mut, _)) = get_rptr_lm(ty) {
let mut immutables = vec![];
- for (_, ref mutbl, ref argspan) in decl
+ for (_, mutbl, argspan) in decl
.inputs
.iter()
.filter_map(get_rptr_lm)
.filter(|&(lt, _, _)| lt.name == out.name)
{
- if *mutbl == Mutability::Mut {
+ if mutbl == Mutability::Mut {
return;
}
- immutables.push(*argspan);
+ immutables.push(argspan);
}
if immutables.is_empty() {
return;
}
}
-fn get_only_generic_arg_snippet(cx: &LateContext<'_>, arg: &Ty<'_>) -> Option<String> {
- if_chain! {
- if let TyKind::Path(QPath::Resolved(_, path)) = walk_ptrs_hir_ty(arg).kind;
- if let Some(&PathSegment{args: Some(parameters), ..}) = path.segments.last();
- let types: Vec<_> = parameters.args.iter().filter_map(|arg| match arg {
- GenericArg::Type(ty) => Some(ty),
- _ => None,
- }).collect();
- if types.len() == 1;
- then {
- snippet_opt(cx, types[0].span)
- } else {
- None
+#[allow(clippy::too_many_lines)]
+fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: &[PtrArg<'tcx>]) -> Vec<PtrArgResult> {
+ struct V<'cx, 'tcx> {
+ cx: &'cx LateContext<'tcx>,
+ /// Map from a local id to which argument it came from (index into `Self::args` and
+ /// `Self::results`)
+ bindings: HirIdMap<usize>,
+ /// The arguments being checked.
+ args: &'cx [PtrArg<'tcx>],
+ /// The results for each argument (len should match args.len)
+ results: Vec<PtrArgResult>,
+ /// The number of arguments which can't be linted. Used to return early.
+ skip_count: usize,
+ }
+ impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
+ type Map = Map<'tcx>;
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::OnlyBodies(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 {
Some((lt, m.mutbl, ty.span))
} else {
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::{snippet_opt, snippet_with_applicability};
-use clippy_utils::sugg::Sugg;
use if_chain::if_chain;
use rustc_ast::ast::{Expr, ExprKind, Mutability, UnOp};
use rustc_errors::Applicability;
}
}
}
-
-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,
- );
- }
- }
- }
-}
/// ```
#[clippy::version = "1.59.0"]
pub RETURN_SELF_NOT_MUST_USE,
- suspicious,
+ pedantic,
"missing `#[must_use]` annotation on a method returning `Self`"
}
use clippy_utils::{SpanlessEq, SpanlessHash};
use core::hash::{Hash, Hasher};
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_errors::Applicability;
use rustc_hir::def::Res;
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'tcx>) {
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 {
if_chain! {
)
) = cx.tcx.hir().get_if_local(*def_id);
then {
- if self_bounds_set.is_empty() {
+ if self_bounds_map.is_empty() {
for bound in self_bounds.iter() {
- let Some((self_res, _)) = get_trait_res_span_from_bound(bound) else { continue };
- self_bounds_set.insert(self_res);
+ let Some((self_res, self_segments, _)) = get_trait_info_from_bound(bound) else { continue };
+ self_bounds_map.insert(self_res, self_segments);
}
}
bound_predicate
.bounds
.iter()
- .filter_map(get_trait_res_span_from_bound)
- .for_each(|(trait_item_res, span)| {
- if self_bounds_set.get(&trait_item_res).is_some() {
- span_lint_and_help(
- cx,
- TRAIT_DUPLICATION_IN_BOUNDS,
- span,
- "this trait bound is already specified in trait declaration",
- None,
- "consider removing this trait bound",
- );
+ .filter_map(get_trait_info_from_bound)
+ .for_each(|(trait_item_res, trait_item_segments, span)| {
+ if let Some(self_segments) = self_bounds_map.get(&trait_item_res) {
+ if SpanlessEq::new(cx).eq_path_segments(self_segments, trait_item_segments) {
+ span_lint_and_help(
+ cx,
+ TRAIT_DUPLICATION_IN_BOUNDS,
+ span,
+ "this trait bound is already specified in trait declaration",
+ None,
+ "consider removing this trait bound",
+ );
+ }
}
});
}
}
}
-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 {
fn check_type_repetition<'tcx>(self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) {
struct SpanlessTy<'cx, 'tcx> {
let res = param
.bounds
.iter()
- .filter_map(get_trait_res_span_from_bound)
+ .filter_map(get_trait_info_from_bound)
.collect::<Vec<_>>();
map.insert(*ident, res);
}
if let Some(segment) = segments.first();
if let Some(trait_resolutions_direct) = map.get(&segment.ident);
then {
- for (res_where, _) in bound_predicate.bounds.iter().filter_map(get_trait_res_span_from_bound) {
- if let Some((_, span_direct)) = trait_resolutions_direct
+ for (res_where, _, _) in bound_predicate.bounds.iter().filter_map(get_trait_info_from_bound) {
+ if let Some((_, _, span_direct)) = trait_resolutions_direct
.iter()
- .find(|(res_direct, _)| *res_direct == res_where) {
+ .find(|(res_direct, _, _)| *res_direct == res_where) {
span_lint_and_help(
cx,
TRAIT_DUPLICATION_IN_BOUNDS,
}
}
}
+
+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
+ }
+}
fn extend_with_struct_pat(
qself1: &Option<ast::QSelf>,
path1: &ast::Path,
- fps1: &mut Vec<ast::PatField>,
+ fps1: &mut [ast::PatField],
rest1: bool,
start: usize,
alternatives: &mut Vec<P<Pat>>,
/// while also requiring `ps1[..n] ~ ps2[..n]` (pre) and `ps1[n + 1..] ~ ps2[n + 1..]` (post),
/// where `~` denotes semantic equality.
fn extend_with_matching_product(
- targets: &mut Vec<P<Pat>>,
+ targets: &mut [P<Pat>],
start: usize,
alternatives: &mut Vec<P<Pat>>,
predicate: impl Fn(&PatKind, &[P<Pat>], usize) -> bool,
///
/// Suppress lints whenever the suggested change would cause breakage for other crates.
(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
(msrv: Option<String> = None),
}
/// 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 iter = tcx.hir().parent_iter(child_id);
loop {
ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => 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,
- _ => break Some(Node::Expr(expr)),
+ _ => break Some((Node::Expr(expr), child_id)),
},
- Some((_, node)) => break Some(node),
+ Some((_, node)) => break Some((node, child_id)),
}
}
}
pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
!matches!(
get_expr_use_or_unification_node(tcx, expr),
- None | Some(Node::Stmt(Stmt {
- kind: StmtKind::Expr(_)
- | StmtKind::Semi(_)
- | StmtKind::Local(Local {
- pat: Pat {
- kind: PatKind::Wild,
+ None | Some((
+ Node::Stmt(Stmt {
+ kind: StmtKind::Expr(_)
+ | StmtKind::Semi(_)
+ | StmtKind::Local(Local {
+ pat: Pat {
+ kind: PatKind::Wild,
+ ..
+ },
..
- },
- ..
- }),
- ..
- }))
+ }),
+ ..
+ }),
+ _
+ ))
)
}
}
/// 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();
if chars.next() == Some('(') {
let mut depth = 1;
use rustc_ast::ast::Mutability;
use rustc_data_structures::fx::FxHashMap;
use rustc_hir as hir;
+use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::def_id::DefId;
-use rustc_hir::{TyKind, Unsafety};
+use rustc_hir::{Expr, TyKind, Unsafety};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::LateContext;
-use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
-use rustc_middle::ty::{self, AdtDef, IntTy, Predicate, Ty, TyCtxt, TypeFoldable, UintTy};
+use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst};
+use rustc_middle::ty::{
+ self, AdtDef, Binder, FnSig, IntTy, Predicate, PredicateKind, Ty, TyCtxt, TypeFoldable, UintTy,
+};
use rustc_span::symbol::Ident;
use rustc_span::{sym, Span, Symbol, DUMMY_SP};
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::query::normalize::AtExt;
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.
pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
})
.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.ty));
+ 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;
+ Some(p.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,
+ }
+ }
+}
## License
-Copyright 2014-2020 The Rust Project Developers
+Copyright 2014-2022 The Rust Project Developers
Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// - 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 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 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 {
args.extend(clippy_args);
}
}
}
-fn prepare_env() {
- set_var("CLIPPY_DISABLE_DOCS_LINKS", "true");
- set_var("__CLIPPY_INTERNAL_TESTS", "true");
- //set_var("RUST_BACKTRACE", "0");
-}
-
#[test]
fn compile_test() {
- prepare_env();
+ set_var("CLIPPY_DISABLE_DOCS_LINKS", "true");
let mut config = default_config();
run_ui(&mut config);
run_ui_test(&mut config);
-#![allow(clippy::redundant_clone)]
-#![warn(clippy::manual_non_exhaustive)]
+#![allow(clippy::redundant_clone, clippy::unnecessary_operation)]
+#![warn(clippy::manual_non_exhaustive, clippy::borrow_as_ptr, clippy::manual_bits)]
+use std::mem::{size_of, size_of_val};
use std::ops::Deref;
mod enums {
}
}
+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() {
option_as_ref_deref();
match_like_matches();
match_same_arms2();
manual_strip_msrv();
check_index_refutable_slice();
+ borrow_as_ptr();
}
--- /dev/null
+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
+
#![warn(clippy::borrow_interior_mutable_const)]
-#![allow(
- clippy::declare_interior_mutable_const,
- clippy::ref_in_deref,
- clippy::needless_borrow
-)]
+#![allow(clippy::declare_interior_mutable_const, clippy::needless_borrow)]
#![allow(const_item_mutation)]
use std::borrow::Cow;
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
| ^^^^^^
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
- --> $DIR/others.rs:59:16
+ --> $DIR/others.rs:55:16
|
LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutability
| ^^^^^^
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
- --> $DIR/others.rs:62:22
+ --> $DIR/others.rs:58:22
|
LL | let _once_ref = &ONCE_INIT; //~ ERROR interior mutability
| ^^^^^^^^^
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
- --> $DIR/others.rs:63:25
+ --> $DIR/others.rs:59:25
|
LL | let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability
| ^^^^^^^^^
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
- --> $DIR/others.rs:64:27
+ --> $DIR/others.rs:60:27
|
LL | let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability
| ^^^^^^^^^
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
- --> $DIR/others.rs:65:26
+ --> $DIR/others.rs:61:26
|
LL | let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability
| ^^^^^^^^^
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
- --> $DIR/others.rs:76:14
+ --> $DIR/others.rs:72:14
|
LL | let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability
| ^^^^^^^^^^^^
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
- --> $DIR/others.rs:77:14
+ --> $DIR/others.rs:73:14
|
LL | let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability
| ^^^^^^^^^^^^
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
- --> $DIR/others.rs:78:19
+ --> $DIR/others.rs:74:19
|
LL | let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability
| ^^^^^^^^^^^^
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
- --> $DIR/others.rs:79:14
+ --> $DIR/others.rs:75:14
|
LL | let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability
| ^^^^^^^^^^^^
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
- --> $DIR/others.rs:80:13
+ --> $DIR/others.rs:76:13
|
LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mutability
| ^^^^^^^^^^^^
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
- --> $DIR/others.rs:86:13
+ --> $DIR/others.rs:82:13
|
LL | let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability
| ^^^^^^^^^^^^
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
- --> $DIR/others.rs:91:5
+ --> $DIR/others.rs:87:5
|
LL | CELL.set(2); //~ ERROR interior mutability
| ^^^^
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
- --> $DIR/others.rs:92:16
+ --> $DIR/others.rs:88:16
|
LL | assert_eq!(CELL.get(), 6); //~ ERROR interior mutability
| ^^^^
+#![allow(clippy::needless_borrow)]
+
#[deny(clippy::naive_bytecount)]
fn main() {
let x = vec![0_u8; 16];
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
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using the bytecount crate: `bytecount::count(x, 0)`
|
note: the lint level is defined here
- --> $DIR/bytecount.rs:1:8
+ --> $DIR/bytecount.rs:3:8
|
LL | #[deny(clippy::naive_bytecount)]
| ^^^^^^^^^^^^^^^^^^^^^^^
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
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using the bytecount crate: `bytecount::count((&x[..]), 0)`
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
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using the bytecount crate: `bytecount::count(x, b + 1)`
clippy::no_effect,
clippy::unnecessary_operation,
clippy::vec_init_then_push,
- clippy::toplevel_ref_arg
+ clippy::toplevel_ref_arg,
+ clippy::needless_borrow
)]
use std::cell::RefCell;
clippy::no_effect,
clippy::unnecessary_operation,
clippy::vec_init_then_push,
- clippy::toplevel_ref_arg
+ clippy::toplevel_ref_arg,
+ clippy::needless_borrow
)]
use std::cell::RefCell;
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();
| ^^^^^^^^^^ help: try removing the `clone` call: `42`
= note: `-D clippy::clone-on-copy` implied by `-D warnings`
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();
| ^^^^^^^^^^^^^ help: try dereferencing it: `*(&42)`
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();
| ^^^^^^^^^^^^^^^^^^^ help: try dereferencing it: `*rc.borrow()`
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);
| ^^^^^^^^^ help: try removing the `clone` call: `x`
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();
| ^^^^^^^^^^^^^^ help: try removing the `clone` call: `m!(42)`
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];
| ^^^^^^^^^ help: try dereferencing it: `(*x)`
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());
| ^^^^^^^^^^^ help: try removing the `clone` call: `'z'`
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());
| ^^^^^^^^^^ help: try removing the `clone` call: `42`
--- /dev/null
+// run-rustfix
+
+use std::fmt::{self, Display};
+
+fn main() {
+ let a = Foo;
+
+ if a != "bar" {
+ println!("foo");
+ }
+
+ if a != "bar" {
+ println!("foo");
+ }
+}
+
+struct Foo;
+
+impl Display for Foo {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "foo")
+ }
+}
+
+impl PartialEq<&str> for Foo {
+ fn eq(&self, other: &&str) -> bool {
+ "foo" == *other
+ }
+}
--- /dev/null
+// run-rustfix
+
+use std::fmt::{self, Display};
+
+fn main() {
+ let a = Foo;
+
+ if a.to_string() != "bar" {
+ println!("foo");
+ }
+
+ if "bar" != a.to_string() {
+ println!("foo");
+ }
+}
+
+struct Foo;
+
+impl Display for Foo {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "foo")
+ }
+}
+
+impl PartialEq<&str> for Foo {
+ fn eq(&self, other: &&str) -> bool {
+ "foo" == *other
+ }
+}
--- /dev/null
+error: this creates an owned instance just for comparison
+ --> $DIR/comparison_flip.rs:8:8
+ |
+LL | if a.to_string() != "bar" {
+ | ^^^^^^^^^^^^^ help: try: `a`
+ |
+ = note: `-D clippy::cmp-owned` implied by `-D warnings`
+
+error: this creates an owned instance just for comparison
+ --> $DIR/comparison_flip.rs:12:17
+ |
+LL | if "bar" != a.to_string() {
+ | ---------^^^^^^^^^^^^^
+ | |
+ | help: try: `a != "bar"`
+
+error: aborting due to 2 previous errors
+
// run-rustfix
-#![allow(dead_code)]
+#![allow(dead_code, clippy::needless_borrow)]
#![warn(clippy::duration_subsec)]
use std::time::Duration;
// run-rustfix
-#![allow(dead_code)]
+#![allow(dead_code, clippy::needless_borrow)]
#![warn(clippy::duration_subsec)]
use std::time::Duration;
NoRight,
}
+// #8324
+enum Phase {
+ PreLookup,
+ Lookup,
+ PostLookup,
+}
+
fn main() {}
clippy::no_effect,
clippy::redundant_closure_call,
clippy::needless_pass_by_value,
- clippy::option_map_unit_fn
-)]
-#![warn(
- clippy::redundant_closure,
- clippy::redundant_closure_for_method_calls,
+ clippy::option_map_unit_fn,
clippy::needless_borrow
)]
+#![warn(clippy::redundant_closure, clippy::redundant_closure_for_method_calls)]
use std::path::{Path, PathBuf};
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 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 {
Some(1u8).map(|a| unsafe_fn(a)); // unsafe fn
}
clippy::no_effect,
clippy::redundant_closure_call,
clippy::needless_pass_by_value,
- clippy::option_map_unit_fn
-)]
-#![warn(
- clippy::redundant_closure,
- clippy::redundant_closure_for_method_calls,
+ clippy::option_map_unit_fn,
clippy::needless_borrow
)]
+#![warn(clippy::redundant_closure, clippy::redundant_closure_for_method_calls)]
use std::path::{Path, PathBuf};
error: redundant closure
- --> $DIR/eta.rs:31:27
+ --> $DIR/eta.rs:28:27
|
LL | let a = Some(1u8).map(|a| foo(a));
| ^^^^^^^^^^ help: replace the closure with the function itself: `foo`
= note: `-D clippy::redundant-closure` implied by `-D warnings`
error: redundant closure
- --> $DIR/eta.rs:35:40
+ --> $DIR/eta.rs:32:40
|
LL | let _: Option<Vec<u8>> = true.then(|| vec![]); // special case vec!
| ^^^^^^^^^ help: replace the closure with `Vec::new`: `std::vec::Vec::new`
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?
| ^^^^^^^^^^^^^ 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
- --> $DIR/eta.rs:37:26
+ --> $DIR/eta.rs:34:26
|
LL | all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted
| ^^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `below`
error: redundant closure
- --> $DIR/eta.rs:43:27
+ --> $DIR/eta.rs:40:27
|
LL | let e = Some(1u8).map(|a| divergent(a));
| ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `divergent`
error: redundant closure
- --> $DIR/eta.rs:44:27
+ --> $DIR/eta.rs:41:27
|
LL | let e = Some(1u8).map(|a| generic(a));
| ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `generic`
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());
| ^^^^^^^^^^^ help: replace the closure with the method itself: `TestStruct::foo`
= note: `-D clippy::redundant-closure-for-method-calls` implied by `-D warnings`
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());
| ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `TestTrait::trait_foo`
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());
| ^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::vec::Vec::clear`
error: redundant closure
- --> $DIR/eta.rs:97:29
+ --> $DIR/eta.rs:94:29
|
LL | let e = Some("str").map(|s| s.to_string());
| ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::string::ToString::to_string`
error: redundant closure
- --> $DIR/eta.rs:98:27
+ --> $DIR/eta.rs:95:27
|
LL | let e = Some('a').map(|s| s.to_uppercase());
| ^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_uppercase`
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();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_ascii_uppercase`
error: redundant closure
- --> $DIR/eta.rs:163:22
+ --> $DIR/eta.rs:160:22
|
LL | requires_fn_once(|| x());
| ^^^^^^ help: replace the closure with the function itself: `x`
error: redundant closure
- --> $DIR/eta.rs:170:27
+ --> $DIR/eta.rs:167:27
|
LL | let a = Some(1u8).map(|a| foo_ptr(a));
| ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo_ptr`
error: redundant closure
- --> $DIR/eta.rs:175:27
+ --> $DIR/eta.rs:172:27
|
LL | let a = Some(1u8).map(|a| closure(a));
| ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `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));
| ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res`
error: redundant closure
- --> $DIR/eta.rs:208:28
+ --> $DIR/eta.rs:205:28
|
LL | y.into_iter().for_each(|x| add_to_res(x));
| ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res`
error: redundant closure
- --> $DIR/eta.rs:209:28
+ --> $DIR/eta.rs:206:28
|
LL | z.into_iter().for_each(|x| add_to_res(x));
| ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `add_to_res`
error: redundant closure
- --> $DIR/eta.rs:216:21
+ --> $DIR/eta.rs:213:21
|
LL | Some(1).map(|n| closure(n));
| ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut closure`
error: redundant closure
- --> $DIR/eta.rs:235:21
+ --> $DIR/eta.rs:232:21
|
LL | map_str_to_path(|s| s.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
// run-rustfix
-#![allow(unused_variables, clippy::clone_double_ref)]
+#![allow(unused_variables, clippy::clone_double_ref, clippy::needless_borrow)]
#![warn(clippy::explicit_deref_methods)]
use std::ops::{Deref, DerefMut};
// run-rustfix
-#![allow(unused_variables, clippy::clone_double_ref)]
+#![allow(unused_variables, clippy::clone_double_ref, clippy::needless_borrow)]
#![warn(clippy::explicit_deref_methods)]
use std::ops::{Deref, DerefMut};
clippy::iter_next_loop,
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)]
fn main() {
let mut vec = vec![1, 2, 3, 4];
clippy::iter_next_loop,
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)]
fn main() {
let mut vec = vec![1, 2, 3, 4];
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() {}
| ^^^^^^^^^^ help: to write this more concisely, try: `&vec`
= 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
- --> $DIR/for_loop_fixable.rs:40:15
+ --> $DIR/for_loop_fixable.rs:45:15
|
LL | for _v in vec.iter_mut() {}
| ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&mut vec`
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() {}
| ^^^^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `out_vec`
= 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
- --> $DIR/for_loop_fixable.rs:48:15
+ --> $DIR/for_loop_fixable.rs:53:15
|
LL | for _v in [1, 2, 3].iter() {}
| ^^^^^^^^^^^^^^^^ 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
- --> $DIR/for_loop_fixable.rs:52:15
+ --> $DIR/for_loop_fixable.rs:57:15
|
LL | for _v in [0; 32].iter() {}
| ^^^^^^^^^^^^^^ 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
- --> $DIR/for_loop_fixable.rs:57:15
+ --> $DIR/for_loop_fixable.rs:62:15
|
LL | for _v in ll.iter() {}
| ^^^^^^^^^ 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
- --> $DIR/for_loop_fixable.rs:60:15
+ --> $DIR/for_loop_fixable.rs:65:15
|
LL | for _v in vd.iter() {}
| ^^^^^^^^^ 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
- --> $DIR/for_loop_fixable.rs:63:15
+ --> $DIR/for_loop_fixable.rs:68:15
|
LL | for _v in bh.iter() {}
| ^^^^^^^^^ 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
- --> $DIR/for_loop_fixable.rs:66:15
+ --> $DIR/for_loop_fixable.rs:71:15
|
LL | for _v in hm.iter() {}
| ^^^^^^^^^ 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
- --> $DIR/for_loop_fixable.rs:69:15
+ --> $DIR/for_loop_fixable.rs:74:15
|
LL | for _v in bt.iter() {}
| ^^^^^^^^^ 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
- --> $DIR/for_loop_fixable.rs:72:15
+ --> $DIR/for_loop_fixable.rs:77:15
|
LL | for _v in hs.iter() {}
| ^^^^^^^^^ 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
- --> $DIR/for_loop_fixable.rs:75:15
+ --> $DIR/for_loop_fixable.rs:80:15
|
LL | for _v in bs.iter() {}
| ^^^^^^^^^ help: to write this more concisely, try: `&bs`
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() {
| ^^^^^^^^^^^^^^^^^^^^ 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
- --> $DIR/for_loop_fixable.rs:270:18
+ --> $DIR/for_loop_fixable.rs:275:18
|
LL | for _ in t.into_iter() {}
| ^^^^^^^^^^^^^ help: to write this more concisely, try: `&t`
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() {}
| ^^^^^^^^^^^^^ help: to write this more concisely, try: `r`
// 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)]
struct Foo(pub String);
let _s: String = (&*v.join("\n")).to_string();
format!("prepend {:+}", "s");
+
+ // Issue #8290
+ let x = "foo";
+ let _ = x.to_string();
+ let _ = format!("{x:?}"); // Don't lint on debug
+ let _ = x.to_string();
}
// 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)]
struct Foo(pub String);
let _s: String = format!("{}", &*v.join("\n"));
format!("prepend {:+}", "s");
+
+ // Issue #8290
+ let x = "foo";
+ let _ = format!("{x}");
+ let _ = format!("{x:?}"); // Don't lint on debug
+ let _ = format!("{y}", y = x);
}
error: useless use of `format!`
- --> $DIR/format.rs:13:5
+ --> $DIR/format.rs:18:5
|
LL | format!("foo");
| ^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()`
= note: `-D clippy::useless-format` implied by `-D warnings`
error: useless use of `format!`
- --> $DIR/format.rs:14:5
+ --> $DIR/format.rs:19:5
|
LL | format!("{{}}");
| ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{}".to_string()`
error: useless use of `format!`
- --> $DIR/format.rs:15:5
+ --> $DIR/format.rs:20:5
|
LL | format!("{{}} abc {{}}");
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{} abc {}".to_string()`
error: useless use of `format!`
- --> $DIR/format.rs:16:5
+ --> $DIR/format.rs:21:5
|
LL | / format!(
LL | | r##"foo {{}}
|
error: useless use of `format!`
- --> $DIR/format.rs:21:13
+ --> $DIR/format.rs:26:13
|
LL | let _ = format!("");
| ^^^^^^^^^^^ help: consider using `String::new()`: `String::new()`
error: useless use of `format!`
- --> $DIR/format.rs:23:5
+ --> $DIR/format.rs:28:5
|
LL | format!("{}", "foo");
| ^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()`
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.
| ^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()`
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.
| ^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()`
error: useless use of `format!`
- --> $DIR/format.rs:33:5
+ --> $DIR/format.rs:38:5
|
LL | format!("{}", arg);
| ^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string()`
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.
| ^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string()`
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.
| ^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string()`
error: useless use of `format!`
- --> $DIR/format.rs:65:5
+ --> $DIR/format.rs:70:5
|
LL | format!("{}", 42.to_string());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `42.to_string()`
error: useless use of `format!`
- --> $DIR/format.rs:67:5
+ --> $DIR/format.rs:72:5
|
LL | format!("{}", x.display().to_string());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.display().to_string()`
error: useless use of `format!`
- --> $DIR/format.rs:71:18
+ --> $DIR/format.rs:76:18
|
LL | let _ = Some(format!("{}", a + "bar"));
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `a + "bar"`
error: useless use of `format!`
- --> $DIR/format.rs:75:22
+ --> $DIR/format.rs:80:22
|
LL | let _s: String = format!("{}", &*v.join("/n"));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `(&*v.join("/n")).to_string()`
-error: aborting due to 15 previous errors
+error: useless use of `format!`
+ --> $DIR/format.rs:86:13
+ |
+LL | let _ = format!("{x}");
+ | ^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()`
+
+error: useless use of `format!`
+ --> $DIR/format.rs:88:13
+ |
+LL | let _ = format!("{y}", y = x);
+ | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()`
+
+error: aborting due to 17 previous errors
let (y, x) = (1, 2);
return Ok(&foo[x..y]);
}
+
+ // Issue #7579
+ let _ = if let Some(0) = None { 0 } else { 0 };
+
+ if true {
+ return Err(());
+ } else if let Some(0) = None {
+ return Err(());
+ }
+
+ let _ = if let Some(0) = None {
+ 0
+ } else if let Some(1) = None {
+ 0
+ } else {
+ 0
+ };
}
fn main() {}
--- /dev/null
+// run-rustfix
+#![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)]
+
+fn main() {
+ let vec = vec!["1".to_string(), "2".to_string(), "3".to_string()];
+
+ let _: Option<String> = vec.iter().last().cloned();
+
+ let _: Option<String> = vec.iter().chain(vec.iter()).next().cloned();
+
+ let _: usize = vec.iter().filter(|x| x == &"2").count();
+
+ let _: Vec<_> = vec.iter().take(2).cloned().collect();
+
+ let _: Vec<_> = vec.iter().skip(2).cloned().collect();
+
+ let _ = vec.iter().filter(|x| x == &"2").nth(2).cloned();
+
+ let _ = [Some(Some("str".to_string())), Some(Some("str".to_string()))]
+ .iter().flatten().cloned();
+
+ // Not implemented yet
+ let _ = vec.iter().cloned().filter(|x| x.starts_with('2'));
+
+ // Not implemented yet
+ let _ = vec.iter().cloned().map(|x| x.len());
+
+ // This would fail if changed.
+ let _ = vec.iter().cloned().map(|x| x + "2");
+
+ // Not implemented yet
+ let _ = vec.iter().cloned().find(|x| x == "2");
+
+ // Not implemented yet
+ let _ = vec.iter().cloned().for_each(|x| assert!(!x.is_empty()));
+
+ // Not implemented yet
+ let _ = vec.iter().cloned().all(|x| x.len() == 1);
+
+ // Not implemented yet
+ let _ = vec.iter().cloned().any(|x| x.len() == 1);
+
+ // Should probably stay as it is.
+ let _ = [0, 1, 2, 3, 4].iter().cloned().take(10);
+}
--- /dev/null
+// run-rustfix
+#![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)]
+
+fn main() {
+ let vec = vec!["1".to_string(), "2".to_string(), "3".to_string()];
+
+ let _: Option<String> = vec.iter().cloned().last();
+
+ let _: Option<String> = vec.iter().chain(vec.iter()).cloned().next();
+
+ let _: usize = vec.iter().filter(|x| x == &"2").cloned().count();
+
+ let _: Vec<_> = vec.iter().cloned().take(2).collect();
+
+ let _: Vec<_> = vec.iter().cloned().skip(2).collect();
+
+ let _ = vec.iter().filter(|x| x == &"2").cloned().nth(2);
+
+ let _ = [Some(Some("str".to_string())), Some(Some("str".to_string()))]
+ .iter()
+ .cloned()
+ .flatten();
+
+ // Not implemented yet
+ let _ = vec.iter().cloned().filter(|x| x.starts_with('2'));
+
+ // Not implemented yet
+ let _ = vec.iter().cloned().map(|x| x.len());
+
+ // This would fail if changed.
+ let _ = vec.iter().cloned().map(|x| x + "2");
+
+ // Not implemented yet
+ let _ = vec.iter().cloned().find(|x| x == "2");
+
+ // Not implemented yet
+ let _ = vec.iter().cloned().for_each(|x| assert!(!x.is_empty()));
+
+ // Not implemented yet
+ let _ = vec.iter().cloned().all(|x| x.len() == 1);
+
+ // Not implemented yet
+ let _ = vec.iter().cloned().any(|x| x.len() == 1);
+
+ // Should probably stay as it is.
+ let _ = [0, 1, 2, 3, 4].iter().cloned().take(10);
+}
--- /dev/null
+error: called `cloned().last()` on an `Iterator`. It may be more efficient to call `last().cloned()` instead
+ --> $DIR/iter_overeager_cloned.rs:7:29
+ |
+LL | let _: Option<String> = vec.iter().cloned().last();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().last().cloned()`
+ |
+ = note: `-D clippy::iter-overeager-cloned` implied by `-D warnings`
+
+error: called `cloned().next()` on an `Iterator`. It may be more efficient to call `next().cloned()` instead
+ --> $DIR/iter_overeager_cloned.rs:9:29
+ |
+LL | let _: Option<String> = vec.iter().chain(vec.iter()).cloned().next();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().chain(vec.iter()).next().cloned()`
+
+error: called `cloned().count()` on an `Iterator`. It may be more efficient to call `count()` instead
+ --> $DIR/iter_overeager_cloned.rs:11:20
+ |
+LL | let _: usize = vec.iter().filter(|x| x == &"2").cloned().count();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().filter(|x| x == &"2").count()`
+ |
+ = note: `-D clippy::redundant-clone` implied by `-D warnings`
+
+error: called `cloned().take(...)` on an `Iterator`. It may be more efficient to call `take(...).cloned()` instead
+ --> $DIR/iter_overeager_cloned.rs:13:21
+ |
+LL | let _: Vec<_> = vec.iter().cloned().take(2).collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().take(2).cloned()`
+
+error: called `cloned().skip(...)` on an `Iterator`. It may be more efficient to call `skip(...).cloned()` instead
+ --> $DIR/iter_overeager_cloned.rs:15:21
+ |
+LL | let _: Vec<_> = vec.iter().cloned().skip(2).collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().skip(2).cloned()`
+
+error: called `cloned().nth(...)` on an `Iterator`. It may be more efficient to call `nth(...).cloned()` instead
+ --> $DIR/iter_overeager_cloned.rs:17:13
+ |
+LL | let _ = vec.iter().filter(|x| x == &"2").cloned().nth(2);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().filter(|x| x == &"2").nth(2).cloned()`
+
+error: called `cloned().flatten()` on an `Iterator`. It may be more efficient to call `flatten().cloned()` instead
+ --> $DIR/iter_overeager_cloned.rs:19:13
+ |
+LL | let _ = [Some(Some("str".to_string())), Some(Some("str".to_string()))]
+ | _____________^
+LL | | .iter()
+LL | | .cloned()
+LL | | .flatten();
+ | |__________________^
+ |
+help: try this
+ |
+LL ~ let _ = [Some(Some("str".to_string())), Some(Some("str".to_string()))]
+LL ~ .iter().flatten().cloned();
+ |
+
+error: aborting due to 7 previous errors
+
let b = &mut 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)]
let b = &mut 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)]
-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
|
LL | let _ = x(&&a); // warn
|
= 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
|
LL | mut_ref(&mut &mut b); // warn
| ^^^^^^^^^^^ 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
|
LL | &&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
|
LL | 46 => &&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
|
LL | break &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
|
LL | let _ = x(&&&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
|
LL | let _ = x(&mut &&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
|
LL | let _ = x(&&&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
|
LL | let _ = x(&&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
|
LL | x(&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
#![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) {}
}
}
+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() {}
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) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= 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)
- --> $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) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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, ()> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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, ()>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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)) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-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
let x = Some(3);
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
+}
let x = Some(3);
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?)
+}
|
= 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
#![allow(unused_variables, clippy::blacklisted_name)]
#![warn(clippy::op_ref)]
use std::collections::HashSet;
-use std::ops::BitAnd;
+use std::ops::{BitAnd, Mul};
fn main() {
let tracked_fds: HashSet<i32> = HashSet::new();
let y = Y(2);
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
+ }
+}
| |
| 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
}
}
+mod issue8239 {
+ fn more_than_max_suggestion_highest_lines_0() {
+ let frames = Vec::new();
+ frames
+ .iter()
+ .map(|f: &String| f.to_lowercase())
+ .reduce(|mut acc, f| {
+ acc.push_str(&f);
+ acc
+ })
+ .unwrap_or_default();
+ }
+
+ fn more_to_max_suggestion_highest_lines_1() {
+ let frames = Vec::new();
+ let iter = frames.iter();
+ iter.map(|f: &String| f.to_lowercase())
+ .reduce(|mut acc, f| {
+ let _ = "";
+ let _ = "";
+ acc.push_str(&f);
+ acc
+ })
+ .unwrap_or_default();
+ }
+
+ fn equal_to_max_suggestion_highest_lines() {
+ let frames = Vec::new();
+ let iter = frames.iter();
+ iter.map(|f: &String| f.to_lowercase())
+ .reduce(|mut acc, f| {
+ let _ = "";
+ acc.push_str(&f);
+ acc
+ }).unwrap_or_default();
+ }
+
+ fn less_than_max_suggestion_highest_lines() {
+ let frames = Vec::new();
+ let iter = frames.iter();
+ let map = iter.map(|f: &String| f.to_lowercase());
+ map.reduce(|mut acc, f| {
+ acc.push_str(&f);
+ acc
+ }).unwrap_or_default();
+ }
+}
+
fn main() {}
}
}
+mod issue8239 {
+ fn more_than_max_suggestion_highest_lines_0() {
+ let frames = Vec::new();
+ frames
+ .iter()
+ .map(|f: &String| f.to_lowercase())
+ .reduce(|mut acc, f| {
+ acc.push_str(&f);
+ acc
+ })
+ .unwrap_or(String::new());
+ }
+
+ fn more_to_max_suggestion_highest_lines_1() {
+ let frames = Vec::new();
+ let iter = frames.iter();
+ iter.map(|f: &String| f.to_lowercase())
+ .reduce(|mut acc, f| {
+ let _ = "";
+ let _ = "";
+ acc.push_str(&f);
+ acc
+ })
+ .unwrap_or(String::new());
+ }
+
+ fn equal_to_max_suggestion_highest_lines() {
+ let frames = Vec::new();
+ let iter = frames.iter();
+ iter.map(|f: &String| f.to_lowercase())
+ .reduce(|mut acc, f| {
+ let _ = "";
+ acc.push_str(&f);
+ acc
+ })
+ .unwrap_or(String::new());
+ }
+
+ fn less_than_max_suggestion_highest_lines() {
+ let frames = Vec::new();
+ let iter = frames.iter();
+ let map = iter.map(|f: &String| f.to_lowercase());
+ map.reduce(|mut acc, f| {
+ acc.push_str(&f);
+ acc
+ })
+ .unwrap_or(String::new());
+ }
+}
+
fn main() {}
LL | None.unwrap_or( unsafe { ptr_to_ref(s) } );
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })`
-error: aborting due to 18 previous errors
+error: use of `unwrap_or` followed by a call to `new`
+ --> $DIR/or_fun_call.rs:189:14
+ |
+LL | .unwrap_or(String::new());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
+
+error: use of `unwrap_or` followed by a call to `new`
+ --> $DIR/or_fun_call.rs:202:14
+ |
+LL | .unwrap_or(String::new());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
+
+error: use of `unwrap_or` followed by a call to `new`
+ --> $DIR/or_fun_call.rs:208:9
+ |
+LL | / iter.map(|f: &String| f.to_lowercase())
+LL | | .reduce(|mut acc, f| {
+LL | | let _ = "";
+LL | | acc.push_str(&f);
+LL | | acc
+LL | | })
+LL | | .unwrap_or(String::new());
+ | |_____________________________________^
+ |
+help: try this
+ |
+LL ~ iter.map(|f: &String| f.to_lowercase())
+LL + .reduce(|mut acc, f| {
+LL + let _ = "";
+LL + acc.push_str(&f);
+LL + acc
+LL ~ }).unwrap_or_default();
+ |
+
+error: use of `unwrap_or` followed by a call to `new`
+ --> $DIR/or_fun_call.rs:221:9
+ |
+LL | / map.reduce(|mut acc, f| {
+LL | | acc.push_str(&f);
+LL | | acc
+LL | | })
+LL | | .unwrap_or(String::new());
+ | |_________________________________^
+ |
+help: try this
+ |
+LL ~ map.reduce(|mut acc, f| {
+LL + acc.push_str(&f);
+LL + acc
+LL ~ }).unwrap_or_default();
+ |
+
+error: aborting due to 22 previous errors
}
fn do_vec_mut(x: &mut Vec<i64>) {
- // no error here
//Nothing here
}
}
fn do_str_mut(x: &mut String) {
- // no error here
//Nothing here either
}
}
fn do_path_mut(x: &mut PathBuf) {
- // no error here
//Nothing here either
}
let e = x.clone();
let f = e.clone(); // OK
let g = x;
- let h = g.clone(); // Alas, we cannot reliably detect this without following data.
+ let h = g.clone();
let i = (e).clone();
x.clone()
}
}
}
+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)
type A = Vec<u8>;
fn aliased(a: &A) {}
-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
|
LL | fn do_vec(x: &Vec<i64>) {
|
= 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
- --> $DIR/ptr_arg.rs:16:14
+ --> $DIR/ptr_arg.rs:15:14
|
LL | fn do_str(x: &String) {
| ^^^^^^^ 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
- --> $DIR/ptr_arg.rs:25:15
+ --> $DIR/ptr_arg.rs:23:15
|
LL | fn do_path(x: &PathBuf) {
| ^^^^^^^^ help: change this to: `&Path`
-error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices
- --> $DIR/ptr_arg.rs:38:18
+error: writing `&mut PathBuf` instead of `&mut Path` involves a new object where a slice will do
+ --> $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>);
| ^^^^^^^^^ help: change this to: `&[i64]`
-error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices
- --> $DIR/ptr_arg.rs:51:14
+error: writing `&Vec` instead of `&[_]` involves a new object where a slice will do
+ --> $DIR/ptr_arg.rs:48:14
|
LL | fn cloned(x: &Vec<u8>) -> Vec<u8> {
| ^^^^^^^^
|
help: change this to
|
-LL | fn cloned(x: &[u8]) -> Vec<u8> {
- | ~~~~~
-help: change `x.clone()` to
- |
-LL | let e = x.to_owned();
- | ~~~~~~~~~~~~
-help: change `x.clone()` to
- |
-LL | x.to_owned()
- |
+LL ~ fn cloned(x: &[u8]) -> Vec<u8> {
+LL ~ let e = x.to_owned();
+LL | let f = e.clone(); // OK
+LL | let g = x;
+LL ~ let h = g.to_owned();
+LL | let i = (e).clone();
+ ...
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 {
| ^^^^^^^
|
help: change this to
|
-LL | fn str_cloned(x: &str) -> String {
- | ~~~~
-help: change `x.clone()` to
- |
-LL | let a = x.to_string();
- | ~~~~~~~~~~~~~
-help: change `x.clone()` to
- |
-LL | let b = x.to_string();
- | ~~~~~~~~~~~~~
-help: change `x.clone()` to
- |
-LL | x.to_string()
+LL ~ fn str_cloned(x: &str) -> String {
+LL ~ let a = x.to_owned();
+LL ~ let b = x.to_owned();
+LL | let c = b.clone();
+LL | let d = a.clone().clone().clone();
+LL ~ x.to_owned()
|
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 {
| ^^^^^^^^
|
help: change this to
|
-LL | fn path_cloned(x: &Path) -> PathBuf {
- | ~~~~~
-help: change `x.clone()` to
- |
-LL | let a = 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()
+LL ~ fn path_cloned(x: &Path) -> PathBuf {
+LL ~ let a = x.to_path_buf();
+LL ~ let b = x.to_path_buf();
+LL | let c = b.clone();
+LL | let d = a.clone().clone().clone();
+LL ~ x.to_path_buf()
|
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) {
| ^^^^^^^
|
help: change this to
|
-LL | fn false_positive_capacity(x: &Vec<u8>, y: &str) {
- | ~~~~
-help: change `y.clone()` to
+LL ~ fn false_positive_capacity(x: &Vec<u8>, y: &str) {
+LL | let a = x.capacity();
+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
- --> $DIR/ptr_arg.rs:90:25
+ --> $DIR/ptr_arg.rs:87:25
|
LL | fn test_cow_with_ref(c: &Cow<[i32]>) {}
| ^^^^^^^^^^^ help: change this to: `&[i32]`
-error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices
- --> $DIR/ptr_arg.rs:143:21
+error: writing `&Vec` instead of `&[_]` involves a new object where a slice will do
+ --> $DIR/ptr_arg.rs:140:21
|
LL | fn foo_vec(vec: &Vec<u8>) {
| ^^^^^^^^
|
help: change this to
|
-LL | fn foo_vec(vec: &[u8]) {
- | ~~~~~
-help: change `vec.clone()` to
- |
-LL | let _ = vec.to_owned().pop();
- | ~~~~~~~~~~~~~~
-help: change `vec.clone()` to
+LL ~ fn foo_vec(vec: &[u8]) {
+LL ~ let _ = vec.to_owned().pop();
+LL ~ let _ = vec.to_owned().clone();
|
-LL | let _ = vec.to_owned().clone();
- | ~~~~~~~~~~~~~~
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) {
| ^^^^^^^^
|
help: change this to
|
-LL | fn foo_path(path: &Path) {
- | ~~~~~
-help: change `path.clone()` to
+LL ~ fn foo_path(path: &Path) {
+LL ~ let _ = path.to_path_buf().pop();
+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
- --> $DIR/ptr_arg.rs:153:21
+ --> $DIR/ptr_arg.rs:150:21
|
LL | fn foo_str(str: &PathBuf) {
| ^^^^^^^^
|
help: change this to
|
-LL | fn foo_str(str: &Path) {
- | ~~~~~
-help: change `str.clone()` to
+LL ~ fn foo_str(str: &Path) {
+LL ~ let _ = str.to_path_buf().pop();
+LL ~ let _ = str.to_path_buf().clone();
|
-LL | let _ = str.to_path_buf().pop();
- | ~~~~~~~~~~~~~~~~~
-help: change `str.clone()` to
+
+error: writing `&mut Vec` instead of `&mut [_]` involves a new object where a slice will do
+ --> $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
None::<()>.is_none();
}
-#[allow(clippy::deref_addrof, dead_code)]
+#[allow(clippy::deref_addrof, dead_code, clippy::needless_borrow)]
fn issue7921() {
if (&None::<()>).is_none() {}
if (&None::<()>).is_none() {}
};
}
-#[allow(clippy::deref_addrof, dead_code)]
+#[allow(clippy::deref_addrof, dead_code, clippy::needless_borrow)]
fn issue7921() {
if let None = *(&None::<()>) {}
if let None = *&None::<()> {}
#![warn(clippy::match_result_ok)]
#![warn(clippy::disallowed_types)]
#![warn(clippy::disallowed_methods)]
+#![warn(clippy::needless_borrow)]
// uplifted lints
#![warn(invalid_value)]
#![warn(array_into_iter)]
#![warn(clippy::if_let_some_result)]
#![warn(clippy::disallowed_type)]
#![warn(clippy::disallowed_method)]
+#![warn(clippy::ref_in_deref)]
// uplifted lints
#![warn(clippy::invalid_ref)]
#![warn(clippy::into_iter_on_array)]
LL | #![warn(clippy::disallowed_method)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^ 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`
- --> $DIR/rename.rs:58:9
+ --> $DIR/rename.rs:59:9
|
LL | #![warn(clippy::invalid_ref)]
| ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value`
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)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter`
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)]
| ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels`
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)]
| ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds`
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)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr`
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)]
| ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics`
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)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints`
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)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering`
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)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums`
-error: aborting due to 32 previous errors
+error: aborting due to 33 previous errors
#![crate_type = "lib"]
+#![warn(clippy::return_self_not_must_use)]
#[derive(Clone)]
pub struct Bar;
error: missing `#[must_use]` attribute on a method returning `Self`
- --> $DIR/return_self_not_must_use.rs:7:5
+ --> $DIR/return_self_not_must_use.rs:8:5
|
LL | fn what(&self) -> Self;
| ^^^^^^^^^^^^^^^^^^^^^^^
= help: consider adding the `#[must_use]` attribute to the method or directly to the `Self` type
error: missing `#[must_use]` attribute on a method returning `Self`
- --> $DIR/return_self_not_must_use.rs:17:5
+ --> $DIR/return_self_not_must_use.rs:18:5
|
LL | / pub fn foo(&self) -> Self {
LL | | Self
= help: consider adding the `#[must_use]` attribute to the method or directly to the `Self` type
error: missing `#[must_use]` attribute on a method returning `Self`
- --> $DIR/return_self_not_must_use.rs:20:5
+ --> $DIR/return_self_not_must_use.rs:21:5
|
LL | / pub fn bar(self) -> Self {
LL | | self
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()
}
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)`
-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`
--> $DIR/search_is_some_fixable_none.rs:208:17
|
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))`
-error: aborting due to 44 previous errors
+error: aborting due to 43 previous errors
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()
}
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)`
-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`
--> $DIR/search_is_some_fixable_some.rs:207:26
|
LL | let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_some();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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
vec1.resize(10, 0);
}
-fn do_stuff(vec: &mut Vec<u8>) {}
+fn do_stuff(vec: &mut [u8]) {}
fn extend_vector_with_manipulations_between() {
let len = 300;
#![deny(clippy::trait_duplication_in_bounds)]
+use std::collections::BTreeMap;
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
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() {}
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)
| ^^^^^
= help: consider removing this trait bound
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)
| ^^^^^^^
= help: consider removing this trait bound
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;
| ^^^^^^^
= help: consider removing this trait bound
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;
| ^^^^^^^
= help: consider removing this trait bound
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;
| ^^^^^^^
= help: consider removing this trait bound
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;
| ^^^^^
= help: consider removing this trait bound
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;
| ^^^^^^^
|
= 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
+++ /dev/null
-// 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();
-}
+++ /dev/null
-// 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();
-}
+++ /dev/null
-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
-
foo_rslice(mrrrrrslice);
foo_rslice(mrrrrrslice);
}
- #[allow(unused_parens, clippy::double_parens)]
+ #[allow(unused_parens, clippy::double_parens, clippy::needless_borrow)]
foo_rrrrmr((&&&&MoreRef));
generic_not_ok(mrslice);
foo_rslice(mrrrrrslice.as_ref());
foo_rslice(mrrrrrslice);
}
- #[allow(unused_parens, clippy::double_parens)]
+ #[allow(unused_parens, clippy::double_parens, clippy::needless_borrow)]
foo_rrrrmr((&&&&MoreRef).as_ref());
generic_not_ok(mrslice);
let mut v = Vec::new();
// these should be fine
- write!(&mut v, "Hello");
- writeln!(&mut v, "Hello");
+ write!(v, "Hello");
+ writeln!(v, "Hello");
let world = "world";
- writeln!(&mut v, "Hello {}", world);
- writeln!(&mut v, "Hello {world}", world = world);
- writeln!(&mut v, "3 in hex is {:X}", 3);
- writeln!(&mut v, "2 + 1 = {:.4}", 3);
- writeln!(&mut v, "2 + 1 = {:5.4}", 3);
- writeln!(&mut v, "Debug test {:?}", "hello, world");
- writeln!(&mut v, "{0:8} {1:>8}", "hello", "world");
- writeln!(&mut v, "{1:8} {0:>8}", "hello", "world");
- writeln!(&mut v, "{foo:8} {bar:>8}", foo = "hello", bar = "world");
- writeln!(&mut v, "{bar:8} {foo:>8}", foo = "hello", bar = "world");
- writeln!(&mut v, "{number:>width$}", number = 1, width = 6);
- writeln!(&mut v, "{number:>0width$}", number = 1, width = 6);
- writeln!(&mut v, "{} of {:b} people know binary, the other half doesn't", 1, 2);
- writeln!(&mut v, "10 / 4 is {}", 2.5);
- writeln!(&mut v, "2 + 1 = {}", 3);
+ writeln!(v, "Hello {}", world);
+ writeln!(v, "Hello {world}", world = world);
+ writeln!(v, "3 in hex is {:X}", 3);
+ writeln!(v, "2 + 1 = {:.4}", 3);
+ writeln!(v, "2 + 1 = {:5.4}", 3);
+ writeln!(v, "Debug test {:?}", "hello, world");
+ writeln!(v, "{0:8} {1:>8}", "hello", "world");
+ writeln!(v, "{1:8} {0:>8}", "hello", "world");
+ writeln!(v, "{foo:8} {bar:>8}", foo = "hello", bar = "world");
+ writeln!(v, "{bar:8} {foo:>8}", foo = "hello", bar = "world");
+ writeln!(v, "{number:>width$}", number = 1, width = 6);
+ writeln!(v, "{number:>0width$}", number = 1, width = 6);
+ writeln!(v, "{} of {:b} people know binary, the other half doesn't", 1, 2);
+ writeln!(v, "10 / 4 is {}", 2.5);
+ writeln!(v, "2 + 1 = {}", 3);
// these should throw warnings
- write!(&mut v, "Hello {}", "world");
- writeln!(&mut v, "Hello {} {}", world, "world");
- writeln!(&mut v, "Hello {}", "world");
+ write!(v, "Hello {}", "world");
+ writeln!(v, "Hello {} {}", world, "world");
+ writeln!(v, "Hello {}", "world");
// positional args don't change the fact
// that we're using a literal -- this should
// throw a warning
- writeln!(&mut v, "{0} {1}", "hello", "world");
- writeln!(&mut v, "{1} {0}", "hello", "world");
+ writeln!(v, "{0} {1}", "hello", "world");
+ writeln!(v, "{1} {0}", "hello", "world");
// named args shouldn't change anything either
- writeln!(&mut v, "{foo} {bar}", foo = "hello", bar = "world");
- writeln!(&mut v, "{bar} {foo}", foo = "hello", bar = "world");
+ writeln!(v, "{foo} {bar}", foo = "hello", bar = "world");
+ writeln!(v, "{bar} {foo}", foo = "hello", bar = "world");
}
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`
help: try this
|
-LL - write!(&mut v, "Hello {}", "world");
-LL + write!(&mut v, "Hello world");
+LL - write!(v, "Hello {}", "world");
+LL + write!(v, "Hello world");
|
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
|
-LL - writeln!(&mut v, "Hello {} {}", world, "world");
-LL + writeln!(&mut v, "Hello {} world", world);
+LL - writeln!(v, "Hello {} {}", world, "world");
+LL + writeln!(v, "Hello {} world", world);
|
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
|
-LL - writeln!(&mut v, "Hello {}", "world");
-LL + writeln!(&mut v, "Hello world");
+LL - writeln!(v, "Hello {}", "world");
+LL + writeln!(v, "Hello world");
|
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
|
-LL - writeln!(&mut v, "{0} {1}", "hello", "world");
-LL + writeln!(&mut v, "hello {1}", "world");
+LL - writeln!(v, "{0} {1}", "hello", "world");
+LL + writeln!(v, "hello {1}", "world");
|
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
|
-LL - writeln!(&mut v, "{0} {1}", "hello", "world");
-LL + writeln!(&mut v, "{0} world", "hello");
+LL - writeln!(v, "{0} {1}", "hello", "world");
+LL + writeln!(v, "{0} world", "hello");
|
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
|
-LL - writeln!(&mut v, "{1} {0}", "hello", "world");
-LL + writeln!(&mut v, "{1} hello", "world");
+LL - writeln!(v, "{1} {0}", "hello", "world");
+LL + writeln!(v, "{1} hello", "world");
|
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
|
-LL - writeln!(&mut v, "{1} {0}", "hello", "world");
-LL + writeln!(&mut v, "world {0}", "hello");
+LL - writeln!(v, "{1} {0}", "hello", "world");
+LL + writeln!(v, "world {0}", "hello");
|
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
|
-LL - writeln!(&mut v, "{foo} {bar}", foo = "hello", bar = "world");
-LL + writeln!(&mut v, "hello {bar}", bar = "world");
+LL - writeln!(v, "{foo} {bar}", foo = "hello", bar = "world");
+LL + writeln!(v, "hello {bar}", bar = "world");
|
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
|
-LL - writeln!(&mut v, "{foo} {bar}", foo = "hello", bar = "world");
-LL + writeln!(&mut v, "{foo} world", foo = "hello");
+LL - writeln!(v, "{foo} {bar}", foo = "hello", bar = "world");
+LL + writeln!(v, "{foo} world", foo = "hello");
|
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
|
-LL - writeln!(&mut v, "{bar} {foo}", foo = "hello", bar = "world");
-LL + writeln!(&mut v, "{bar} hello", bar = "world");
+LL - writeln!(v, "{bar} {foo}", foo = "hello", bar = "world");
+LL + writeln!(v, "{bar} hello", bar = "world");
|
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
|
-LL - writeln!(&mut v, "{bar} {foo}", foo = "hello", bar = "world");
-LL + writeln!(&mut v, "world {foo}", foo = "hello");
+LL - writeln!(v, "{bar} {foo}", foo = "hello", bar = "world");
+LL + writeln!(v, "world {foo}", foo = "hello");
|
error: aborting due to 11 previous errors
fn main() {
let mut v = Vec::new();
- writeln!(&mut v, "{}", "{hello}");
- writeln!(&mut v, r"{}", r"{hello}");
- writeln!(&mut v, "{}", '\'');
- writeln!(&mut v, "{}", '"');
- writeln!(&mut v, r"{}", '"'); // don't lint
- writeln!(&mut v, r"{}", '\'');
+ writeln!(v, "{}", "{hello}");
+ writeln!(v, r"{}", r"{hello}");
+ writeln!(v, "{}", '\'');
+ writeln!(v, "{}", '"');
+ writeln!(v, r"{}", '"'); // don't lint
+ writeln!(v, r"{}", '\'');
writeln!(
- &mut v,
+ v,
"some {}",
"hello \
world!"
);
writeln!(
- &mut v,
+ v,
"some {}\
{} \\ {}",
"1", "2", "3",
error: literal with an empty format string
- --> $DIR/write_literal_2.rs:9:28
+ --> $DIR/write_literal_2.rs:9:23
|
-LL | writeln!(&mut v, "{}", "{hello}");
- | ^^^^^^^^^
+LL | writeln!(v, "{}", "{hello}");
+ | ^^^^^^^^^
|
= note: `-D clippy::write-literal` implied by `-D warnings`
help: try this
|
-LL - writeln!(&mut v, "{}", "{hello}");
-LL + writeln!(&mut v, "{{hello}}");
+LL - writeln!(v, "{}", "{hello}");
+LL + writeln!(v, "{{hello}}");
|
error: literal with an empty format string
- --> $DIR/write_literal_2.rs:10:29
+ --> $DIR/write_literal_2.rs:10:24
|
-LL | writeln!(&mut v, r"{}", r"{hello}");
- | ^^^^^^^^^^
+LL | writeln!(v, r"{}", r"{hello}");
+ | ^^^^^^^^^^
|
help: try this
|
-LL - writeln!(&mut v, r"{}", r"{hello}");
-LL + writeln!(&mut v, r"{{hello}}");
+LL - writeln!(v, r"{}", r"{hello}");
+LL + writeln!(v, r"{{hello}}");
|
error: literal with an empty format string
- --> $DIR/write_literal_2.rs:11:28
+ --> $DIR/write_literal_2.rs:11:23
|
-LL | writeln!(&mut v, "{}", '/'');
- | ^^^^
+LL | writeln!(v, "{}", '/'');
+ | ^^^^
|
help: try this
|
-LL - writeln!(&mut v, "{}", '/'');
-LL + writeln!(&mut v, "'");
+LL - writeln!(v, "{}", '/'');
+LL + writeln!(v, "'");
|
error: literal with an empty format string
- --> $DIR/write_literal_2.rs:12:28
+ --> $DIR/write_literal_2.rs:12:23
|
-LL | writeln!(&mut v, "{}", '"');
- | ^^^
+LL | writeln!(v, "{}", '"');
+ | ^^^
|
help: try this
|
-LL - writeln!(&mut v, "{}", '"');
-LL + writeln!(&mut v, "/"");
+LL - writeln!(v, "{}", '"');
+LL + writeln!(v, "/"");
|
error: literal with an empty format string
- --> $DIR/write_literal_2.rs:14:29
+ --> $DIR/write_literal_2.rs:14:24
|
-LL | writeln!(&mut v, r"{}", '/'');
- | ^^^^
+LL | writeln!(v, r"{}", '/'');
+ | ^^^^
|
help: try this
|
-LL - writeln!(&mut v, r"{}", '/'');
-LL + writeln!(&mut v, r"'");
+LL - writeln!(v, r"{}", '/'');
+LL + writeln!(v, r"'");
|
error: literal with an empty format string
let mut v = Vec::new();
// These should fail
- write!(&mut v, "Hello\n");
- write!(&mut v, "Hello {}\n", "world");
- write!(&mut v, "Hello {} {}\n", "world", "#2");
- write!(&mut v, "{}\n", 1265);
- write!(&mut v, "\n");
+ write!(v, "Hello\n");
+ write!(v, "Hello {}\n", "world");
+ write!(v, "Hello {} {}\n", "world", "#2");
+ write!(v, "{}\n", 1265);
+ write!(v, "\n");
// These should be fine
- write!(&mut v, "");
- write!(&mut v, "Hello");
- writeln!(&mut v, "Hello");
- writeln!(&mut v, "Hello\n");
- writeln!(&mut v, "Hello {}\n", "world");
- write!(&mut v, "Issue\n{}", 1265);
- write!(&mut v, "{}", 1265);
- write!(&mut v, "\n{}", 1275);
- write!(&mut v, "\n\n");
- write!(&mut v, "like eof\n\n");
- write!(&mut v, "Hello {} {}\n\n", "world", "#2");
- writeln!(&mut v, "\ndon't\nwarn\nfor\nmultiple\nnewlines\n"); // #3126
- writeln!(&mut v, "\nbla\n\n"); // #3126
+ write!(v, "");
+ write!(v, "Hello");
+ writeln!(v, "Hello");
+ writeln!(v, "Hello\n");
+ writeln!(v, "Hello {}\n", "world");
+ write!(v, "Issue\n{}", 1265);
+ write!(v, "{}", 1265);
+ write!(v, "\n{}", 1275);
+ write!(v, "\n\n");
+ write!(v, "like eof\n\n");
+ write!(v, "Hello {} {}\n\n", "world", "#2");
+ writeln!(v, "\ndon't\nwarn\nfor\nmultiple\nnewlines\n"); // #3126
+ writeln!(v, "\nbla\n\n"); // #3126
// Escaping
- write!(&mut v, "\\n"); // #3514
- write!(&mut v, "\\\n"); // should fail
- write!(&mut v, "\\\\n");
+ write!(v, "\\n"); // #3514
+ write!(v, "\\\n"); // should fail
+ write!(v, "\\\\n");
// Raw strings
- write!(&mut v, r"\n"); // #3778
+ write!(v, r"\n"); // #3778
// Literal newlines should also fail
write!(
- &mut v,
+ v,
"
"
);
write!(
- &mut v,
+ v,
r"
"
);
// Don't warn on CRLF (#4208)
- write!(&mut v, "\r\n");
- write!(&mut v, "foo\r\n");
- write!(&mut v, "\\r\n"); //~ ERROR
- write!(&mut v, "foo\rbar\n");
+ write!(v, "\r\n");
+ write!(v, "foo\r\n");
+ write!(v, "\\r\n"); //~ ERROR
+ write!(v, "foo\rbar\n");
}
error: using `write!()` with a format string that ends in a single newline
--> $DIR/write_with_newline.rs:13:5
|
-LL | write!(&mut v, "Hello/n");
- | ^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | write!(v, "Hello/n");
+ | ^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::write-with-newline` implied by `-D warnings`
help: use `writeln!()` instead
|
-LL - write!(&mut v, "Hello/n");
-LL + writeln!(&mut v, "Hello");
+LL - write!(v, "Hello/n");
+LL + writeln!(v, "Hello");
|
error: using `write!()` with a format string that ends in a single newline
--> $DIR/write_with_newline.rs:14:5
|
-LL | write!(&mut v, "Hello {}/n", "world");
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | write!(v, "Hello {}/n", "world");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: use `writeln!()` instead
|
-LL - write!(&mut v, "Hello {}/n", "world");
-LL + writeln!(&mut v, "Hello {}", "world");
+LL - write!(v, "Hello {}/n", "world");
+LL + writeln!(v, "Hello {}", "world");
|
error: using `write!()` with a format string that ends in a single newline
--> $DIR/write_with_newline.rs:15:5
|
-LL | write!(&mut v, "Hello {} {}/n", "world", "#2");
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | write!(v, "Hello {} {}/n", "world", "#2");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: use `writeln!()` instead
|
-LL - write!(&mut v, "Hello {} {}/n", "world", "#2");
-LL + writeln!(&mut v, "Hello {} {}", "world", "#2");
+LL - write!(v, "Hello {} {}/n", "world", "#2");
+LL + writeln!(v, "Hello {} {}", "world", "#2");
|
error: using `write!()` with a format string that ends in a single newline
--> $DIR/write_with_newline.rs:16:5
|
-LL | write!(&mut v, "{}/n", 1265);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | write!(v, "{}/n", 1265);
+ | ^^^^^^^^^^^^^^^^^^^^^^^
|
help: use `writeln!()` instead
|
-LL - write!(&mut v, "{}/n", 1265);
-LL + writeln!(&mut v, "{}", 1265);
+LL - write!(v, "{}/n", 1265);
+LL + writeln!(v, "{}", 1265);
|
error: using `write!()` with a format string that ends in a single newline
--> $DIR/write_with_newline.rs:17:5
|
-LL | write!(&mut v, "/n");
- | ^^^^^^^^^^^^^^^^^^^^
+LL | write!(v, "/n");
+ | ^^^^^^^^^^^^^^^
|
help: use `writeln!()` instead
|
-LL - write!(&mut v, "/n");
-LL + writeln!(&mut v);
+LL - write!(v, "/n");
+LL + writeln!(v);
|
error: using `write!()` with a format string that ends in a single newline
--> $DIR/write_with_newline.rs:36:5
|
-LL | write!(&mut v, "//n"); // should fail
- | ^^^^^^^^^^^^^^^^^^^^^^
+LL | write!(v, "//n"); // should fail
+ | ^^^^^^^^^^^^^^^^^
|
help: use `writeln!()` instead
|
-LL - write!(&mut v, "//n"); // should fail
-LL + writeln!(&mut v, "/"); // should fail
+LL - write!(v, "//n"); // should fail
+LL + writeln!(v, "/"); // should fail
|
error: using `write!()` with a format string that ends in a single newline
--> $DIR/write_with_newline.rs:43:5
|
LL | / write!(
-LL | | &mut v,
+LL | | v,
LL | | "
LL | | "
LL | | );
help: use `writeln!()` instead
|
LL ~ writeln!(
-LL | &mut v,
+LL | v,
LL ~ ""
|
--> $DIR/write_with_newline.rs:48:5
|
LL | / write!(
-LL | | &mut v,
+LL | | v,
LL | | r"
LL | | "
LL | | );
help: use `writeln!()` instead
|
LL ~ writeln!(
-LL | &mut v,
+LL | v,
LL ~ r""
|
error: using `write!()` with a format string that ends in a single newline
--> $DIR/write_with_newline.rs:57:5
|
-LL | write!(&mut v, "/r/n"); //~ ERROR
- | ^^^^^^^^^^^^^^^^^^^^^^^
+LL | write!(v, "/r/n"); //~ ERROR
+ | ^^^^^^^^^^^^^^^^^^
|
help: use `writeln!()` instead
|
-LL - write!(&mut v, "/r/n"); //~ ERROR
-LL + writeln!(&mut v, "/r"); //~ ERROR
+LL - write!(v, "/r/n"); //~ ERROR
+LL + writeln!(v, "/r"); //~ ERROR
|
error: using `write!()` with a format string that ends in a single newline
--> $DIR/write_with_newline.rs:58:5
|
-LL | write!(&mut v, "foo/rbar/n");
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | write!(v, "foo/rbar/n");
+ | ^^^^^^^^^^^^^^^^^^^^^^^
|
help: use `writeln!()` instead
|
-LL - write!(&mut v, "foo/rbar/n");
-LL + writeln!(&mut v, "foo/rbar");
+LL - write!(v, "foo/rbar/n");
+LL + writeln!(v, "foo/rbar");
|
error: aborting due to 10 previous errors
let mut v = Vec::new();
// These should fail
- writeln!(&mut v);
+ writeln!(v);
let mut suggestion = Vec::new();
- writeln!(&mut suggestion);
+ writeln!(suggestion);
// These should be fine
- writeln!(&mut v);
- writeln!(&mut v, " ");
- write!(&mut v, "");
+ writeln!(v);
+ writeln!(v, " ");
+ write!(v, "");
}
let mut v = Vec::new();
// These should fail
- writeln!(&mut v, "");
+ writeln!(v, "");
let mut suggestion = Vec::new();
- writeln!(&mut suggestion, "");
+ writeln!(suggestion, "");
// These should be fine
- writeln!(&mut v);
- writeln!(&mut v, " ");
- write!(&mut v, "");
+ writeln!(v);
+ writeln!(v, " ");
+ write!(v, "");
}
-error: using `writeln!(&mut v, "")`
+error: using `writeln!(v, "")`
--> $DIR/writeln_empty_string.rs:11:5
|
-LL | writeln!(&mut v, "");
- | ^^^^^^^^^^^^^^^^^^^^ help: replace it with: `writeln!(&mut v)`
+LL | writeln!(v, "");
+ | ^^^^^^^^^^^^^^^ help: replace it with: `writeln!(v)`
|
= note: `-D clippy::writeln-empty-string` implied by `-D warnings`
-error: using `writeln!(&mut suggestion, "")`
+error: using `writeln!(suggestion, "")`
--> $DIR/writeln_empty_string.rs:14:5
|
-LL | writeln!(&mut suggestion, "");
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `writeln!(&mut suggestion)`
+LL | writeln!(suggestion, "");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `writeln!(suggestion)`
error: aborting due to 2 previous errors
<img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on Github"/>
</a>
- <script src="https://cdnjs.cloudflare.com/ajax/libs/markdown-it/7.0.0/markdown-it.min.js"></script>
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/markdown-it/12.3.2/markdown-it.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.5.0/highlight.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.5.0/languages/rust.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.12/angular.min.js"></script>
}
scrollToLintByURL($scope);
+
+ setTimeout(function () {
+ var el = document.getElementById('filter-input');
+ if (el) { el.focus() }
+ }, 0);
})
.error(function (data) {
$scope.error = data;