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