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:
bors 2023-09-17 13:48:40 +00:00
commit 251a475b72
16 changed files with 1140 additions and 1070 deletions

View file

@ -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

View file

@ -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,

View file

@ -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)

View file

@ -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`
}

View 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
}

View file

@ -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
}
})
}

View file

@ -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 {

View file

@ -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 {

View file

@ -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

View 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,
{
}
}
}

View 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,
{
}
}
}

View 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

View file

@ -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)]

View file

@ -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("^$");
| ^^^^

View file

@ -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;

View file

@ -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;