mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-13 15:33:53 +00:00
Split part of needless_borrow
into needless_borrows_for_generic_args
This commit is contained in:
parent
fed036a57c
commit
79247d95f7
@ -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_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_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_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_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_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
|
[`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main
|
||||||
|
@ -479,6 +479,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
|||||||
crate::needless_bool::NEEDLESS_BOOL_INFO,
|
crate::needless_bool::NEEDLESS_BOOL_INFO,
|
||||||
crate::needless_bool::NEEDLESS_BOOL_ASSIGN_INFO,
|
crate::needless_bool::NEEDLESS_BOOL_ASSIGN_INFO,
|
||||||
crate::needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE_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_continue::NEEDLESS_CONTINUE_INFO,
|
||||||
crate::needless_else::NEEDLESS_ELSE_INFO,
|
crate::needless_else::NEEDLESS_ELSE_INFO,
|
||||||
crate::needless_for_each::NEEDLESS_FOR_EACH_INFO,
|
crate::needless_for_each::NEEDLESS_FOR_EACH_INFO,
|
||||||
|
@ -1,40 +1,24 @@
|
|||||||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
|
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::source::{snippet_with_applicability, snippet_with_context};
|
||||||
use clippy_utils::sugg::has_enclosing_paren;
|
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::{
|
use clippy_utils::{
|
||||||
expr_use_ctxt, get_parent_expr, get_parent_node, is_lint_allowed, path_to_local, DefinedTy, ExprUseNode,
|
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_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX};
|
||||||
use rustc_data_structures::fx::FxIndexMap;
|
use rustc_data_structures::fx::FxIndexMap;
|
||||||
use rustc_errors::Applicability;
|
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::intravisit::{walk_ty, Visitor};
|
||||||
use rustc_hir::{
|
use rustc_hir::{
|
||||||
self as hir, BindingAnnotation, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, Mutability, Node, Pat, PatKind,
|
self as hir, BindingAnnotation, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, MatchSource, Mutability, Node,
|
||||||
Path, QPath, TyKind, UnOp,
|
Pat, PatKind, Path, QPath, TyKind, UnOp,
|
||||||
};
|
};
|
||||||
use rustc_index::bit_set::BitSet;
|
|
||||||
use rustc_infer::infer::TyCtxtInferExt;
|
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_middle::mir::{Rvalue, StatementKind};
|
|
||||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
|
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
|
||||||
use rustc_middle::ty::{
|
use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt, TypeVisitableExt, TypeckResults};
|
||||||
self, ClauseKind, EarlyBinder, FnSig, GenericArg, GenericArgKind, List, ParamEnv, ParamTy, ProjectionPredicate, Ty,
|
|
||||||
TyCtxt, TypeVisitableExt, TypeckResults,
|
|
||||||
};
|
|
||||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||||
use rustc_span::symbol::sym;
|
use rustc_span::symbol::sym;
|
||||||
use rustc_span::{Span, Symbol};
|
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! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
@ -182,24 +166,6 @@ pub struct Dereferencing<'tcx> {
|
|||||||
///
|
///
|
||||||
/// e.g. `m!(x) | Foo::Bar(ref x)`
|
/// e.g. `m!(x) | Foo::Bar(ref x)`
|
||||||
ref_locals: FxIndexMap<HirId, Option<RefPat>>,
|
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)]
|
#[derive(Debug)]
|
||||||
@ -354,52 +320,6 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
|
|||||||
));
|
));
|
||||||
},
|
},
|
||||||
(Some(use_cx), RefOp::AddrOf(mutability)) => {
|
(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.
|
// Find the number of times the borrow is auto-derefed.
|
||||||
let mut iter = use_cx.adjustments.iter();
|
let mut iter = use_cx.adjustments.iter();
|
||||||
let mut deref_count = 0usize;
|
let mut deref_count = 0usize;
|
||||||
@ -418,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())
|
TyCoercionStability::for_defined_ty(cx, ty, use_cx.node.is_return())
|
||||||
});
|
});
|
||||||
let can_auto_borrow = match use_cx.node {
|
let can_auto_borrow = match use_cx.node {
|
||||||
@ -699,12 +619,6 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
|
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 {
|
if Some(body.id()) == self.current_body {
|
||||||
for pat in self.ref_locals.drain(..).filter_map(|(_, x)| x) {
|
for pat in self.ref_locals.drain(..).filter_map(|(_, x)| x) {
|
||||||
let replacements = pat.replacements;
|
let replacements = pat.replacements;
|
||||||
@ -728,8 +642,6 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
|
|||||||
self.current_body = None;
|
self.current_body = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extract_msrv_attr!(LateContext);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_parse_ref_op<'tcx>(
|
fn try_parse_ref_op<'tcx>(
|
||||||
@ -787,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 {
|
fn in_postfix_position<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
|
||||||
if let Some(parent) = get_parent_expr(cx, e)
|
if let Some(parent) = get_parent_expr(cx, e)
|
||||||
&& parent.span.ctxt() == e.span.ctxt()
|
&& parent.span.ctxt() == e.span.ctxt()
|
||||||
@ -980,272 +885,6 @@ fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool {
|
|||||||
v.0
|
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()
|
|
||||||
{
|
|
||||||
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 {
|
fn ty_contains_field(ty: Ty<'_>, name: Symbol) -> bool {
|
||||||
if let ty::Adt(adt, _) = *ty.kind() {
|
if let ty::Adt(adt, _) = *ty.kind() {
|
||||||
adt.is_struct() && adt.all_fields().any(|f| f.name == name)
|
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_arbitrary_self_type;
|
||||||
mod needless_bool;
|
mod needless_bool;
|
||||||
mod needless_borrowed_ref;
|
mod needless_borrowed_ref;
|
||||||
|
mod needless_borrows_for_generic_args;
|
||||||
mod needless_continue;
|
mod needless_continue;
|
||||||
mod needless_else;
|
mod needless_else;
|
||||||
mod needless_for_each;
|
mod needless_for_each;
|
||||||
@ -880,7 +881,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(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::<redundant_pub_crate::RedundantPubCrate>::default());
|
||||||
store.register_late_pass(|_| Box::new(unnamed_address::UnnamedAddress));
|
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(option_if_let_else::OptionIfLetElse));
|
||||||
store.register_late_pass(|_| Box::new(future_not_send::FutureNotSend));
|
store.register_late_pass(|_| Box::new(future_not_send::FutureNotSend));
|
||||||
let future_size_threshold = conf.future_size_threshold;
|
let future_size_threshold = conf.future_size_threshold;
|
||||||
@ -1104,6 +1105,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(implied_bounds_in_impls::ImpliedBoundsInImpls));
|
||||||
store.register_late_pass(|_| Box::new(missing_asserts_for_indexing::MissingAssertsForIndexing));
|
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(|_| 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`
|
// 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
|
||||||
|
}
|
@ -131,21 +131,6 @@ fn main() {
|
|||||||
0
|
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)]
|
#[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() {
|
fn issue9383() {
|
||||||
// Should not lint because unions need explicit deref when accessing field
|
// Should not lint because unions need explicit deref when accessing field
|
||||||
use std::mem::ManuallyDrop;
|
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 {
|
mod issue_10253 {
|
||||||
struct S;
|
struct S;
|
||||||
trait X {
|
trait X {
|
||||||
|
@ -131,21 +131,6 @@ fn main() {
|
|||||||
0
|
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)]
|
#[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() {
|
fn issue9383() {
|
||||||
// Should not lint because unions need explicit deref when accessing field
|
// Should not lint because unions need explicit deref when accessing field
|
||||||
use std::mem::ManuallyDrop;
|
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 {
|
mod issue_10253 {
|
||||||
struct S;
|
struct S;
|
||||||
trait X {
|
trait X {
|
||||||
|
@ -121,101 +121,17 @@ error: this expression creates a reference which is immediately dereferenced by
|
|||||||
LL | (&&5).foo();
|
LL | (&&5).foo();
|
||||||
| ^^^^^ help: change this to: `(&5)`
|
| ^^^^^ 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
|
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)()
|
LL | (&self.f)()
|
||||||
| ^^^^^^^^^ help: change this to: `(self.f)`
|
| ^^^^^^^^^ help: change this to: `(self.f)`
|
||||||
|
|
||||||
error: this expression borrows a value the compiler would automatically borrow
|
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)()
|
LL | (&mut self.f)()
|
||||||
| ^^^^^^^^^^^^^ help: change this to: `(self.f)`
|
| ^^^^^^^^^^^^^ help: change this to: `(self.f)`
|
||||||
|
|
||||||
error: the borrowed expression implements the required traits
|
error: aborting due to 22 previous errors
|
||||||
--> $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
|
|
||||||
|
|
||||||
|
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,
|
unused,
|
||||||
clippy::needless_raw_strings,
|
clippy::needless_raw_strings,
|
||||||
clippy::needless_raw_string_hashes,
|
clippy::needless_raw_string_hashes,
|
||||||
clippy::needless_borrow
|
clippy::needless_borrow,
|
||||||
|
clippy::needless_borrows_for_generic_args
|
||||||
)]
|
)]
|
||||||
#![warn(clippy::invalid_regex, clippy::trivial_regex)]
|
#![warn(clippy::invalid_regex, clippy::trivial_regex)]
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
error: trivial regex
|
error: trivial regex
|
||||||
--> $DIR/regex.rs:18:45
|
--> $DIR/regex.rs:19:45
|
||||||
|
|
|
|
||||||
LL | let pipe_in_wrong_position = Regex::new("|");
|
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)]`
|
= help: to override `-D warnings` add `#[allow(clippy::trivial_regex)]`
|
||||||
|
|
||||||
error: trivial regex
|
error: trivial regex
|
||||||
--> $DIR/regex.rs:20:60
|
--> $DIR/regex.rs:21:60
|
||||||
|
|
|
|
||||||
LL | let pipe_in_wrong_position_builder = RegexBuilder::new("|");
|
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
|
= 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
|
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]");
|
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)]`
|
= help: to override `-D warnings` add `#[allow(clippy::invalid_regex)]`
|
||||||
|
|
||||||
error: regex syntax error: invalid character class range, the start must be <= the end
|
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("[é-è]");
|
LL | let some_unicode = Regex::new("[é-è]");
|
||||||
| ^^^
|
| ^^^
|
||||||
@ -35,13 +35,13 @@ error: regex parse error:
|
|||||||
(
|
(
|
||||||
^
|
^
|
||||||
error: unclosed group
|
error: unclosed group
|
||||||
--> $DIR/regex.rs:28:33
|
--> $DIR/regex.rs:29:33
|
||||||
|
|
|
|
||||||
LL | let some_regex = Regex::new(OPENING_PAREN);
|
LL | let some_regex = Regex::new(OPENING_PAREN);
|
||||||
| ^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: trivial regex
|
error: trivial regex
|
||||||
--> $DIR/regex.rs:30:53
|
--> $DIR/regex.rs:31:53
|
||||||
|
|
|
|
||||||
LL | let binary_pipe_in_wrong_position = BRegex::new("|");
|
LL | let binary_pipe_in_wrong_position = BRegex::new("|");
|
||||||
| ^^^
|
| ^^^
|
||||||
@ -52,7 +52,7 @@ error: regex parse error:
|
|||||||
(
|
(
|
||||||
^
|
^
|
||||||
error: unclosed group
|
error: unclosed group
|
||||||
--> $DIR/regex.rs:32:41
|
--> $DIR/regex.rs:33:41
|
||||||
|
|
|
|
||||||
LL | let some_binary_regex = BRegex::new(OPENING_PAREN);
|
LL | let some_binary_regex = BRegex::new(OPENING_PAREN);
|
||||||
| ^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^
|
||||||
@ -61,7 +61,7 @@ error: regex parse error:
|
|||||||
(
|
(
|
||||||
^
|
^
|
||||||
error: unclosed group
|
error: unclosed group
|
||||||
--> $DIR/regex.rs:33:56
|
--> $DIR/regex.rs:34:56
|
||||||
|
|
|
|
||||||
LL | let some_binary_regex_builder = BRegexBuilder::new(OPENING_PAREN);
|
LL | let some_binary_regex_builder = BRegexBuilder::new(OPENING_PAREN);
|
||||||
| ^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^
|
||||||
@ -70,7 +70,7 @@ error: regex parse error:
|
|||||||
(
|
(
|
||||||
^
|
^
|
||||||
error: unclosed group
|
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)"]);
|
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
|
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)"]);
|
LL | let bset_error = BRegexSet::new(&[OPENING_PAREN, r"[a-z]+\.(com|org|net)"]);
|
||||||
| ^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^
|
||||||
@ -88,7 +88,7 @@ error: regex parse error:
|
|||||||
\b\c
|
\b\c
|
||||||
^^
|
^^
|
||||||
error: unrecognized escape sequence
|
error: unrecognized escape sequence
|
||||||
--> $DIR/regex.rs:53:42
|
--> $DIR/regex.rs:54:42
|
||||||
|
|
|
|
||||||
LL | let escaped_string_span = Regex::new("\\b\\c");
|
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".."`
|
= help: consider using a raw string literal: `r".."`
|
||||||
|
|
||||||
error: regex syntax error: duplicate flag
|
error: regex syntax error: duplicate flag
|
||||||
--> $DIR/regex.rs:55:34
|
--> $DIR/regex.rs:56:34
|
||||||
|
|
|
|
||||||
LL | let aux_span = Regex::new("(?ixi)");
|
LL | let aux_span = Regex::new("(?ixi)");
|
||||||
| ^ ^
|
| ^ ^
|
||||||
|
|
||||||
error: regex syntax error: pattern can match invalid UTF-8
|
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).");
|
LL | let invalid_utf8_should_lint = Regex::new("(?-u).");
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: trivial regex
|
error: trivial regex
|
||||||
--> $DIR/regex.rs:66:33
|
--> $DIR/regex.rs:67:33
|
||||||
|
|
|
|
||||||
LL | let trivial_eq = Regex::new("^foobar$");
|
LL | let trivial_eq = Regex::new("^foobar$");
|
||||||
| ^^^^^^^^^^
|
| ^^^^^^^^^^
|
||||||
@ -116,7 +116,7 @@ LL | let trivial_eq = Regex::new("^foobar$");
|
|||||||
= help: consider using `==` on `str`s
|
= help: consider using `==` on `str`s
|
||||||
|
|
||||||
error: trivial regex
|
error: trivial regex
|
||||||
--> $DIR/regex.rs:69:48
|
--> $DIR/regex.rs:70:48
|
||||||
|
|
|
|
||||||
LL | let trivial_eq_builder = RegexBuilder::new("^foobar$");
|
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
|
= help: consider using `==` on `str`s
|
||||||
|
|
||||||
error: trivial regex
|
error: trivial regex
|
||||||
--> $DIR/regex.rs:72:42
|
--> $DIR/regex.rs:73:42
|
||||||
|
|
|
|
||||||
LL | let trivial_starts_with = Regex::new("^foobar");
|
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`
|
= help: consider using `str::starts_with`
|
||||||
|
|
||||||
error: trivial regex
|
error: trivial regex
|
||||||
--> $DIR/regex.rs:75:40
|
--> $DIR/regex.rs:76:40
|
||||||
|
|
|
|
||||||
LL | let trivial_ends_with = Regex::new("foobar$");
|
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`
|
= help: consider using `str::ends_with`
|
||||||
|
|
||||||
error: trivial regex
|
error: trivial regex
|
||||||
--> $DIR/regex.rs:78:39
|
--> $DIR/regex.rs:79:39
|
||||||
|
|
|
|
||||||
LL | let trivial_contains = Regex::new("foobar");
|
LL | let trivial_contains = Regex::new("foobar");
|
||||||
| ^^^^^^^^
|
| ^^^^^^^^
|
||||||
@ -148,7 +148,7 @@ LL | let trivial_contains = Regex::new("foobar");
|
|||||||
= help: consider using `str::contains`
|
= help: consider using `str::contains`
|
||||||
|
|
||||||
error: trivial regex
|
error: trivial regex
|
||||||
--> $DIR/regex.rs:81:39
|
--> $DIR/regex.rs:82:39
|
||||||
|
|
|
|
||||||
LL | let trivial_contains = Regex::new(NOT_A_REAL_REGEX);
|
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`
|
= help: consider using `str::contains`
|
||||||
|
|
||||||
error: trivial regex
|
error: trivial regex
|
||||||
--> $DIR/regex.rs:84:40
|
--> $DIR/regex.rs:85:40
|
||||||
|
|
|
|
||||||
LL | let trivial_backslash = Regex::new("a\\.b");
|
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`
|
= help: consider using `str::contains`
|
||||||
|
|
||||||
error: trivial regex
|
error: trivial regex
|
||||||
--> $DIR/regex.rs:88:36
|
--> $DIR/regex.rs:89:36
|
||||||
|
|
|
|
||||||
LL | let trivial_empty = Regex::new("");
|
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
|
= help: the regex is unlikely to be useful as it is
|
||||||
|
|
||||||
error: trivial regex
|
error: trivial regex
|
||||||
--> $DIR/regex.rs:91:36
|
--> $DIR/regex.rs:92:36
|
||||||
|
|
|
|
||||||
LL | let trivial_empty = Regex::new("^");
|
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
|
= help: the regex is unlikely to be useful as it is
|
||||||
|
|
||||||
error: trivial regex
|
error: trivial regex
|
||||||
--> $DIR/regex.rs:94:36
|
--> $DIR/regex.rs:95:36
|
||||||
|
|
|
|
||||||
LL | let trivial_empty = Regex::new("^$");
|
LL | let trivial_empty = Regex::new("^$");
|
||||||
| ^^^^
|
| ^^^^
|
||||||
@ -188,7 +188,7 @@ LL | let trivial_empty = Regex::new("^$");
|
|||||||
= help: consider using `str::is_empty`
|
= help: consider using `str::is_empty`
|
||||||
|
|
||||||
error: trivial regex
|
error: trivial regex
|
||||||
--> $DIR/regex.rs:97:44
|
--> $DIR/regex.rs:98:44
|
||||||
|
|
|
|
||||||
LL | let binary_trivial_empty = BRegex::new("^$");
|
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)]
|
#![warn(clippy::unnecessary_to_owned, clippy::redundant_clone)]
|
||||||
|
|
||||||
use std::borrow::Cow;
|
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)]
|
#![warn(clippy::unnecessary_to_owned, clippy::redundant_clone)]
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
Loading…
Reference in New Issue
Block a user