mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-10 23:24:24 +00:00
Auto merge of #11511 - Jarcho:split_borrow, r=llogiq
Split `needless_borrow` into two lints Splits off the case where the borrow is used as a generic argument to a function. I think the two cases are different enough to warrant a separate lint. The tests for the new lint have been reordered to group related parts together. Two warning have been dropped, one looked like it was testing the generic argument form, but it ends up triggering the auto-deref variant. The second was just a redundant test that didn't do anything interesting. An issue with cycle detection is also included. The old version was checking if a cycle was reachable from a block when it should have been checking if the block is part or a cycle. As a side note, I'm liking the style of just jamming all the tests into separate scopes in main. changelog: Split off `needless_borrows_for_generic_args` from `needless_borrow`
This commit is contained in:
commit
251a475b72
16 changed files with 1140 additions and 1070 deletions
|
@ -5171,6 +5171,7 @@ Released 2018-09-13
|
|||
[`needless_bool_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool_assign
|
||||
[`needless_borrow`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow
|
||||
[`needless_borrowed_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrowed_reference
|
||||
[`needless_borrows_for_generic_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrows_for_generic_args
|
||||
[`needless_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_collect
|
||||
[`needless_continue`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_continue
|
||||
[`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main
|
||||
|
|
|
@ -480,6 +480,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::needless_bool::NEEDLESS_BOOL_INFO,
|
||||
crate::needless_bool::NEEDLESS_BOOL_ASSIGN_INFO,
|
||||
crate::needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE_INFO,
|
||||
crate::needless_borrows_for_generic_args::NEEDLESS_BORROWS_FOR_GENERIC_ARGS_INFO,
|
||||
crate::needless_continue::NEEDLESS_CONTINUE_INFO,
|
||||
crate::needless_else::NEEDLESS_ELSE_INFO,
|
||||
crate::needless_for_each::NEEDLESS_FOR_EACH_INFO,
|
||||
|
|
|
@ -1,41 +1,24 @@
|
|||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
|
||||
use clippy_utils::mir::{enclosing_mir, expr_local, local_assignments, used_exactly_once, PossibleBorrowerMap};
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
|
||||
use clippy_utils::sugg::has_enclosing_paren;
|
||||
use clippy_utils::ty::{implements_trait, is_copy, peel_mid_ty_refs};
|
||||
use clippy_utils::ty::{implements_trait, peel_mid_ty_refs};
|
||||
use clippy_utils::{
|
||||
expr_use_ctxt, get_parent_expr, get_parent_node, is_lint_allowed, path_to_local, DefinedTy, ExprUseNode,
|
||||
};
|
||||
|
||||
use hir::def::DefKind;
|
||||
use hir::MatchSource;
|
||||
use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX};
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_data_structures::graph::iterate::{CycleDetector, TriColorDepthFirstSearch};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_hir::intravisit::{walk_ty, Visitor};
|
||||
use rustc_hir::{
|
||||
self as hir, BindingAnnotation, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, Mutability, Node, Pat, PatKind,
|
||||
Path, QPath, TyKind, UnOp,
|
||||
self as hir, BindingAnnotation, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, MatchSource, Mutability, Node,
|
||||
Pat, PatKind, Path, QPath, TyKind, UnOp,
|
||||
};
|
||||
use rustc_index::bit_set::BitSet;
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::mir::{Rvalue, StatementKind};
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
|
||||
use rustc_middle::ty::{
|
||||
self, ClauseKind, EarlyBinder, FnSig, GenericArg, GenericArgKind, List, ParamEnv, ParamTy, ProjectionPredicate, Ty,
|
||||
TyCtxt, TypeVisitableExt, TypeckResults,
|
||||
};
|
||||
use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt, TypeVisitableExt, TypeckResults};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_span::{Span, Symbol};
|
||||
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
|
||||
use rustc_trait_selection::traits::{Obligation, ObligationCause};
|
||||
use std::collections::VecDeque;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
@ -183,24 +166,6 @@ pub struct Dereferencing<'tcx> {
|
|||
///
|
||||
/// e.g. `m!(x) | Foo::Bar(ref x)`
|
||||
ref_locals: FxIndexMap<HirId, Option<RefPat>>,
|
||||
|
||||
/// Stack of (body owner, `PossibleBorrowerMap`) pairs. Used by
|
||||
/// `needless_borrow_impl_arg_position` to determine when a borrowed expression can instead
|
||||
/// be moved.
|
||||
possible_borrowers: Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
|
||||
|
||||
// `IntoIterator` for arrays requires Rust 1.53.
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
||||
impl<'tcx> Dereferencing<'tcx> {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Msrv) -> Self {
|
||||
Self {
|
||||
msrv,
|
||||
..Dereferencing::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -355,52 +320,6 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
|
|||
));
|
||||
},
|
||||
(Some(use_cx), RefOp::AddrOf(mutability)) => {
|
||||
let defined_ty = use_cx.node.defined_ty(cx);
|
||||
|
||||
// Check needless_borrow for generic arguments.
|
||||
if !use_cx.is_ty_unified
|
||||
&& let Some(DefinedTy::Mir(ty)) = defined_ty
|
||||
&& let ty::Param(ty) = *ty.value.skip_binder().kind()
|
||||
&& let Some((hir_id, fn_id, i)) = match use_cx.node {
|
||||
ExprUseNode::MethodArg(_, _, 0) => None,
|
||||
ExprUseNode::MethodArg(hir_id, None, i) => {
|
||||
typeck.type_dependent_def_id(hir_id).map(|id| (hir_id, id, i))
|
||||
},
|
||||
ExprUseNode::FnArg(&Expr { kind: ExprKind::Path(ref p), hir_id, .. }, i)
|
||||
if !path_has_args(p) => match typeck.qpath_res(p, hir_id) {
|
||||
Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) => {
|
||||
Some((hir_id, id, i))
|
||||
},
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
} && let count = needless_borrow_generic_arg_count(
|
||||
cx,
|
||||
&mut self.possible_borrowers,
|
||||
fn_id,
|
||||
typeck.node_args(hir_id),
|
||||
i,
|
||||
ty,
|
||||
expr,
|
||||
&self.msrv,
|
||||
) && count != 0
|
||||
{
|
||||
self.state = Some((
|
||||
State::DerefedBorrow(DerefedBorrow {
|
||||
count: count - 1,
|
||||
msg: "the borrowed expression implements the required traits",
|
||||
stability: TyCoercionStability::None,
|
||||
for_field_access: None,
|
||||
}),
|
||||
StateData {
|
||||
span: expr.span,
|
||||
hir_id: expr.hir_id,
|
||||
adjusted_ty: use_cx.adjustments.last().map_or(expr_ty, |a| a.target),
|
||||
},
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the number of times the borrow is auto-derefed.
|
||||
let mut iter = use_cx.adjustments.iter();
|
||||
let mut deref_count = 0usize;
|
||||
|
@ -419,7 +338,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
|
|||
};
|
||||
};
|
||||
|
||||
let stability = defined_ty.map_or(TyCoercionStability::None, |ty| {
|
||||
let stability = use_cx.node.defined_ty(cx).map_or(TyCoercionStability::None, |ty| {
|
||||
TyCoercionStability::for_defined_ty(cx, ty, use_cx.node.is_return())
|
||||
});
|
||||
let can_auto_borrow = match use_cx.node {
|
||||
|
@ -700,12 +619,6 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
|
|||
}
|
||||
|
||||
fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
|
||||
if self.possible_borrowers.last().map_or(false, |&(local_def_id, _)| {
|
||||
local_def_id == cx.tcx.hir().body_owner_def_id(body.id())
|
||||
}) {
|
||||
self.possible_borrowers.pop();
|
||||
}
|
||||
|
||||
if Some(body.id()) == self.current_body {
|
||||
for pat in self.ref_locals.drain(..).filter_map(|(_, x)| x) {
|
||||
let replacements = pat.replacements;
|
||||
|
@ -729,8 +642,6 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
|
|||
self.current_body = None;
|
||||
}
|
||||
}
|
||||
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
|
||||
fn try_parse_ref_op<'tcx>(
|
||||
|
@ -788,13 +699,6 @@ fn deref_method_same_type<'tcx>(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
fn path_has_args(p: &QPath<'_>) -> bool {
|
||||
match *p {
|
||||
QPath::Resolved(_, Path { segments: [.., s], .. }) | QPath::TypeRelative(_, s) => s.args.is_some(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn in_postfix_position<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
|
||||
if let Some(parent) = get_parent_expr(cx, e)
|
||||
&& parent.span.ctxt() == e.span.ctxt()
|
||||
|
@ -981,274 +885,6 @@ fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool {
|
|||
v.0
|
||||
}
|
||||
|
||||
/// Checks for the number of borrow expressions which can be removed from the given expression
|
||||
/// where the expression is used as an argument to a function expecting a generic type.
|
||||
///
|
||||
/// The following constraints will be checked:
|
||||
/// * The borrowed expression meets all the generic type's constraints.
|
||||
/// * The generic type appears only once in the functions signature.
|
||||
/// * The borrowed value will not be moved if it is used later in the function.
|
||||
#[expect(clippy::too_many_arguments)]
|
||||
fn needless_borrow_generic_arg_count<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
|
||||
fn_id: DefId,
|
||||
callee_args: &'tcx List<GenericArg<'tcx>>,
|
||||
arg_index: usize,
|
||||
param_ty: ParamTy,
|
||||
mut expr: &Expr<'tcx>,
|
||||
msrv: &Msrv,
|
||||
) -> usize {
|
||||
let destruct_trait_def_id = cx.tcx.lang_items().destruct_trait();
|
||||
let sized_trait_def_id = cx.tcx.lang_items().sized_trait();
|
||||
|
||||
let fn_sig = cx.tcx.fn_sig(fn_id).instantiate_identity().skip_binder();
|
||||
let predicates = cx.tcx.param_env(fn_id).caller_bounds();
|
||||
let projection_predicates = predicates
|
||||
.iter()
|
||||
.filter_map(|predicate| {
|
||||
if let ClauseKind::Projection(projection_predicate) = predicate.kind().skip_binder() {
|
||||
Some(projection_predicate)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut trait_with_ref_mut_self_method = false;
|
||||
|
||||
// If no traits were found, or only the `Destruct`, `Sized`, or `Any` traits were found, return.
|
||||
if predicates
|
||||
.iter()
|
||||
.filter_map(|predicate| {
|
||||
if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder()
|
||||
&& trait_predicate.trait_ref.self_ty() == param_ty.to_ty(cx.tcx)
|
||||
{
|
||||
Some(trait_predicate.trait_ref.def_id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.inspect(|trait_def_id| {
|
||||
trait_with_ref_mut_self_method |= has_ref_mut_self_method(cx, *trait_def_id);
|
||||
})
|
||||
.all(|trait_def_id| {
|
||||
Some(trait_def_id) == destruct_trait_def_id
|
||||
|| Some(trait_def_id) == sized_trait_def_id
|
||||
|| cx.tcx.is_diagnostic_item(sym::Any, trait_def_id)
|
||||
})
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// See:
|
||||
// - https://github.com/rust-lang/rust-clippy/pull/9674#issuecomment-1289294201
|
||||
// - https://github.com/rust-lang/rust-clippy/pull/9674#issuecomment-1292225232
|
||||
if projection_predicates
|
||||
.iter()
|
||||
.any(|projection_predicate| is_mixed_projection_predicate(cx, fn_id, projection_predicate))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// `args_with_referent_ty` can be constructed outside of `check_referent` because the same
|
||||
// elements are modified each time `check_referent` is called.
|
||||
let mut args_with_referent_ty = callee_args.to_vec();
|
||||
|
||||
let mut check_reference_and_referent = |reference, referent| {
|
||||
let referent_ty = cx.typeck_results().expr_ty(referent);
|
||||
|
||||
if !is_copy(cx, referent_ty)
|
||||
&& (referent_ty.has_significant_drop(cx.tcx, cx.param_env)
|
||||
|| !referent_used_exactly_once(cx, possible_borrowers, reference))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321
|
||||
if trait_with_ref_mut_self_method && !matches!(referent_ty.kind(), ty::Ref(_, _, Mutability::Mut)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if !replace_types(
|
||||
cx,
|
||||
param_ty,
|
||||
referent_ty,
|
||||
fn_sig,
|
||||
arg_index,
|
||||
&projection_predicates,
|
||||
&mut args_with_referent_ty,
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
predicates.iter().all(|predicate| {
|
||||
if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder()
|
||||
&& cx.tcx.is_diagnostic_item(sym::IntoIterator, trait_predicate.trait_ref.def_id)
|
||||
&& let ty::Param(param_ty) = trait_predicate.self_ty().kind()
|
||||
&& let GenericArgKind::Type(ty) = args_with_referent_ty[param_ty.index as usize].unpack()
|
||||
&& ty.is_array()
|
||||
&& !msrv.meets(msrvs::ARRAY_INTO_ITERATOR)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
let predicate = EarlyBinder::bind(predicate).instantiate(cx.tcx, &args_with_referent_ty);
|
||||
let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate);
|
||||
let infcx = cx.tcx.infer_ctxt().build();
|
||||
infcx.predicate_must_hold_modulo_regions(&obligation)
|
||||
})
|
||||
};
|
||||
|
||||
let mut count = 0;
|
||||
while let ExprKind::AddrOf(_, _, referent) = expr.kind {
|
||||
if !check_reference_and_referent(expr, referent) {
|
||||
break;
|
||||
}
|
||||
expr = referent;
|
||||
count += 1;
|
||||
}
|
||||
count
|
||||
}
|
||||
|
||||
fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool {
|
||||
cx.tcx
|
||||
.associated_items(trait_def_id)
|
||||
.in_definition_order()
|
||||
.any(|assoc_item| {
|
||||
if assoc_item.fn_has_self_parameter {
|
||||
let self_ty = cx
|
||||
.tcx
|
||||
.fn_sig(assoc_item.def_id)
|
||||
.instantiate_identity()
|
||||
.skip_binder()
|
||||
.inputs()[0];
|
||||
matches!(self_ty.kind(), ty::Ref(_, _, Mutability::Mut))
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn is_mixed_projection_predicate<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
callee_def_id: DefId,
|
||||
projection_predicate: &ProjectionPredicate<'tcx>,
|
||||
) -> bool {
|
||||
let generics = cx.tcx.generics_of(callee_def_id);
|
||||
// The predicate requires the projected type to equal a type parameter from the parent context.
|
||||
if let Some(term_ty) = projection_predicate.term.ty()
|
||||
&& let ty::Param(term_param_ty) = term_ty.kind()
|
||||
&& (term_param_ty.index as usize) < generics.parent_count
|
||||
{
|
||||
// The inner-most self type is a type parameter from the current function.
|
||||
let mut projection_ty = projection_predicate.projection_ty;
|
||||
loop {
|
||||
match projection_ty.self_ty().kind() {
|
||||
ty::Alias(ty::Projection, inner_projection_ty) => {
|
||||
projection_ty = *inner_projection_ty;
|
||||
}
|
||||
ty::Param(param_ty) => {
|
||||
return (param_ty.index as usize) >= generics.parent_count;
|
||||
}
|
||||
_ => {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn referent_used_exactly_once<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
|
||||
reference: &Expr<'tcx>,
|
||||
) -> bool {
|
||||
if let Some(mir) = enclosing_mir(cx.tcx, reference.hir_id)
|
||||
&& let Some(local) = expr_local(cx.tcx, reference)
|
||||
&& let [location] = *local_assignments(mir, local).as_slice()
|
||||
&& let Some(statement) = mir.basic_blocks[location.block].statements.get(location.statement_index)
|
||||
&& let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) = statement.kind
|
||||
&& !place.is_indirect_first_projection()
|
||||
// Ensure not in a loop (https://github.com/rust-lang/rust-clippy/issues/9710)
|
||||
&& TriColorDepthFirstSearch::new(&mir.basic_blocks).run_from(location.block, &mut CycleDetector).is_none()
|
||||
{
|
||||
let body_owner_local_def_id = cx.tcx.hir().enclosing_body_owner(reference.hir_id);
|
||||
if possible_borrowers
|
||||
.last()
|
||||
.map_or(true, |&(local_def_id, _)| local_def_id != body_owner_local_def_id)
|
||||
{
|
||||
possible_borrowers.push((body_owner_local_def_id, PossibleBorrowerMap::new(cx, mir)));
|
||||
}
|
||||
let possible_borrower = &mut possible_borrowers.last_mut().unwrap().1;
|
||||
// If `only_borrowers` were used here, the `copyable_iterator::warn` test would fail. The reason is
|
||||
// that `PossibleBorrowerVisitor::visit_terminator` considers `place.local` a possible borrower of
|
||||
// itself. See the comment in that method for an explanation as to why.
|
||||
possible_borrower.bounded_borrowers(&[local], &[local, place.local], place.local, location)
|
||||
&& used_exactly_once(mir, place.local).unwrap_or(false)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// Iteratively replaces `param_ty` with `new_ty` in `args`, and similarly for each resulting
|
||||
// projected type that is a type parameter. Returns `false` if replacing the types would have an
|
||||
// effect on the function signature beyond substituting `new_ty` for `param_ty`.
|
||||
// See: https://github.com/rust-lang/rust-clippy/pull/9136#discussion_r927212757
|
||||
fn replace_types<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
param_ty: ParamTy,
|
||||
new_ty: Ty<'tcx>,
|
||||
fn_sig: FnSig<'tcx>,
|
||||
arg_index: usize,
|
||||
projection_predicates: &[ProjectionPredicate<'tcx>],
|
||||
args: &mut [ty::GenericArg<'tcx>],
|
||||
) -> bool {
|
||||
let mut replaced = BitSet::new_empty(args.len());
|
||||
|
||||
let mut deque = VecDeque::with_capacity(args.len());
|
||||
deque.push_back((param_ty, new_ty));
|
||||
|
||||
while let Some((param_ty, new_ty)) = deque.pop_front() {
|
||||
// If `replaced.is_empty()`, then `param_ty` and `new_ty` are those initially passed in.
|
||||
if !fn_sig
|
||||
.inputs_and_output
|
||||
.iter()
|
||||
.enumerate()
|
||||
.all(|(i, ty)| (replaced.is_empty() && i == arg_index) || !ty.contains(param_ty.to_ty(cx.tcx)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
args[param_ty.index as usize] = ty::GenericArg::from(new_ty);
|
||||
|
||||
// The `replaced.insert(...)` check provides some protection against infinite loops.
|
||||
if replaced.insert(param_ty.index) {
|
||||
for projection_predicate in projection_predicates {
|
||||
if projection_predicate.projection_ty.self_ty() == param_ty.to_ty(cx.tcx)
|
||||
&& let Some(term_ty) = projection_predicate.term.ty()
|
||||
&& let ty::Param(term_param_ty) = term_ty.kind()
|
||||
{
|
||||
let projection = cx.tcx.mk_ty_from_kind(ty::Alias(
|
||||
ty::Projection,
|
||||
projection_predicate.projection_ty.with_self_ty(cx.tcx, new_ty),
|
||||
));
|
||||
|
||||
if let Ok(projected_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, projection)
|
||||
&& args[term_param_ty.index as usize] != ty::GenericArg::from(projected_ty)
|
||||
{
|
||||
deque.push_back((*term_param_ty, projected_ty));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
fn ty_contains_field(ty: Ty<'_>, name: Symbol) -> bool {
|
||||
if let ty::Adt(adt, _) = *ty.kind() {
|
||||
adt.is_struct() && adt.all_fields().any(|f| f.name == name)
|
||||
|
|
|
@ -228,6 +228,7 @@ mod mutex_atomic;
|
|||
mod needless_arbitrary_self_type;
|
||||
mod needless_bool;
|
||||
mod needless_borrowed_ref;
|
||||
mod needless_borrows_for_generic_args;
|
||||
mod needless_continue;
|
||||
mod needless_else;
|
||||
mod needless_for_each;
|
||||
|
@ -887,7 +888,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(move |_| Box::new(wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports)));
|
||||
store.register_late_pass(|_| Box::<redundant_pub_crate::RedundantPubCrate>::default());
|
||||
store.register_late_pass(|_| Box::new(unnamed_address::UnnamedAddress));
|
||||
store.register_late_pass(move |_| Box::new(dereference::Dereferencing::new(msrv())));
|
||||
store.register_late_pass(|_| Box::<dereference::Dereferencing<'_>>::default());
|
||||
store.register_late_pass(|_| Box::new(option_if_let_else::OptionIfLetElse));
|
||||
store.register_late_pass(|_| Box::new(future_not_send::FutureNotSend));
|
||||
let future_size_threshold = conf.future_size_threshold;
|
||||
|
@ -1111,6 +1112,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|_| Box::new(implied_bounds_in_impls::ImpliedBoundsInImpls));
|
||||
store.register_late_pass(|_| Box::new(missing_asserts_for_indexing::MissingAssertsForIndexing));
|
||||
store.register_late_pass(|_| Box::new(unnecessary_map_on_constructor::UnnecessaryMapOnConstructor));
|
||||
store.register_late_pass(move |_| {
|
||||
Box::new(needless_borrows_for_generic_args::NeedlessBorrowsForGenericArgs::new(
|
||||
msrv(),
|
||||
))
|
||||
});
|
||||
// add lints here, do not remove this comment, it's used in `new_lint`
|
||||
}
|
||||
|
||||
|
|
410
clippy_lints/src/needless_borrows_for_generic_args.rs
Normal file
410
clippy_lints/src/needless_borrows_for_generic_args.rs
Normal file
|
@ -0,0 +1,410 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::mir::{enclosing_mir, expr_local, local_assignments, used_exactly_once, PossibleBorrowerMap};
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
use clippy_utils::ty::is_copy;
|
||||
use clippy_utils::{expr_use_ctxt, peel_n_hir_expr_refs, DefinedTy, ExprUseNode};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_hir::{Body, Expr, ExprKind, Mutability, Path, QPath};
|
||||
use rustc_index::bit_set::BitSet;
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::mir::{Rvalue, StatementKind};
|
||||
use rustc_middle::ty::{
|
||||
self, ClauseKind, EarlyBinder, FnSig, GenericArg, GenericArgKind, List, ParamTy, ProjectionPredicate, Ty,
|
||||
};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
|
||||
use rustc_trait_selection::traits::{Obligation, ObligationCause};
|
||||
use std::collections::VecDeque;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for borrow operations (`&`) that used as a generic argument to a
|
||||
/// function when the borrowed value could be used.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Suggests that the receiver of the expression borrows
|
||||
/// the expression.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// The lint cannot tell when the implementation of a trait
|
||||
/// for `&T` and `T` do different things. Removing a borrow
|
||||
/// in such a case can change the semantics of the code.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// fn f(_: impl AsRef<str>) {}
|
||||
///
|
||||
/// let x = "foo";
|
||||
/// f(&x);
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// fn f(_: impl AsRef<str>) {}
|
||||
///
|
||||
/// let x = "foo";
|
||||
/// f(x);
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub NEEDLESS_BORROWS_FOR_GENERIC_ARGS,
|
||||
style,
|
||||
"taking a reference that is going to be automatically dereferenced"
|
||||
}
|
||||
|
||||
pub struct NeedlessBorrowsForGenericArgs<'tcx> {
|
||||
/// Stack of (body owner, `PossibleBorrowerMap`) pairs. Used by
|
||||
/// `needless_borrow_impl_arg_position` to determine when a borrowed expression can instead
|
||||
/// be moved.
|
||||
possible_borrowers: Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
|
||||
|
||||
// `IntoIterator` for arrays requires Rust 1.53.
|
||||
msrv: Msrv,
|
||||
}
|
||||
impl_lint_pass!(NeedlessBorrowsForGenericArgs<'_> => [NEEDLESS_BORROWS_FOR_GENERIC_ARGS]);
|
||||
|
||||
impl NeedlessBorrowsForGenericArgs<'_> {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Msrv) -> Self {
|
||||
Self {
|
||||
possible_borrowers: Vec::new(),
|
||||
msrv,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowsForGenericArgs<'tcx> {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if matches!(expr.kind, ExprKind::AddrOf(..))
|
||||
&& !expr.span.from_expansion()
|
||||
&& let Some(use_cx) = expr_use_ctxt(cx, expr)
|
||||
&& !use_cx.is_ty_unified
|
||||
&& let Some(DefinedTy::Mir(ty)) = use_cx.node.defined_ty(cx)
|
||||
&& let ty::Param(ty) = *ty.value.skip_binder().kind()
|
||||
&& let Some((hir_id, fn_id, i)) = match use_cx.node {
|
||||
ExprUseNode::MethodArg(_, _, 0) => None,
|
||||
ExprUseNode::MethodArg(hir_id, None, i) => {
|
||||
cx.typeck_results().type_dependent_def_id(hir_id).map(|id| (hir_id, id, i))
|
||||
},
|
||||
ExprUseNode::FnArg(&Expr { kind: ExprKind::Path(ref p), hir_id, .. }, i)
|
||||
if !path_has_args(p) => match cx.typeck_results().qpath_res(p, hir_id) {
|
||||
Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) => {
|
||||
Some((hir_id, id, i))
|
||||
},
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
} && let count = needless_borrow_count(
|
||||
cx,
|
||||
&mut self.possible_borrowers,
|
||||
fn_id,
|
||||
cx.typeck_results().node_args(hir_id),
|
||||
i,
|
||||
ty,
|
||||
expr,
|
||||
&self.msrv,
|
||||
) && count != 0
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
NEEDLESS_BORROWS_FOR_GENERIC_ARGS,
|
||||
expr.span,
|
||||
"the borrowed expression implements the required traits",
|
||||
|diag| {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let snip_span = peel_n_hir_expr_refs(expr, count).0.span;
|
||||
let snip = snippet_with_context(cx, snip_span, expr.span.ctxt(), "..", &mut app).0;
|
||||
diag.span_suggestion(expr.span, "change this to", snip.into_owned(), app);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
|
||||
if self.possible_borrowers.last().map_or(false, |&(local_def_id, _)| {
|
||||
local_def_id == cx.tcx.hir().body_owner_def_id(body.id())
|
||||
}) {
|
||||
self.possible_borrowers.pop();
|
||||
}
|
||||
}
|
||||
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
|
||||
fn path_has_args(p: &QPath<'_>) -> bool {
|
||||
match *p {
|
||||
QPath::Resolved(_, Path { segments: [.., s], .. }) | QPath::TypeRelative(_, s) => s.args.is_some(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks for the number of borrow expressions which can be removed from the given expression
|
||||
/// where the expression is used as an argument to a function expecting a generic type.
|
||||
///
|
||||
/// The following constraints will be checked:
|
||||
/// * The borrowed expression meets all the generic type's constraints.
|
||||
/// * The generic type appears only once in the functions signature.
|
||||
/// * The borrowed value will not be moved if it is used later in the function.
|
||||
#[expect(clippy::too_many_arguments)]
|
||||
fn needless_borrow_count<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
|
||||
fn_id: DefId,
|
||||
callee_args: &'tcx List<GenericArg<'tcx>>,
|
||||
arg_index: usize,
|
||||
param_ty: ParamTy,
|
||||
mut expr: &Expr<'tcx>,
|
||||
msrv: &Msrv,
|
||||
) -> usize {
|
||||
let destruct_trait_def_id = cx.tcx.lang_items().destruct_trait();
|
||||
let sized_trait_def_id = cx.tcx.lang_items().sized_trait();
|
||||
|
||||
let fn_sig = cx.tcx.fn_sig(fn_id).instantiate_identity().skip_binder();
|
||||
let predicates = cx.tcx.param_env(fn_id).caller_bounds();
|
||||
let projection_predicates = predicates
|
||||
.iter()
|
||||
.filter_map(|predicate| {
|
||||
if let ClauseKind::Projection(projection_predicate) = predicate.kind().skip_binder() {
|
||||
Some(projection_predicate)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut trait_with_ref_mut_self_method = false;
|
||||
|
||||
// If no traits were found, or only the `Destruct`, `Sized`, or `Any` traits were found, return.
|
||||
if predicates
|
||||
.iter()
|
||||
.filter_map(|predicate| {
|
||||
if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder()
|
||||
&& trait_predicate.trait_ref.self_ty() == param_ty.to_ty(cx.tcx)
|
||||
{
|
||||
Some(trait_predicate.trait_ref.def_id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.inspect(|trait_def_id| {
|
||||
trait_with_ref_mut_self_method |= has_ref_mut_self_method(cx, *trait_def_id);
|
||||
})
|
||||
.all(|trait_def_id| {
|
||||
Some(trait_def_id) == destruct_trait_def_id
|
||||
|| Some(trait_def_id) == sized_trait_def_id
|
||||
|| cx.tcx.is_diagnostic_item(sym::Any, trait_def_id)
|
||||
})
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// See:
|
||||
// - https://github.com/rust-lang/rust-clippy/pull/9674#issuecomment-1289294201
|
||||
// - https://github.com/rust-lang/rust-clippy/pull/9674#issuecomment-1292225232
|
||||
if projection_predicates
|
||||
.iter()
|
||||
.any(|projection_predicate| is_mixed_projection_predicate(cx, fn_id, projection_predicate))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// `args_with_referent_ty` can be constructed outside of `check_referent` because the same
|
||||
// elements are modified each time `check_referent` is called.
|
||||
let mut args_with_referent_ty = callee_args.to_vec();
|
||||
|
||||
let mut check_reference_and_referent = |reference, referent| {
|
||||
let referent_ty = cx.typeck_results().expr_ty(referent);
|
||||
|
||||
if !is_copy(cx, referent_ty)
|
||||
&& (referent_ty.has_significant_drop(cx.tcx, cx.param_env)
|
||||
|| !referent_used_exactly_once(cx, possible_borrowers, reference))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321
|
||||
if trait_with_ref_mut_self_method && !matches!(referent_ty.kind(), ty::Ref(_, _, Mutability::Mut)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if !replace_types(
|
||||
cx,
|
||||
param_ty,
|
||||
referent_ty,
|
||||
fn_sig,
|
||||
arg_index,
|
||||
&projection_predicates,
|
||||
&mut args_with_referent_ty,
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
predicates.iter().all(|predicate| {
|
||||
if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder()
|
||||
&& cx.tcx.is_diagnostic_item(sym::IntoIterator, trait_predicate.trait_ref.def_id)
|
||||
&& let ty::Param(param_ty) = trait_predicate.self_ty().kind()
|
||||
&& let GenericArgKind::Type(ty) = args_with_referent_ty[param_ty.index as usize].unpack()
|
||||
&& ty.is_array()
|
||||
&& !msrv.meets(msrvs::ARRAY_INTO_ITERATOR)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
let predicate = EarlyBinder::bind(predicate).instantiate(cx.tcx, &args_with_referent_ty);
|
||||
let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate);
|
||||
let infcx = cx.tcx.infer_ctxt().build();
|
||||
infcx.predicate_must_hold_modulo_regions(&obligation)
|
||||
})
|
||||
};
|
||||
|
||||
let mut count = 0;
|
||||
while let ExprKind::AddrOf(_, _, referent) = expr.kind {
|
||||
if !check_reference_and_referent(expr, referent) {
|
||||
break;
|
||||
}
|
||||
expr = referent;
|
||||
count += 1;
|
||||
}
|
||||
count
|
||||
}
|
||||
|
||||
fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool {
|
||||
cx.tcx
|
||||
.associated_items(trait_def_id)
|
||||
.in_definition_order()
|
||||
.any(|assoc_item| {
|
||||
if assoc_item.fn_has_self_parameter {
|
||||
let self_ty = cx
|
||||
.tcx
|
||||
.fn_sig(assoc_item.def_id)
|
||||
.instantiate_identity()
|
||||
.skip_binder()
|
||||
.inputs()[0];
|
||||
matches!(self_ty.kind(), ty::Ref(_, _, Mutability::Mut))
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn is_mixed_projection_predicate<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
callee_def_id: DefId,
|
||||
projection_predicate: &ProjectionPredicate<'tcx>,
|
||||
) -> bool {
|
||||
let generics = cx.tcx.generics_of(callee_def_id);
|
||||
// The predicate requires the projected type to equal a type parameter from the parent context.
|
||||
if let Some(term_ty) = projection_predicate.term.ty()
|
||||
&& let ty::Param(term_param_ty) = term_ty.kind()
|
||||
&& (term_param_ty.index as usize) < generics.parent_count
|
||||
{
|
||||
// The inner-most self type is a type parameter from the current function.
|
||||
let mut projection_ty = projection_predicate.projection_ty;
|
||||
loop {
|
||||
match projection_ty.self_ty().kind() {
|
||||
ty::Alias(ty::Projection, inner_projection_ty) => {
|
||||
projection_ty = *inner_projection_ty;
|
||||
}
|
||||
ty::Param(param_ty) => {
|
||||
return (param_ty.index as usize) >= generics.parent_count;
|
||||
}
|
||||
_ => {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn referent_used_exactly_once<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
|
||||
reference: &Expr<'tcx>,
|
||||
) -> bool {
|
||||
if let Some(mir) = enclosing_mir(cx.tcx, reference.hir_id)
|
||||
&& let Some(local) = expr_local(cx.tcx, reference)
|
||||
&& let [location] = *local_assignments(mir, local).as_slice()
|
||||
&& let block_data = &mir.basic_blocks[location.block]
|
||||
&& let Some(statement) = block_data.statements.get(location.statement_index)
|
||||
&& let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) = statement.kind
|
||||
&& !place.is_indirect_first_projection()
|
||||
{
|
||||
let body_owner_local_def_id = cx.tcx.hir().enclosing_body_owner(reference.hir_id);
|
||||
if possible_borrowers
|
||||
.last()
|
||||
.map_or(true, |&(local_def_id, _)| local_def_id != body_owner_local_def_id)
|
||||
{
|
||||
possible_borrowers.push((body_owner_local_def_id, PossibleBorrowerMap::new(cx, mir)));
|
||||
}
|
||||
let possible_borrower = &mut possible_borrowers.last_mut().unwrap().1;
|
||||
// If `only_borrowers` were used here, the `copyable_iterator::warn` test would fail. The reason is
|
||||
// that `PossibleBorrowerVisitor::visit_terminator` considers `place.local` a possible borrower of
|
||||
// itself. See the comment in that method for an explanation as to why.
|
||||
possible_borrower.bounded_borrowers(&[local], &[local, place.local], place.local, location)
|
||||
&& used_exactly_once(mir, place.local).unwrap_or(false)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// Iteratively replaces `param_ty` with `new_ty` in `args`, and similarly for each resulting
|
||||
// projected type that is a type parameter. Returns `false` if replacing the types would have an
|
||||
// effect on the function signature beyond substituting `new_ty` for `param_ty`.
|
||||
// See: https://github.com/rust-lang/rust-clippy/pull/9136#discussion_r927212757
|
||||
fn replace_types<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
param_ty: ParamTy,
|
||||
new_ty: Ty<'tcx>,
|
||||
fn_sig: FnSig<'tcx>,
|
||||
arg_index: usize,
|
||||
projection_predicates: &[ProjectionPredicate<'tcx>],
|
||||
args: &mut [ty::GenericArg<'tcx>],
|
||||
) -> bool {
|
||||
let mut replaced = BitSet::new_empty(args.len());
|
||||
|
||||
let mut deque = VecDeque::with_capacity(args.len());
|
||||
deque.push_back((param_ty, new_ty));
|
||||
|
||||
while let Some((param_ty, new_ty)) = deque.pop_front() {
|
||||
// If `replaced.is_empty()`, then `param_ty` and `new_ty` are those initially passed in.
|
||||
if !fn_sig
|
||||
.inputs_and_output
|
||||
.iter()
|
||||
.enumerate()
|
||||
.all(|(i, ty)| (replaced.is_empty() && i == arg_index) || !ty.contains(param_ty.to_ty(cx.tcx)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
args[param_ty.index as usize] = ty::GenericArg::from(new_ty);
|
||||
|
||||
// The `replaced.insert(...)` check provides some protection against infinite loops.
|
||||
if replaced.insert(param_ty.index) {
|
||||
for projection_predicate in projection_predicates {
|
||||
if projection_predicate.projection_ty.self_ty() == param_ty.to_ty(cx.tcx)
|
||||
&& let Some(term_ty) = projection_predicate.term.ty()
|
||||
&& let ty::Param(term_param_ty) = term_ty.kind()
|
||||
{
|
||||
let projection = cx.tcx.mk_ty_from_kind(ty::Alias(
|
||||
ty::Projection,
|
||||
projection_predicate.projection_ty.with_self_ty(cx.tcx, new_ty),
|
||||
));
|
||||
|
||||
if let Ok(projected_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, projection)
|
||||
&& args[term_param_ty.index as usize] != ty::GenericArg::from(projected_ty)
|
||||
{
|
||||
deque.push_back((*term_param_ty, projected_ty));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
use rustc_hir::{Expr, HirId};
|
||||
use rustc_index::bit_set::BitSet;
|
||||
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
|
||||
use rustc_middle::mir::{
|
||||
traversal, Body, InlineAsmOperand, Local, Location, Place, StatementKind, TerminatorKind, START_BLOCK,
|
||||
traversal, BasicBlock, Body, InlineAsmOperand, Local, Location, Place, StatementKind, TerminatorKind, START_BLOCK,
|
||||
};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
|
||||
|
@ -79,8 +80,32 @@ impl<'a, 'tcx> Visitor<'tcx> for V<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Checks if the block is part of a cycle
|
||||
pub fn block_in_cycle(body: &Body<'_>, block: BasicBlock) -> bool {
|
||||
let mut seen = BitSet::new_empty(body.basic_blocks.len());
|
||||
let mut to_visit = Vec::with_capacity(body.basic_blocks.len() / 2);
|
||||
|
||||
seen.insert(block);
|
||||
let mut next = block;
|
||||
loop {
|
||||
for succ in body.basic_blocks[next].terminator().successors() {
|
||||
if seen.insert(succ) {
|
||||
to_visit.push(succ);
|
||||
} else if succ == block {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(x) = to_visit.pop() {
|
||||
next = x;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Convenience wrapper around `visit_local_usage`.
|
||||
pub fn used_exactly_once(mir: &rustc_middle::mir::Body<'_>, local: rustc_middle::mir::Local) -> Option<bool> {
|
||||
pub fn used_exactly_once(mir: &Body<'_>, local: rustc_middle::mir::Local) -> Option<bool> {
|
||||
visit_local_usage(
|
||||
&[local],
|
||||
mir,
|
||||
|
@ -91,11 +116,14 @@ pub fn used_exactly_once(mir: &rustc_middle::mir::Body<'_>, local: rustc_middle:
|
|||
)
|
||||
.map(|mut vec| {
|
||||
let LocalUsage { local_use_locs, .. } = vec.remove(0);
|
||||
local_use_locs
|
||||
let mut locations = local_use_locs
|
||||
.into_iter()
|
||||
.filter(|location| !is_local_assignment(mir, local, *location))
|
||||
.count()
|
||||
== 1
|
||||
.filter(|&location| !is_local_assignment(mir, local, location));
|
||||
if let Some(location) = locations.next() {
|
||||
locations.next().is_none() && !block_in_cycle(mir, location.block)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -131,21 +131,6 @@ fn main() {
|
|||
0
|
||||
}
|
||||
}
|
||||
|
||||
let _ = std::process::Command::new("ls").args(["-a", "-l"]).status().unwrap();
|
||||
let _ = std::path::Path::new(".").join(".");
|
||||
deref_target_is_x(X);
|
||||
multiple_constraints([[""]]);
|
||||
multiple_constraints_normalizes_to_same(X, X);
|
||||
let _ = Some("").unwrap_or("");
|
||||
let _ = std::fs::write("x", "".to_string());
|
||||
|
||||
only_sized(&""); // Don't lint. `Sized` is only bound
|
||||
let _ = std::any::Any::type_id(&""); // Don't lint. `Any` is only bound
|
||||
let _ = Box::new(&""); // Don't lint. Type parameter appears in return type
|
||||
ref_as_ref_path(&""); // Don't lint. Argument type is not a type parameter
|
||||
refs_only(&()); // Don't lint. `&T` implements trait, but `T` doesn't
|
||||
multiple_constraints_normalizes_to_different(&[[""]], &[""]); // Don't lint. Projected type appears in arguments
|
||||
}
|
||||
|
||||
#[allow(clippy::needless_borrowed_reference)]
|
||||
|
@ -201,103 +186,6 @@ mod issue9160 {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct X;
|
||||
|
||||
impl std::ops::Deref for X {
|
||||
type Target = X;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
fn deref_target_is_x<T>(_: T)
|
||||
where
|
||||
T: std::ops::Deref<Target = X>,
|
||||
{
|
||||
}
|
||||
|
||||
fn multiple_constraints<T, U, V, X, Y>(_: T)
|
||||
where
|
||||
T: IntoIterator<Item = U> + IntoIterator<Item = X>,
|
||||
U: IntoIterator<Item = V>,
|
||||
V: AsRef<str>,
|
||||
X: IntoIterator<Item = Y>,
|
||||
Y: AsRef<std::ffi::OsStr>,
|
||||
{
|
||||
}
|
||||
|
||||
fn multiple_constraints_normalizes_to_same<T, U, V>(_: T, _: V)
|
||||
where
|
||||
T: std::ops::Deref<Target = U>,
|
||||
U: std::ops::Deref<Target = V>,
|
||||
{
|
||||
}
|
||||
|
||||
fn only_sized<T>(_: T) {}
|
||||
|
||||
fn ref_as_ref_path<T: 'static>(_: &'static T)
|
||||
where
|
||||
&'static T: AsRef<std::path::Path>,
|
||||
{
|
||||
}
|
||||
|
||||
trait RefsOnly {
|
||||
type Referent;
|
||||
}
|
||||
|
||||
impl<T> RefsOnly for &T {
|
||||
type Referent = T;
|
||||
}
|
||||
|
||||
fn refs_only<T, U>(_: T)
|
||||
where
|
||||
T: RefsOnly<Referent = U>,
|
||||
{
|
||||
}
|
||||
|
||||
fn multiple_constraints_normalizes_to_different<T, U, V>(_: T, _: U)
|
||||
where
|
||||
T: IntoIterator<Item = U>,
|
||||
U: IntoIterator<Item = V>,
|
||||
V: AsRef<str>,
|
||||
{
|
||||
}
|
||||
|
||||
// https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321
|
||||
mod copyable_iterator {
|
||||
#[derive(Clone, Copy)]
|
||||
struct Iter;
|
||||
impl Iterator for Iter {
|
||||
type Item = ();
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
None
|
||||
}
|
||||
}
|
||||
fn takes_iter(_: impl Iterator) {}
|
||||
fn dont_warn(mut x: Iter) {
|
||||
takes_iter(&mut x);
|
||||
}
|
||||
#[allow(unused_mut)]
|
||||
fn warn(mut x: &mut Iter) {
|
||||
takes_iter(x)
|
||||
}
|
||||
}
|
||||
|
||||
#[clippy::msrv = "1.52.0"]
|
||||
mod under_msrv {
|
||||
fn foo() {
|
||||
let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[clippy::msrv = "1.53.0"]
|
||||
mod meets_msrv {
|
||||
fn foo() {
|
||||
let _ = std::process::Command::new("ls").args(["-a", "-l"]).status().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn issue9383() {
|
||||
// Should not lint because unions need explicit deref when accessing field
|
||||
use std::mem::ManuallyDrop;
|
||||
|
@ -326,184 +214,6 @@ fn issue9383() {
|
|||
}
|
||||
}
|
||||
|
||||
fn closure_test() {
|
||||
let env = "env".to_owned();
|
||||
let arg = "arg".to_owned();
|
||||
let f = |arg| {
|
||||
let loc = "loc".to_owned();
|
||||
let _ = std::fs::write("x", &env); // Don't lint. In environment
|
||||
let _ = std::fs::write("x", arg);
|
||||
let _ = std::fs::write("x", loc);
|
||||
};
|
||||
let _ = std::fs::write("x", &env); // Don't lint. Borrowed by `f`
|
||||
f(arg);
|
||||
}
|
||||
|
||||
mod significant_drop {
|
||||
#[derive(Debug)]
|
||||
struct X;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Y;
|
||||
|
||||
impl Drop for Y {
|
||||
fn drop(&mut self) {}
|
||||
}
|
||||
|
||||
fn foo(x: X, y: Y) {
|
||||
debug(x);
|
||||
debug(&y); // Don't lint. Has significant drop
|
||||
}
|
||||
|
||||
fn debug(_: impl std::fmt::Debug) {}
|
||||
}
|
||||
|
||||
mod used_exactly_once {
|
||||
fn foo(x: String) {
|
||||
use_x(x);
|
||||
}
|
||||
fn use_x(_: impl AsRef<str>) {}
|
||||
}
|
||||
|
||||
mod used_more_than_once {
|
||||
fn foo(x: String) {
|
||||
use_x(&x);
|
||||
use_x_again(&x);
|
||||
}
|
||||
fn use_x(_: impl AsRef<str>) {}
|
||||
fn use_x_again(_: impl AsRef<str>) {}
|
||||
}
|
||||
|
||||
// https://github.com/rust-lang/rust-clippy/issues/9111#issuecomment-1277114280
|
||||
mod issue_9111 {
|
||||
struct A;
|
||||
|
||||
impl Extend<u8> for A {
|
||||
fn extend<T: IntoIterator<Item = u8>>(&mut self, _: T) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Extend<&'a u8> for A {
|
||||
fn extend<T: IntoIterator<Item = &'a u8>>(&mut self, _: T) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut a = A;
|
||||
a.extend(&[]); // vs a.extend([]);
|
||||
}
|
||||
}
|
||||
|
||||
mod issue_9710 {
|
||||
fn main() {
|
||||
let string = String::new();
|
||||
for _i in 0..10 {
|
||||
f(&string);
|
||||
}
|
||||
}
|
||||
|
||||
fn f<T: AsRef<str>>(_: T) {}
|
||||
}
|
||||
|
||||
mod issue_9739 {
|
||||
fn foo<D: std::fmt::Display>(_it: impl IntoIterator<Item = D>) {}
|
||||
|
||||
fn main() {
|
||||
foo(if std::env::var_os("HI").is_some() {
|
||||
&[0]
|
||||
} else {
|
||||
&[] as &[u32]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
mod issue_9739_method_variant {
|
||||
struct S;
|
||||
|
||||
impl S {
|
||||
fn foo<D: std::fmt::Display>(&self, _it: impl IntoIterator<Item = D>) {}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
S.foo(if std::env::var_os("HI").is_some() {
|
||||
&[0]
|
||||
} else {
|
||||
&[] as &[u32]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
mod issue_9782 {
|
||||
fn foo<T: AsRef<[u8]>>(t: T) {
|
||||
println!("{}", std::mem::size_of::<T>());
|
||||
let _t: &[u8] = t.as_ref();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let a: [u8; 100] = [0u8; 100];
|
||||
|
||||
// 100
|
||||
foo::<[u8; 100]>(a);
|
||||
foo(a);
|
||||
|
||||
// 16
|
||||
foo::<&[u8]>(&a);
|
||||
foo(a.as_slice());
|
||||
|
||||
// 8
|
||||
foo::<&[u8; 100]>(&a);
|
||||
foo(a);
|
||||
}
|
||||
}
|
||||
|
||||
mod issue_9782_type_relative_variant {
|
||||
struct S;
|
||||
|
||||
impl S {
|
||||
fn foo<T: AsRef<[u8]>>(t: T) {
|
||||
println!("{}", std::mem::size_of::<T>());
|
||||
let _t: &[u8] = t.as_ref();
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let a: [u8; 100] = [0u8; 100];
|
||||
|
||||
S::foo::<&[u8; 100]>(&a);
|
||||
}
|
||||
}
|
||||
|
||||
mod issue_9782_method_variant {
|
||||
struct S;
|
||||
|
||||
impl S {
|
||||
fn foo<T: AsRef<[u8]>>(&self, t: T) {
|
||||
println!("{}", std::mem::size_of::<T>());
|
||||
let _t: &[u8] = t.as_ref();
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let a: [u8; 100] = [0u8; 100];
|
||||
|
||||
S.foo::<&[u8; 100]>(&a);
|
||||
}
|
||||
}
|
||||
|
||||
mod issue_10535 {
|
||||
static SOME_STATIC: String = String::new();
|
||||
|
||||
static UNIT: () = compute(&SOME_STATIC);
|
||||
|
||||
pub const fn compute<T>(_: T)
|
||||
where
|
||||
T: Copy,
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
mod issue_10253 {
|
||||
struct S;
|
||||
trait X {
|
||||
|
|
|
@ -131,21 +131,6 @@ fn main() {
|
|||
0
|
||||
}
|
||||
}
|
||||
|
||||
let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
|
||||
let _ = std::path::Path::new(".").join(&&".");
|
||||
deref_target_is_x(&X);
|
||||
multiple_constraints(&[[""]]);
|
||||
multiple_constraints_normalizes_to_same(&X, X);
|
||||
let _ = Some("").unwrap_or(&"");
|
||||
let _ = std::fs::write("x", &"".to_string());
|
||||
|
||||
only_sized(&""); // Don't lint. `Sized` is only bound
|
||||
let _ = std::any::Any::type_id(&""); // Don't lint. `Any` is only bound
|
||||
let _ = Box::new(&""); // Don't lint. Type parameter appears in return type
|
||||
ref_as_ref_path(&""); // Don't lint. Argument type is not a type parameter
|
||||
refs_only(&()); // Don't lint. `&T` implements trait, but `T` doesn't
|
||||
multiple_constraints_normalizes_to_different(&[[""]], &[""]); // Don't lint. Projected type appears in arguments
|
||||
}
|
||||
|
||||
#[allow(clippy::needless_borrowed_reference)]
|
||||
|
@ -201,103 +186,6 @@ mod issue9160 {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct X;
|
||||
|
||||
impl std::ops::Deref for X {
|
||||
type Target = X;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
fn deref_target_is_x<T>(_: T)
|
||||
where
|
||||
T: std::ops::Deref<Target = X>,
|
||||
{
|
||||
}
|
||||
|
||||
fn multiple_constraints<T, U, V, X, Y>(_: T)
|
||||
where
|
||||
T: IntoIterator<Item = U> + IntoIterator<Item = X>,
|
||||
U: IntoIterator<Item = V>,
|
||||
V: AsRef<str>,
|
||||
X: IntoIterator<Item = Y>,
|
||||
Y: AsRef<std::ffi::OsStr>,
|
||||
{
|
||||
}
|
||||
|
||||
fn multiple_constraints_normalizes_to_same<T, U, V>(_: T, _: V)
|
||||
where
|
||||
T: std::ops::Deref<Target = U>,
|
||||
U: std::ops::Deref<Target = V>,
|
||||
{
|
||||
}
|
||||
|
||||
fn only_sized<T>(_: T) {}
|
||||
|
||||
fn ref_as_ref_path<T: 'static>(_: &'static T)
|
||||
where
|
||||
&'static T: AsRef<std::path::Path>,
|
||||
{
|
||||
}
|
||||
|
||||
trait RefsOnly {
|
||||
type Referent;
|
||||
}
|
||||
|
||||
impl<T> RefsOnly for &T {
|
||||
type Referent = T;
|
||||
}
|
||||
|
||||
fn refs_only<T, U>(_: T)
|
||||
where
|
||||
T: RefsOnly<Referent = U>,
|
||||
{
|
||||
}
|
||||
|
||||
fn multiple_constraints_normalizes_to_different<T, U, V>(_: T, _: U)
|
||||
where
|
||||
T: IntoIterator<Item = U>,
|
||||
U: IntoIterator<Item = V>,
|
||||
V: AsRef<str>,
|
||||
{
|
||||
}
|
||||
|
||||
// https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321
|
||||
mod copyable_iterator {
|
||||
#[derive(Clone, Copy)]
|
||||
struct Iter;
|
||||
impl Iterator for Iter {
|
||||
type Item = ();
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
None
|
||||
}
|
||||
}
|
||||
fn takes_iter(_: impl Iterator) {}
|
||||
fn dont_warn(mut x: Iter) {
|
||||
takes_iter(&mut x);
|
||||
}
|
||||
#[allow(unused_mut)]
|
||||
fn warn(mut x: &mut Iter) {
|
||||
takes_iter(&mut x)
|
||||
}
|
||||
}
|
||||
|
||||
#[clippy::msrv = "1.52.0"]
|
||||
mod under_msrv {
|
||||
fn foo() {
|
||||
let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[clippy::msrv = "1.53.0"]
|
||||
mod meets_msrv {
|
||||
fn foo() {
|
||||
let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn issue9383() {
|
||||
// Should not lint because unions need explicit deref when accessing field
|
||||
use std::mem::ManuallyDrop;
|
||||
|
@ -326,184 +214,6 @@ fn issue9383() {
|
|||
}
|
||||
}
|
||||
|
||||
fn closure_test() {
|
||||
let env = "env".to_owned();
|
||||
let arg = "arg".to_owned();
|
||||
let f = |arg| {
|
||||
let loc = "loc".to_owned();
|
||||
let _ = std::fs::write("x", &env); // Don't lint. In environment
|
||||
let _ = std::fs::write("x", &arg);
|
||||
let _ = std::fs::write("x", &loc);
|
||||
};
|
||||
let _ = std::fs::write("x", &env); // Don't lint. Borrowed by `f`
|
||||
f(arg);
|
||||
}
|
||||
|
||||
mod significant_drop {
|
||||
#[derive(Debug)]
|
||||
struct X;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Y;
|
||||
|
||||
impl Drop for Y {
|
||||
fn drop(&mut self) {}
|
||||
}
|
||||
|
||||
fn foo(x: X, y: Y) {
|
||||
debug(&x);
|
||||
debug(&y); // Don't lint. Has significant drop
|
||||
}
|
||||
|
||||
fn debug(_: impl std::fmt::Debug) {}
|
||||
}
|
||||
|
||||
mod used_exactly_once {
|
||||
fn foo(x: String) {
|
||||
use_x(&x);
|
||||
}
|
||||
fn use_x(_: impl AsRef<str>) {}
|
||||
}
|
||||
|
||||
mod used_more_than_once {
|
||||
fn foo(x: String) {
|
||||
use_x(&x);
|
||||
use_x_again(&x);
|
||||
}
|
||||
fn use_x(_: impl AsRef<str>) {}
|
||||
fn use_x_again(_: impl AsRef<str>) {}
|
||||
}
|
||||
|
||||
// https://github.com/rust-lang/rust-clippy/issues/9111#issuecomment-1277114280
|
||||
mod issue_9111 {
|
||||
struct A;
|
||||
|
||||
impl Extend<u8> for A {
|
||||
fn extend<T: IntoIterator<Item = u8>>(&mut self, _: T) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Extend<&'a u8> for A {
|
||||
fn extend<T: IntoIterator<Item = &'a u8>>(&mut self, _: T) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut a = A;
|
||||
a.extend(&[]); // vs a.extend([]);
|
||||
}
|
||||
}
|
||||
|
||||
mod issue_9710 {
|
||||
fn main() {
|
||||
let string = String::new();
|
||||
for _i in 0..10 {
|
||||
f(&string);
|
||||
}
|
||||
}
|
||||
|
||||
fn f<T: AsRef<str>>(_: T) {}
|
||||
}
|
||||
|
||||
mod issue_9739 {
|
||||
fn foo<D: std::fmt::Display>(_it: impl IntoIterator<Item = D>) {}
|
||||
|
||||
fn main() {
|
||||
foo(if std::env::var_os("HI").is_some() {
|
||||
&[0]
|
||||
} else {
|
||||
&[] as &[u32]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
mod issue_9739_method_variant {
|
||||
struct S;
|
||||
|
||||
impl S {
|
||||
fn foo<D: std::fmt::Display>(&self, _it: impl IntoIterator<Item = D>) {}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
S.foo(if std::env::var_os("HI").is_some() {
|
||||
&[0]
|
||||
} else {
|
||||
&[] as &[u32]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
mod issue_9782 {
|
||||
fn foo<T: AsRef<[u8]>>(t: T) {
|
||||
println!("{}", std::mem::size_of::<T>());
|
||||
let _t: &[u8] = t.as_ref();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let a: [u8; 100] = [0u8; 100];
|
||||
|
||||
// 100
|
||||
foo::<[u8; 100]>(a);
|
||||
foo(a);
|
||||
|
||||
// 16
|
||||
foo::<&[u8]>(&a);
|
||||
foo(a.as_slice());
|
||||
|
||||
// 8
|
||||
foo::<&[u8; 100]>(&a);
|
||||
foo(&a);
|
||||
}
|
||||
}
|
||||
|
||||
mod issue_9782_type_relative_variant {
|
||||
struct S;
|
||||
|
||||
impl S {
|
||||
fn foo<T: AsRef<[u8]>>(t: T) {
|
||||
println!("{}", std::mem::size_of::<T>());
|
||||
let _t: &[u8] = t.as_ref();
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let a: [u8; 100] = [0u8; 100];
|
||||
|
||||
S::foo::<&[u8; 100]>(&a);
|
||||
}
|
||||
}
|
||||
|
||||
mod issue_9782_method_variant {
|
||||
struct S;
|
||||
|
||||
impl S {
|
||||
fn foo<T: AsRef<[u8]>>(&self, t: T) {
|
||||
println!("{}", std::mem::size_of::<T>());
|
||||
let _t: &[u8] = t.as_ref();
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let a: [u8; 100] = [0u8; 100];
|
||||
|
||||
S.foo::<&[u8; 100]>(&a);
|
||||
}
|
||||
}
|
||||
|
||||
mod issue_10535 {
|
||||
static SOME_STATIC: String = String::new();
|
||||
|
||||
static UNIT: () = compute(&SOME_STATIC);
|
||||
|
||||
pub const fn compute<T>(_: T)
|
||||
where
|
||||
T: Copy,
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
mod issue_10253 {
|
||||
struct S;
|
||||
trait X {
|
||||
|
|
|
@ -121,101 +121,17 @@ error: this expression creates a reference which is immediately dereferenced by
|
|||
LL | (&&5).foo();
|
||||
| ^^^^^ help: change this to: `(&5)`
|
||||
|
||||
error: the borrowed expression implements the required traits
|
||||
--> $DIR/needless_borrow.rs:135:51
|
||||
|
|
||||
LL | let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
|
||||
| ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]`
|
||||
|
||||
error: the borrowed expression implements the required traits
|
||||
--> $DIR/needless_borrow.rs:136:44
|
||||
|
|
||||
LL | let _ = std::path::Path::new(".").join(&&".");
|
||||
| ^^^^^ help: change this to: `"."`
|
||||
|
||||
error: the borrowed expression implements the required traits
|
||||
--> $DIR/needless_borrow.rs:137:23
|
||||
|
|
||||
LL | deref_target_is_x(&X);
|
||||
| ^^ help: change this to: `X`
|
||||
|
||||
error: the borrowed expression implements the required traits
|
||||
--> $DIR/needless_borrow.rs:138:26
|
||||
|
|
||||
LL | multiple_constraints(&[[""]]);
|
||||
| ^^^^^^^ help: change this to: `[[""]]`
|
||||
|
||||
error: the borrowed expression implements the required traits
|
||||
--> $DIR/needless_borrow.rs:139:45
|
||||
|
|
||||
LL | multiple_constraints_normalizes_to_same(&X, X);
|
||||
| ^^ help: change this to: `X`
|
||||
|
||||
error: this expression creates a reference which is immediately dereferenced by the compiler
|
||||
--> $DIR/needless_borrow.rs:140:32
|
||||
|
|
||||
LL | let _ = Some("").unwrap_or(&"");
|
||||
| ^^^ help: change this to: `""`
|
||||
|
||||
error: the borrowed expression implements the required traits
|
||||
--> $DIR/needless_borrow.rs:141:33
|
||||
|
|
||||
LL | let _ = std::fs::write("x", &"".to_string());
|
||||
| ^^^^^^^^^^^^^^^ help: change this to: `"".to_string()`
|
||||
|
||||
error: this expression borrows a value the compiler would automatically borrow
|
||||
--> $DIR/needless_borrow.rs:190:13
|
||||
--> $DIR/needless_borrow.rs:175:13
|
||||
|
|
||||
LL | (&self.f)()
|
||||
| ^^^^^^^^^ help: change this to: `(self.f)`
|
||||
|
||||
error: this expression borrows a value the compiler would automatically borrow
|
||||
--> $DIR/needless_borrow.rs:199:13
|
||||
--> $DIR/needless_borrow.rs:184:13
|
||||
|
|
||||
LL | (&mut self.f)()
|
||||
| ^^^^^^^^^^^^^ help: change this to: `(self.f)`
|
||||
|
||||
error: the borrowed expression implements the required traits
|
||||
--> $DIR/needless_borrow.rs:283:20
|
||||
|
|
||||
LL | takes_iter(&mut x)
|
||||
| ^^^^^^ help: change this to: `x`
|
||||
|
||||
error: the borrowed expression implements the required traits
|
||||
--> $DIR/needless_borrow.rs:297:55
|
||||
|
|
||||
LL | let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
|
||||
| ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]`
|
||||
|
||||
error: the borrowed expression implements the required traits
|
||||
--> $DIR/needless_borrow.rs:335:37
|
||||
|
|
||||
LL | let _ = std::fs::write("x", &arg);
|
||||
| ^^^^ help: change this to: `arg`
|
||||
|
||||
error: the borrowed expression implements the required traits
|
||||
--> $DIR/needless_borrow.rs:336:37
|
||||
|
|
||||
LL | let _ = std::fs::write("x", &loc);
|
||||
| ^^^^ help: change this to: `loc`
|
||||
|
||||
error: the borrowed expression implements the required traits
|
||||
--> $DIR/needless_borrow.rs:354:15
|
||||
|
|
||||
LL | debug(&x);
|
||||
| ^^ help: change this to: `x`
|
||||
|
||||
error: the borrowed expression implements the required traits
|
||||
--> $DIR/needless_borrow.rs:363:15
|
||||
|
|
||||
LL | use_x(&x);
|
||||
| ^^ help: change this to: `x`
|
||||
|
||||
error: the borrowed expression implements the required traits
|
||||
--> $DIR/needless_borrow.rs:457:13
|
||||
|
|
||||
LL | foo(&a);
|
||||
| ^^ help: change this to: `a`
|
||||
|
||||
error: aborting due to 36 previous errors
|
||||
error: aborting due to 22 previous errors
|
||||
|
||||
|
|
287
tests/ui/needless_borrows_for_generic_args.fixed
Normal file
287
tests/ui/needless_borrows_for_generic_args.fixed
Normal file
|
@ -0,0 +1,287 @@
|
|||
#![warn(clippy::needless_borrows_for_generic_args)]
|
||||
#![allow(
|
||||
clippy::unnecessary_to_owned,
|
||||
clippy::unnecessary_literal_unwrap,
|
||||
clippy::needless_borrow
|
||||
)]
|
||||
|
||||
use core::ops::Deref;
|
||||
use std::any::Any;
|
||||
use std::ffi::OsStr;
|
||||
use std::fmt::{Debug, Display};
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
|
||||
fn main() {
|
||||
let _ = Command::new("ls").args(["-a", "-l"]).status().unwrap();
|
||||
let _ = Path::new(".").join(".");
|
||||
let _ = Any::type_id(&""); // Don't lint. `Any` is only bound
|
||||
let _ = Box::new(&""); // Don't lint. Type parameter appears in return type
|
||||
let _ = Some("").unwrap_or(&"");
|
||||
let _ = std::fs::write("x", "".to_string());
|
||||
|
||||
{
|
||||
#[derive(Clone, Copy)]
|
||||
struct X;
|
||||
|
||||
impl Deref for X {
|
||||
type Target = X;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
fn deref_target_is_x<T: Deref<Target = X>>(_: T) {}
|
||||
|
||||
deref_target_is_x(X);
|
||||
}
|
||||
{
|
||||
fn multiple_constraints<T, U, V, X, Y>(_: T)
|
||||
where
|
||||
T: IntoIterator<Item = U> + IntoIterator<Item = X>,
|
||||
U: IntoIterator<Item = V>,
|
||||
V: AsRef<str>,
|
||||
X: IntoIterator<Item = Y>,
|
||||
Y: AsRef<OsStr>,
|
||||
{
|
||||
}
|
||||
|
||||
multiple_constraints([[""]]);
|
||||
}
|
||||
{
|
||||
#[derive(Clone, Copy)]
|
||||
struct X;
|
||||
|
||||
impl Deref for X {
|
||||
type Target = X;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
fn multiple_constraints_normalizes_to_same<T, U, V>(_: T, _: V)
|
||||
where
|
||||
T: Deref<Target = U>,
|
||||
U: Deref<Target = V>,
|
||||
{
|
||||
}
|
||||
|
||||
multiple_constraints_normalizes_to_same(X, X);
|
||||
}
|
||||
{
|
||||
fn only_sized<T>(_: T) {}
|
||||
only_sized(&""); // Don't lint. `Sized` is only bound
|
||||
}
|
||||
{
|
||||
fn ref_as_ref_path<T: 'static>(_: &'static T)
|
||||
where
|
||||
&'static T: AsRef<Path>,
|
||||
{
|
||||
}
|
||||
|
||||
ref_as_ref_path(&""); // Don't lint. Argument type is not a type parameter
|
||||
}
|
||||
{
|
||||
trait RefsOnly {
|
||||
type Referent;
|
||||
}
|
||||
|
||||
impl<T> RefsOnly for &T {
|
||||
type Referent = T;
|
||||
}
|
||||
|
||||
fn refs_only<T, U>(_: T)
|
||||
where
|
||||
T: RefsOnly<Referent = U>,
|
||||
{
|
||||
}
|
||||
|
||||
refs_only(&()); // Don't lint. `&T` implements trait, but `T` doesn't
|
||||
}
|
||||
{
|
||||
fn multiple_constraints_normalizes_to_different<T, U, V>(_: T, _: U)
|
||||
where
|
||||
T: IntoIterator<Item = U>,
|
||||
U: IntoIterator<Item = V>,
|
||||
V: AsRef<str>,
|
||||
{
|
||||
}
|
||||
multiple_constraints_normalizes_to_different(&[[""]], &[""]); // Don't lint. Projected type appears in arguments
|
||||
}
|
||||
// https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321
|
||||
{
|
||||
#[derive(Clone, Copy)]
|
||||
struct Iter;
|
||||
impl Iterator for Iter {
|
||||
type Item = ();
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
None
|
||||
}
|
||||
}
|
||||
fn takes_iter(_: impl Iterator) {}
|
||||
fn dont_warn(mut x: Iter) {
|
||||
takes_iter(&mut x);
|
||||
}
|
||||
#[allow(unused_mut)]
|
||||
fn warn(mut x: &mut Iter) {
|
||||
takes_iter(x)
|
||||
}
|
||||
}
|
||||
#[clippy::msrv = "1.52.0"]
|
||||
{
|
||||
let _ = Command::new("ls").args(&["-a", "-l"]).status().unwrap();
|
||||
};
|
||||
#[clippy::msrv = "1.53.0"]
|
||||
{
|
||||
let _ = Command::new("ls").args(["-a", "-l"]).status().unwrap();
|
||||
};
|
||||
{
|
||||
let env = "env".to_owned();
|
||||
let arg = "arg".to_owned();
|
||||
let f = |arg| {
|
||||
let loc = "loc".to_owned();
|
||||
let _ = std::fs::write("x", &env); // Don't lint. In environment
|
||||
let _ = std::fs::write("x", arg);
|
||||
let _ = std::fs::write("x", loc);
|
||||
};
|
||||
let _ = std::fs::write("x", &env); // Don't lint. Borrowed by `f`
|
||||
f(arg);
|
||||
}
|
||||
{
|
||||
#[derive(Debug)]
|
||||
struct X;
|
||||
|
||||
impl Drop for X {
|
||||
fn drop(&mut self) {}
|
||||
}
|
||||
|
||||
fn f(_: impl Debug) {}
|
||||
|
||||
let x = X;
|
||||
f(&x); // Don't lint. Has significant drop
|
||||
}
|
||||
{
|
||||
fn f(_: impl AsRef<str>) {}
|
||||
|
||||
let x = String::new();
|
||||
f(x);
|
||||
}
|
||||
{
|
||||
fn f(_: impl AsRef<str>) {}
|
||||
fn f2(_: impl AsRef<str>) {}
|
||||
|
||||
let x = String::new();
|
||||
f(&x);
|
||||
f2(&x);
|
||||
}
|
||||
// https://github.com/rust-lang/rust-clippy/issues/9111#issuecomment-1277114280
|
||||
// issue 9111
|
||||
{
|
||||
struct A;
|
||||
|
||||
impl Extend<u8> for A {
|
||||
fn extend<T: IntoIterator<Item = u8>>(&mut self, _: T) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Extend<&'a u8> for A {
|
||||
fn extend<T: IntoIterator<Item = &'a u8>>(&mut self, _: T) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
let mut a = A;
|
||||
a.extend(&[]); // vs a.extend([]);
|
||||
}
|
||||
// issue 9710
|
||||
{
|
||||
fn f(_: impl AsRef<str>) {}
|
||||
|
||||
let x = String::new();
|
||||
for _ in 0..10 {
|
||||
f(&x);
|
||||
}
|
||||
}
|
||||
// issue 9739
|
||||
{
|
||||
fn foo<D: Display>(_it: impl IntoIterator<Item = D>) {}
|
||||
foo(if std::env::var_os("HI").is_some() {
|
||||
&[0]
|
||||
} else {
|
||||
&[] as &[u32]
|
||||
});
|
||||
}
|
||||
{
|
||||
struct S;
|
||||
|
||||
impl S {
|
||||
fn foo<D: Display>(&self, _it: impl IntoIterator<Item = D>) {}
|
||||
}
|
||||
|
||||
S.foo(if std::env::var_os("HI").is_some() {
|
||||
&[0]
|
||||
} else {
|
||||
&[] as &[u32]
|
||||
});
|
||||
}
|
||||
// issue 9782
|
||||
{
|
||||
fn foo<T: AsRef<[u8]>>(t: T) {
|
||||
println!("{}", std::mem::size_of::<T>());
|
||||
let _t: &[u8] = t.as_ref();
|
||||
}
|
||||
|
||||
let a: [u8; 100] = [0u8; 100];
|
||||
|
||||
// 100
|
||||
foo::<[u8; 100]>(a);
|
||||
foo(a);
|
||||
|
||||
// 16
|
||||
foo::<&[u8]>(&a);
|
||||
foo(a.as_slice());
|
||||
|
||||
// 8
|
||||
foo::<&[u8; 100]>(&a);
|
||||
foo(a);
|
||||
}
|
||||
{
|
||||
struct S;
|
||||
|
||||
impl S {
|
||||
fn foo<T: AsRef<[u8]>>(t: T) {
|
||||
println!("{}", std::mem::size_of::<T>());
|
||||
let _t: &[u8] = t.as_ref();
|
||||
}
|
||||
}
|
||||
|
||||
let a: [u8; 100] = [0u8; 100];
|
||||
S::foo::<&[u8; 100]>(&a);
|
||||
}
|
||||
{
|
||||
struct S;
|
||||
|
||||
impl S {
|
||||
fn foo<T: AsRef<[u8]>>(&self, t: T) {
|
||||
println!("{}", std::mem::size_of::<T>());
|
||||
let _t: &[u8] = t.as_ref();
|
||||
}
|
||||
}
|
||||
|
||||
let a: [u8; 100] = [0u8; 100];
|
||||
S.foo::<&[u8; 100]>(&a);
|
||||
}
|
||||
// issue 10535
|
||||
{
|
||||
static SOME_STATIC: String = String::new();
|
||||
|
||||
static UNIT: () = compute(&SOME_STATIC);
|
||||
|
||||
pub const fn compute<T>(_: T)
|
||||
where
|
||||
T: Copy,
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
287
tests/ui/needless_borrows_for_generic_args.rs
Normal file
287
tests/ui/needless_borrows_for_generic_args.rs
Normal file
|
@ -0,0 +1,287 @@
|
|||
#![warn(clippy::needless_borrows_for_generic_args)]
|
||||
#![allow(
|
||||
clippy::unnecessary_to_owned,
|
||||
clippy::unnecessary_literal_unwrap,
|
||||
clippy::needless_borrow
|
||||
)]
|
||||
|
||||
use core::ops::Deref;
|
||||
use std::any::Any;
|
||||
use std::ffi::OsStr;
|
||||
use std::fmt::{Debug, Display};
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
|
||||
fn main() {
|
||||
let _ = Command::new("ls").args(&["-a", "-l"]).status().unwrap();
|
||||
let _ = Path::new(".").join(&&".");
|
||||
let _ = Any::type_id(&""); // Don't lint. `Any` is only bound
|
||||
let _ = Box::new(&""); // Don't lint. Type parameter appears in return type
|
||||
let _ = Some("").unwrap_or(&"");
|
||||
let _ = std::fs::write("x", &"".to_string());
|
||||
|
||||
{
|
||||
#[derive(Clone, Copy)]
|
||||
struct X;
|
||||
|
||||
impl Deref for X {
|
||||
type Target = X;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
fn deref_target_is_x<T: Deref<Target = X>>(_: T) {}
|
||||
|
||||
deref_target_is_x(&X);
|
||||
}
|
||||
{
|
||||
fn multiple_constraints<T, U, V, X, Y>(_: T)
|
||||
where
|
||||
T: IntoIterator<Item = U> + IntoIterator<Item = X>,
|
||||
U: IntoIterator<Item = V>,
|
||||
V: AsRef<str>,
|
||||
X: IntoIterator<Item = Y>,
|
||||
Y: AsRef<OsStr>,
|
||||
{
|
||||
}
|
||||
|
||||
multiple_constraints(&[[""]]);
|
||||
}
|
||||
{
|
||||
#[derive(Clone, Copy)]
|
||||
struct X;
|
||||
|
||||
impl Deref for X {
|
||||
type Target = X;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
fn multiple_constraints_normalizes_to_same<T, U, V>(_: T, _: V)
|
||||
where
|
||||
T: Deref<Target = U>,
|
||||
U: Deref<Target = V>,
|
||||
{
|
||||
}
|
||||
|
||||
multiple_constraints_normalizes_to_same(&X, X);
|
||||
}
|
||||
{
|
||||
fn only_sized<T>(_: T) {}
|
||||
only_sized(&""); // Don't lint. `Sized` is only bound
|
||||
}
|
||||
{
|
||||
fn ref_as_ref_path<T: 'static>(_: &'static T)
|
||||
where
|
||||
&'static T: AsRef<Path>,
|
||||
{
|
||||
}
|
||||
|
||||
ref_as_ref_path(&""); // Don't lint. Argument type is not a type parameter
|
||||
}
|
||||
{
|
||||
trait RefsOnly {
|
||||
type Referent;
|
||||
}
|
||||
|
||||
impl<T> RefsOnly for &T {
|
||||
type Referent = T;
|
||||
}
|
||||
|
||||
fn refs_only<T, U>(_: T)
|
||||
where
|
||||
T: RefsOnly<Referent = U>,
|
||||
{
|
||||
}
|
||||
|
||||
refs_only(&()); // Don't lint. `&T` implements trait, but `T` doesn't
|
||||
}
|
||||
{
|
||||
fn multiple_constraints_normalizes_to_different<T, U, V>(_: T, _: U)
|
||||
where
|
||||
T: IntoIterator<Item = U>,
|
||||
U: IntoIterator<Item = V>,
|
||||
V: AsRef<str>,
|
||||
{
|
||||
}
|
||||
multiple_constraints_normalizes_to_different(&[[""]], &[""]); // Don't lint. Projected type appears in arguments
|
||||
}
|
||||
// https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321
|
||||
{
|
||||
#[derive(Clone, Copy)]
|
||||
struct Iter;
|
||||
impl Iterator for Iter {
|
||||
type Item = ();
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
None
|
||||
}
|
||||
}
|
||||
fn takes_iter(_: impl Iterator) {}
|
||||
fn dont_warn(mut x: Iter) {
|
||||
takes_iter(&mut x);
|
||||
}
|
||||
#[allow(unused_mut)]
|
||||
fn warn(mut x: &mut Iter) {
|
||||
takes_iter(&mut x)
|
||||
}
|
||||
}
|
||||
#[clippy::msrv = "1.52.0"]
|
||||
{
|
||||
let _ = Command::new("ls").args(&["-a", "-l"]).status().unwrap();
|
||||
};
|
||||
#[clippy::msrv = "1.53.0"]
|
||||
{
|
||||
let _ = Command::new("ls").args(&["-a", "-l"]).status().unwrap();
|
||||
};
|
||||
{
|
||||
let env = "env".to_owned();
|
||||
let arg = "arg".to_owned();
|
||||
let f = |arg| {
|
||||
let loc = "loc".to_owned();
|
||||
let _ = std::fs::write("x", &env); // Don't lint. In environment
|
||||
let _ = std::fs::write("x", &arg);
|
||||
let _ = std::fs::write("x", &loc);
|
||||
};
|
||||
let _ = std::fs::write("x", &env); // Don't lint. Borrowed by `f`
|
||||
f(arg);
|
||||
}
|
||||
{
|
||||
#[derive(Debug)]
|
||||
struct X;
|
||||
|
||||
impl Drop for X {
|
||||
fn drop(&mut self) {}
|
||||
}
|
||||
|
||||
fn f(_: impl Debug) {}
|
||||
|
||||
let x = X;
|
||||
f(&x); // Don't lint. Has significant drop
|
||||
}
|
||||
{
|
||||
fn f(_: impl AsRef<str>) {}
|
||||
|
||||
let x = String::new();
|
||||
f(&x);
|
||||
}
|
||||
{
|
||||
fn f(_: impl AsRef<str>) {}
|
||||
fn f2(_: impl AsRef<str>) {}
|
||||
|
||||
let x = String::new();
|
||||
f(&x);
|
||||
f2(&x);
|
||||
}
|
||||
// https://github.com/rust-lang/rust-clippy/issues/9111#issuecomment-1277114280
|
||||
// issue 9111
|
||||
{
|
||||
struct A;
|
||||
|
||||
impl Extend<u8> for A {
|
||||
fn extend<T: IntoIterator<Item = u8>>(&mut self, _: T) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Extend<&'a u8> for A {
|
||||
fn extend<T: IntoIterator<Item = &'a u8>>(&mut self, _: T) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
let mut a = A;
|
||||
a.extend(&[]); // vs a.extend([]);
|
||||
}
|
||||
// issue 9710
|
||||
{
|
||||
fn f(_: impl AsRef<str>) {}
|
||||
|
||||
let x = String::new();
|
||||
for _ in 0..10 {
|
||||
f(&x);
|
||||
}
|
||||
}
|
||||
// issue 9739
|
||||
{
|
||||
fn foo<D: Display>(_it: impl IntoIterator<Item = D>) {}
|
||||
foo(if std::env::var_os("HI").is_some() {
|
||||
&[0]
|
||||
} else {
|
||||
&[] as &[u32]
|
||||
});
|
||||
}
|
||||
{
|
||||
struct S;
|
||||
|
||||
impl S {
|
||||
fn foo<D: Display>(&self, _it: impl IntoIterator<Item = D>) {}
|
||||
}
|
||||
|
||||
S.foo(if std::env::var_os("HI").is_some() {
|
||||
&[0]
|
||||
} else {
|
||||
&[] as &[u32]
|
||||
});
|
||||
}
|
||||
// issue 9782
|
||||
{
|
||||
fn foo<T: AsRef<[u8]>>(t: T) {
|
||||
println!("{}", std::mem::size_of::<T>());
|
||||
let _t: &[u8] = t.as_ref();
|
||||
}
|
||||
|
||||
let a: [u8; 100] = [0u8; 100];
|
||||
|
||||
// 100
|
||||
foo::<[u8; 100]>(a);
|
||||
foo(a);
|
||||
|
||||
// 16
|
||||
foo::<&[u8]>(&a);
|
||||
foo(a.as_slice());
|
||||
|
||||
// 8
|
||||
foo::<&[u8; 100]>(&a);
|
||||
foo(&a);
|
||||
}
|
||||
{
|
||||
struct S;
|
||||
|
||||
impl S {
|
||||
fn foo<T: AsRef<[u8]>>(t: T) {
|
||||
println!("{}", std::mem::size_of::<T>());
|
||||
let _t: &[u8] = t.as_ref();
|
||||
}
|
||||
}
|
||||
|
||||
let a: [u8; 100] = [0u8; 100];
|
||||
S::foo::<&[u8; 100]>(&a);
|
||||
}
|
||||
{
|
||||
struct S;
|
||||
|
||||
impl S {
|
||||
fn foo<T: AsRef<[u8]>>(&self, t: T) {
|
||||
println!("{}", std::mem::size_of::<T>());
|
||||
let _t: &[u8] = t.as_ref();
|
||||
}
|
||||
}
|
||||
|
||||
let a: [u8; 100] = [0u8; 100];
|
||||
S.foo::<&[u8; 100]>(&a);
|
||||
}
|
||||
// issue 10535
|
||||
{
|
||||
static SOME_STATIC: String = String::new();
|
||||
|
||||
static UNIT: () = compute(&SOME_STATIC);
|
||||
|
||||
pub const fn compute<T>(_: T)
|
||||
where
|
||||
T: Copy,
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
77
tests/ui/needless_borrows_for_generic_args.stderr
Normal file
77
tests/ui/needless_borrows_for_generic_args.stderr
Normal file
|
@ -0,0 +1,77 @@
|
|||
error: the borrowed expression implements the required traits
|
||||
--> $DIR/needless_borrows_for_generic_args.rs:16:37
|
||||
|
|
||||
LL | let _ = Command::new("ls").args(&["-a", "-l"]).status().unwrap();
|
||||
| ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]`
|
||||
|
|
||||
= note: `-D clippy::needless-borrows-for-generic-args` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::needless_borrows_for_generic_args)]`
|
||||
|
||||
error: the borrowed expression implements the required traits
|
||||
--> $DIR/needless_borrows_for_generic_args.rs:17:33
|
||||
|
|
||||
LL | let _ = Path::new(".").join(&&".");
|
||||
| ^^^^^ help: change this to: `"."`
|
||||
|
||||
error: the borrowed expression implements the required traits
|
||||
--> $DIR/needless_borrows_for_generic_args.rs:21:33
|
||||
|
|
||||
LL | let _ = std::fs::write("x", &"".to_string());
|
||||
| ^^^^^^^^^^^^^^^ help: change this to: `"".to_string()`
|
||||
|
||||
error: the borrowed expression implements the required traits
|
||||
--> $DIR/needless_borrows_for_generic_args.rs:36:27
|
||||
|
|
||||
LL | deref_target_is_x(&X);
|
||||
| ^^ help: change this to: `X`
|
||||
|
||||
error: the borrowed expression implements the required traits
|
||||
--> $DIR/needless_borrows_for_generic_args.rs:49:30
|
||||
|
|
||||
LL | multiple_constraints(&[[""]]);
|
||||
| ^^^^^^^ help: change this to: `[[""]]`
|
||||
|
||||
error: the borrowed expression implements the required traits
|
||||
--> $DIR/needless_borrows_for_generic_args.rs:69:49
|
||||
|
|
||||
LL | multiple_constraints_normalizes_to_same(&X, X);
|
||||
| ^^ help: change this to: `X`
|
||||
|
||||
error: the borrowed expression implements the required traits
|
||||
--> $DIR/needless_borrows_for_generic_args.rs:127:24
|
||||
|
|
||||
LL | takes_iter(&mut x)
|
||||
| ^^^^^^ help: change this to: `x`
|
||||
|
||||
error: the borrowed expression implements the required traits
|
||||
--> $DIR/needless_borrows_for_generic_args.rs:136:41
|
||||
|
|
||||
LL | let _ = Command::new("ls").args(&["-a", "-l"]).status().unwrap();
|
||||
| ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]`
|
||||
|
||||
error: the borrowed expression implements the required traits
|
||||
--> $DIR/needless_borrows_for_generic_args.rs:144:41
|
||||
|
|
||||
LL | let _ = std::fs::write("x", &arg);
|
||||
| ^^^^ help: change this to: `arg`
|
||||
|
||||
error: the borrowed expression implements the required traits
|
||||
--> $DIR/needless_borrows_for_generic_args.rs:145:41
|
||||
|
|
||||
LL | let _ = std::fs::write("x", &loc);
|
||||
| ^^^^ help: change this to: `loc`
|
||||
|
||||
error: the borrowed expression implements the required traits
|
||||
--> $DIR/needless_borrows_for_generic_args.rs:167:11
|
||||
|
|
||||
LL | f(&x);
|
||||
| ^^ help: change this to: `x`
|
||||
|
||||
error: the borrowed expression implements the required traits
|
||||
--> $DIR/needless_borrows_for_generic_args.rs:247:13
|
||||
|
|
||||
LL | foo(&a);
|
||||
| ^^ help: change this to: `a`
|
||||
|
||||
error: aborting due to 12 previous errors
|
||||
|
|
@ -2,7 +2,8 @@
|
|||
unused,
|
||||
clippy::needless_raw_strings,
|
||||
clippy::needless_raw_string_hashes,
|
||||
clippy::needless_borrow
|
||||
clippy::needless_borrow,
|
||||
clippy::needless_borrows_for_generic_args
|
||||
)]
|
||||
#![warn(clippy::invalid_regex, clippy::trivial_regex)]
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
error: trivial regex
|
||||
--> $DIR/regex.rs:18:45
|
||||
--> $DIR/regex.rs:19:45
|
||||
|
|
||||
LL | let pipe_in_wrong_position = Regex::new("|");
|
||||
| ^^^
|
||||
|
@ -9,7 +9,7 @@ LL | let pipe_in_wrong_position = Regex::new("|");
|
|||
= help: to override `-D warnings` add `#[allow(clippy::trivial_regex)]`
|
||||
|
||||
error: trivial regex
|
||||
--> $DIR/regex.rs:20:60
|
||||
--> $DIR/regex.rs:21:60
|
||||
|
|
||||
LL | let pipe_in_wrong_position_builder = RegexBuilder::new("|");
|
||||
| ^^^
|
||||
|
@ -17,7 +17,7 @@ LL | let pipe_in_wrong_position_builder = RegexBuilder::new("|");
|
|||
= help: the regex is unlikely to be useful as it is
|
||||
|
||||
error: regex syntax error: invalid character class range, the start must be <= the end
|
||||
--> $DIR/regex.rs:22:42
|
||||
--> $DIR/regex.rs:23:42
|
||||
|
|
||||
LL | let wrong_char_ranice = Regex::new("[z-a]");
|
||||
| ^^^
|
||||
|
@ -26,7 +26,7 @@ LL | let wrong_char_ranice = Regex::new("[z-a]");
|
|||
= help: to override `-D warnings` add `#[allow(clippy::invalid_regex)]`
|
||||
|
||||
error: regex syntax error: invalid character class range, the start must be <= the end
|
||||
--> $DIR/regex.rs:25:37
|
||||
--> $DIR/regex.rs:26:37
|
||||
|
|
||||
LL | let some_unicode = Regex::new("[é-è]");
|
||||
| ^^^
|
||||
|
@ -35,13 +35,13 @@ error: regex parse error:
|
|||
(
|
||||
^
|
||||
error: unclosed group
|
||||
--> $DIR/regex.rs:28:33
|
||||
--> $DIR/regex.rs:29:33
|
||||
|
|
||||
LL | let some_regex = Regex::new(OPENING_PAREN);
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
error: trivial regex
|
||||
--> $DIR/regex.rs:30:53
|
||||
--> $DIR/regex.rs:31:53
|
||||
|
|
||||
LL | let binary_pipe_in_wrong_position = BRegex::new("|");
|
||||
| ^^^
|
||||
|
@ -52,7 +52,7 @@ error: regex parse error:
|
|||
(
|
||||
^
|
||||
error: unclosed group
|
||||
--> $DIR/regex.rs:32:41
|
||||
--> $DIR/regex.rs:33:41
|
||||
|
|
||||
LL | let some_binary_regex = BRegex::new(OPENING_PAREN);
|
||||
| ^^^^^^^^^^^^^
|
||||
|
@ -61,7 +61,7 @@ error: regex parse error:
|
|||
(
|
||||
^
|
||||
error: unclosed group
|
||||
--> $DIR/regex.rs:33:56
|
||||
--> $DIR/regex.rs:34:56
|
||||
|
|
||||
LL | let some_binary_regex_builder = BRegexBuilder::new(OPENING_PAREN);
|
||||
| ^^^^^^^^^^^^^
|
||||
|
@ -70,7 +70,7 @@ error: regex parse error:
|
|||
(
|
||||
^
|
||||
error: unclosed group
|
||||
--> $DIR/regex.rs:45:37
|
||||
--> $DIR/regex.rs:46:37
|
||||
|
|
||||
LL | let set_error = RegexSet::new(&[OPENING_PAREN, r"[a-z]+\.(com|org|net)"]);
|
||||
| ^^^^^^^^^^^^^
|
||||
|
@ -79,7 +79,7 @@ error: regex parse error:
|
|||
(
|
||||
^
|
||||
error: unclosed group
|
||||
--> $DIR/regex.rs:46:39
|
||||
--> $DIR/regex.rs:47:39
|
||||
|
|
||||
LL | let bset_error = BRegexSet::new(&[OPENING_PAREN, r"[a-z]+\.(com|org|net)"]);
|
||||
| ^^^^^^^^^^^^^
|
||||
|
@ -88,7 +88,7 @@ error: regex parse error:
|
|||
\b\c
|
||||
^^
|
||||
error: unrecognized escape sequence
|
||||
--> $DIR/regex.rs:53:42
|
||||
--> $DIR/regex.rs:54:42
|
||||
|
|
||||
LL | let escaped_string_span = Regex::new("\\b\\c");
|
||||
| ^^^^^^^^
|
||||
|
@ -96,19 +96,19 @@ LL | let escaped_string_span = Regex::new("\\b\\c");
|
|||
= help: consider using a raw string literal: `r".."`
|
||||
|
||||
error: regex syntax error: duplicate flag
|
||||
--> $DIR/regex.rs:55:34
|
||||
--> $DIR/regex.rs:56:34
|
||||
|
|
||||
LL | let aux_span = Regex::new("(?ixi)");
|
||||
| ^ ^
|
||||
|
||||
error: regex syntax error: pattern can match invalid UTF-8
|
||||
--> $DIR/regex.rs:61:53
|
||||
--> $DIR/regex.rs:62:53
|
||||
|
|
||||
LL | let invalid_utf8_should_lint = Regex::new("(?-u).");
|
||||
| ^
|
||||
|
||||
error: trivial regex
|
||||
--> $DIR/regex.rs:66:33
|
||||
--> $DIR/regex.rs:67:33
|
||||
|
|
||||
LL | let trivial_eq = Regex::new("^foobar$");
|
||||
| ^^^^^^^^^^
|
||||
|
@ -116,7 +116,7 @@ LL | let trivial_eq = Regex::new("^foobar$");
|
|||
= help: consider using `==` on `str`s
|
||||
|
||||
error: trivial regex
|
||||
--> $DIR/regex.rs:69:48
|
||||
--> $DIR/regex.rs:70:48
|
||||
|
|
||||
LL | let trivial_eq_builder = RegexBuilder::new("^foobar$");
|
||||
| ^^^^^^^^^^
|
||||
|
@ -124,7 +124,7 @@ LL | let trivial_eq_builder = RegexBuilder::new("^foobar$");
|
|||
= help: consider using `==` on `str`s
|
||||
|
||||
error: trivial regex
|
||||
--> $DIR/regex.rs:72:42
|
||||
--> $DIR/regex.rs:73:42
|
||||
|
|
||||
LL | let trivial_starts_with = Regex::new("^foobar");
|
||||
| ^^^^^^^^^
|
||||
|
@ -132,7 +132,7 @@ LL | let trivial_starts_with = Regex::new("^foobar");
|
|||
= help: consider using `str::starts_with`
|
||||
|
||||
error: trivial regex
|
||||
--> $DIR/regex.rs:75:40
|
||||
--> $DIR/regex.rs:76:40
|
||||
|
|
||||
LL | let trivial_ends_with = Regex::new("foobar$");
|
||||
| ^^^^^^^^^
|
||||
|
@ -140,7 +140,7 @@ LL | let trivial_ends_with = Regex::new("foobar$");
|
|||
= help: consider using `str::ends_with`
|
||||
|
||||
error: trivial regex
|
||||
--> $DIR/regex.rs:78:39
|
||||
--> $DIR/regex.rs:79:39
|
||||
|
|
||||
LL | let trivial_contains = Regex::new("foobar");
|
||||
| ^^^^^^^^
|
||||
|
@ -148,7 +148,7 @@ LL | let trivial_contains = Regex::new("foobar");
|
|||
= help: consider using `str::contains`
|
||||
|
||||
error: trivial regex
|
||||
--> $DIR/regex.rs:81:39
|
||||
--> $DIR/regex.rs:82:39
|
||||
|
|
||||
LL | let trivial_contains = Regex::new(NOT_A_REAL_REGEX);
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
@ -156,7 +156,7 @@ LL | let trivial_contains = Regex::new(NOT_A_REAL_REGEX);
|
|||
= help: consider using `str::contains`
|
||||
|
||||
error: trivial regex
|
||||
--> $DIR/regex.rs:84:40
|
||||
--> $DIR/regex.rs:85:40
|
||||
|
|
||||
LL | let trivial_backslash = Regex::new("a\\.b");
|
||||
| ^^^^^^^
|
||||
|
@ -164,7 +164,7 @@ LL | let trivial_backslash = Regex::new("a\\.b");
|
|||
= help: consider using `str::contains`
|
||||
|
||||
error: trivial regex
|
||||
--> $DIR/regex.rs:88:36
|
||||
--> $DIR/regex.rs:89:36
|
||||
|
|
||||
LL | let trivial_empty = Regex::new("");
|
||||
| ^^
|
||||
|
@ -172,7 +172,7 @@ LL | let trivial_empty = Regex::new("");
|
|||
= help: the regex is unlikely to be useful as it is
|
||||
|
||||
error: trivial regex
|
||||
--> $DIR/regex.rs:91:36
|
||||
--> $DIR/regex.rs:92:36
|
||||
|
|
||||
LL | let trivial_empty = Regex::new("^");
|
||||
| ^^^
|
||||
|
@ -180,7 +180,7 @@ LL | let trivial_empty = Regex::new("^");
|
|||
= help: the regex is unlikely to be useful as it is
|
||||
|
||||
error: trivial regex
|
||||
--> $DIR/regex.rs:94:36
|
||||
--> $DIR/regex.rs:95:36
|
||||
|
|
||||
LL | let trivial_empty = Regex::new("^$");
|
||||
| ^^^^
|
||||
|
@ -188,7 +188,7 @@ LL | let trivial_empty = Regex::new("^$");
|
|||
= help: consider using `str::is_empty`
|
||||
|
||||
error: trivial regex
|
||||
--> $DIR/regex.rs:97:44
|
||||
--> $DIR/regex.rs:98:44
|
||||
|
|
||||
LL | let binary_trivial_empty = BRegex::new("^$");
|
||||
| ^^^^
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#![allow(clippy::needless_borrow, clippy::ptr_arg)]
|
||||
#![allow(clippy::needless_borrow, clippy::needless_borrows_for_generic_args, clippy::ptr_arg)]
|
||||
#![warn(clippy::unnecessary_to_owned, clippy::redundant_clone)]
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#![allow(clippy::needless_borrow, clippy::ptr_arg)]
|
||||
#![allow(clippy::needless_borrow, clippy::needless_borrows_for_generic_args, clippy::ptr_arg)]
|
||||
#![warn(clippy::unnecessary_to_owned, clippy::redundant_clone)]
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
|
Loading…
Reference in a new issue