Fix needless_borrow
suggestion when calling a trait method taking self
This commit is contained in:
parent
0b4ba734cb
commit
6d21b79be9
@ -11,11 +11,13 @@
|
|||||||
ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind, Path, QPath, TraitItem,
|
ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind, Path, QPath, TraitItem,
|
||||||
TraitItemKind, TyKind, UnOp,
|
TraitItemKind, TyKind, UnOp,
|
||||||
};
|
};
|
||||||
|
use rustc_infer::infer::TyCtxtInferExt;
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
|
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
|
||||||
use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeckResults};
|
use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeckResults};
|
||||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||||
use rustc_span::{symbol::sym, Span, Symbol};
|
use rustc_span::{symbol::sym, Span, Symbol};
|
||||||
|
use rustc_trait_selection::infer::InferCtxtExt;
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
@ -165,7 +167,6 @@ struct StateData {
|
|||||||
|
|
||||||
struct DerefedBorrow {
|
struct DerefedBorrow {
|
||||||
count: usize,
|
count: usize,
|
||||||
required_precedence: i8,
|
|
||||||
msg: &'static str,
|
msg: &'static str,
|
||||||
position: Position,
|
position: Position,
|
||||||
}
|
}
|
||||||
@ -329,19 +330,19 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
|||||||
"this expression creates a reference which is immediately dereferenced by the compiler";
|
"this expression creates a reference which is immediately dereferenced by the compiler";
|
||||||
let borrow_msg = "this expression borrows a value the compiler would automatically borrow";
|
let borrow_msg = "this expression borrows a value the compiler would automatically borrow";
|
||||||
|
|
||||||
let (required_refs, required_precedence, msg) = if position.can_auto_borrow() {
|
let (required_refs, msg) = if position.can_auto_borrow() {
|
||||||
(1, PREC_POSTFIX, if deref_count == 1 { borrow_msg } else { deref_msg })
|
(1, if deref_count == 1 { borrow_msg } else { deref_msg })
|
||||||
} else if let Some(&Adjust::Borrow(AutoBorrow::Ref(_, mutability))) =
|
} else if let Some(&Adjust::Borrow(AutoBorrow::Ref(_, mutability))) =
|
||||||
next_adjust.map(|a| &a.kind)
|
next_adjust.map(|a| &a.kind)
|
||||||
{
|
{
|
||||||
if matches!(mutability, AutoBorrowMutability::Mut { .. }) && !position.is_reborrow_stable()
|
if matches!(mutability, AutoBorrowMutability::Mut { .. }) && !position.is_reborrow_stable()
|
||||||
{
|
{
|
||||||
(3, 0, deref_msg)
|
(3, deref_msg)
|
||||||
} else {
|
} else {
|
||||||
(2, 0, deref_msg)
|
(2, deref_msg)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
(2, 0, deref_msg)
|
(2, deref_msg)
|
||||||
};
|
};
|
||||||
|
|
||||||
if deref_count >= required_refs {
|
if deref_count >= required_refs {
|
||||||
@ -350,7 +351,6 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
|||||||
// One of the required refs is for the current borrow expression, the remaining ones
|
// One of the required refs is for the current borrow expression, the remaining ones
|
||||||
// can't be removed without breaking the code. See earlier comment.
|
// can't be removed without breaking the code. See earlier comment.
|
||||||
count: deref_count - required_refs,
|
count: deref_count - required_refs,
|
||||||
required_precedence,
|
|
||||||
msg,
|
msg,
|
||||||
position,
|
position,
|
||||||
}),
|
}),
|
||||||
@ -601,6 +601,8 @@ fn deref_method_same_type<'tcx>(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool {
|
|||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
enum Position {
|
enum Position {
|
||||||
MethodReceiver,
|
MethodReceiver,
|
||||||
|
/// The method is defined on a reference type. e.g. `impl Foo for &T`
|
||||||
|
MethodReceiverRefImpl,
|
||||||
Callee,
|
Callee,
|
||||||
FieldAccess(Symbol),
|
FieldAccess(Symbol),
|
||||||
Postfix,
|
Postfix,
|
||||||
@ -627,6 +629,13 @@ fn can_auto_borrow(self) -> bool {
|
|||||||
fn lint_explicit_deref(self) -> bool {
|
fn lint_explicit_deref(self) -> bool {
|
||||||
matches!(self, Self::Other | Self::DerefStable | Self::ReborrowStable)
|
matches!(self, Self::Other | Self::DerefStable | Self::ReborrowStable)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn needs_parens(self, precedence: i8) -> bool {
|
||||||
|
matches!(
|
||||||
|
self,
|
||||||
|
Self::MethodReceiver | Self::MethodReceiverRefImpl | Self::Callee | Self::FieldAccess(_) | Self::Postfix
|
||||||
|
) && precedence < PREC_POSTFIX
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Walks up the parent expressions attempting to determine both how stable the auto-deref result
|
/// Walks up the parent expressions attempting to determine both how stable the auto-deref result
|
||||||
@ -730,10 +739,34 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &
|
|||||||
let id = cx.typeck_results().type_dependent_def_id(parent.hir_id).unwrap();
|
let id = cx.typeck_results().type_dependent_def_id(parent.hir_id).unwrap();
|
||||||
args.iter().position(|arg| arg.hir_id == child_id).map(|i| {
|
args.iter().position(|arg| arg.hir_id == child_id).map(|i| {
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
if e.hir_id == child_id {
|
// Check for calls to trait methods where the trait is implemented on a reference.
|
||||||
Position::MethodReceiver
|
// Two cases need to be handled:
|
||||||
} else {
|
// * `self` methods on `&T` will never have auto-borrow
|
||||||
|
// * `&self` methods on `&T` can have auto-borrow, but `&self` methods on `T` will take
|
||||||
|
// priority.
|
||||||
|
if e.hir_id != child_id {
|
||||||
Position::ReborrowStable
|
Position::ReborrowStable
|
||||||
|
} else if let Some(trait_id) = cx.tcx.trait_of_item(id)
|
||||||
|
&& let arg_ty = cx.tcx.erase_regions(cx.typeck_results().expr_ty_adjusted(e))
|
||||||
|
&& let ty::Ref(_, sub_ty, _) = *arg_ty.kind()
|
||||||
|
&& let subs = cx.typeck_results().node_substs_opt(child_id).unwrap_or_else(
|
||||||
|
|| cx.tcx.mk_substs([].iter())
|
||||||
|
) && let impl_ty = if cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref() {
|
||||||
|
// Trait methods taking `&self`
|
||||||
|
sub_ty
|
||||||
|
} else {
|
||||||
|
// Trait methods taking `self`
|
||||||
|
arg_ty
|
||||||
|
} && impl_ty.is_ref()
|
||||||
|
&& cx.tcx.infer_ctxt().enter(|infcx|
|
||||||
|
infcx
|
||||||
|
.type_implements_trait(trait_id, impl_ty, subs, cx.param_env)
|
||||||
|
.must_apply_modulo_regions()
|
||||||
|
)
|
||||||
|
{
|
||||||
|
Position::MethodReceiverRefImpl
|
||||||
|
} else {
|
||||||
|
Position::MethodReceiver
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
param_auto_deref_stability(cx.tcx.fn_sig(id).skip_binder().inputs()[i])
|
param_auto_deref_stability(cx.tcx.fn_sig(id).skip_binder().inputs()[i])
|
||||||
@ -964,7 +997,7 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
|
|||||||
let mut app = Applicability::MachineApplicable;
|
let mut app = Applicability::MachineApplicable;
|
||||||
let snip = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app).0;
|
let snip = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app).0;
|
||||||
span_lint_hir_and_then(cx, NEEDLESS_BORROW, data.hir_id, data.span, state.msg, |diag| {
|
span_lint_hir_and_then(cx, NEEDLESS_BORROW, data.hir_id, data.span, state.msg, |diag| {
|
||||||
let sugg = if state.required_precedence > expr.precedence().order() && !has_enclosing_paren(&snip) {
|
let sugg = if state.position.needs_parens(expr.precedence().order()) && !has_enclosing_paren(&snip) {
|
||||||
format!("({})", snip)
|
format!("({})", snip)
|
||||||
} else {
|
} else {
|
||||||
snip.into()
|
snip.into()
|
||||||
|
@ -85,6 +85,36 @@ fn main() {
|
|||||||
let _ = x.0;
|
let _ = x.0;
|
||||||
let x = &x as *const (i32, i32);
|
let x = &x as *const (i32, i32);
|
||||||
let _ = unsafe { (*x).0 };
|
let _ = unsafe { (*x).0 };
|
||||||
|
|
||||||
|
// Issue #8367
|
||||||
|
trait Foo {
|
||||||
|
fn foo(self);
|
||||||
|
}
|
||||||
|
impl Foo for &'_ () {
|
||||||
|
fn foo(self) {}
|
||||||
|
}
|
||||||
|
(&()).foo(); // Don't lint. `()` doesn't implement `Foo`
|
||||||
|
(&()).foo();
|
||||||
|
|
||||||
|
impl Foo for i32 {
|
||||||
|
fn foo(self) {}
|
||||||
|
}
|
||||||
|
impl Foo for &'_ i32 {
|
||||||
|
fn foo(self) {}
|
||||||
|
}
|
||||||
|
(&5).foo(); // Don't lint. `5` will call `<i32 as Foo>::foo`
|
||||||
|
(&5).foo();
|
||||||
|
|
||||||
|
trait FooRef {
|
||||||
|
fn foo_ref(&self);
|
||||||
|
}
|
||||||
|
impl FooRef for () {
|
||||||
|
fn foo_ref(&self) {}
|
||||||
|
}
|
||||||
|
impl FooRef for &'_ () {
|
||||||
|
fn foo_ref(&self) {}
|
||||||
|
}
|
||||||
|
(&&()).foo_ref(); // Don't lint. `&()` will call `<() as FooRef>::foo_ref`
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::needless_borrowed_reference)]
|
#[allow(clippy::needless_borrowed_reference)]
|
||||||
|
@ -85,6 +85,36 @@ fn ref_mut_i32(_: &mut i32) {}
|
|||||||
let _ = (&x).0;
|
let _ = (&x).0;
|
||||||
let x = &x as *const (i32, i32);
|
let x = &x as *const (i32, i32);
|
||||||
let _ = unsafe { (&*x).0 };
|
let _ = unsafe { (&*x).0 };
|
||||||
|
|
||||||
|
// Issue #8367
|
||||||
|
trait Foo {
|
||||||
|
fn foo(self);
|
||||||
|
}
|
||||||
|
impl Foo for &'_ () {
|
||||||
|
fn foo(self) {}
|
||||||
|
}
|
||||||
|
(&()).foo(); // Don't lint. `()` doesn't implement `Foo`
|
||||||
|
(&&()).foo();
|
||||||
|
|
||||||
|
impl Foo for i32 {
|
||||||
|
fn foo(self) {}
|
||||||
|
}
|
||||||
|
impl Foo for &'_ i32 {
|
||||||
|
fn foo(self) {}
|
||||||
|
}
|
||||||
|
(&5).foo(); // Don't lint. `5` will call `<i32 as Foo>::foo`
|
||||||
|
(&&5).foo();
|
||||||
|
|
||||||
|
trait FooRef {
|
||||||
|
fn foo_ref(&self);
|
||||||
|
}
|
||||||
|
impl FooRef for () {
|
||||||
|
fn foo_ref(&self) {}
|
||||||
|
}
|
||||||
|
impl FooRef for &'_ () {
|
||||||
|
fn foo_ref(&self) {}
|
||||||
|
}
|
||||||
|
(&&()).foo_ref(); // Don't lint. `&()` will call `<() as FooRef>::foo_ref`
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::needless_borrowed_reference)]
|
#[allow(clippy::needless_borrowed_reference)]
|
||||||
|
@ -108,5 +108,17 @@ error: this expression borrows a value the compiler would automatically borrow
|
|||||||
LL | let _ = unsafe { (&*x).0 };
|
LL | let _ = unsafe { (&*x).0 };
|
||||||
| ^^^^^ help: change this to: `(*x)`
|
| ^^^^^ help: change this to: `(*x)`
|
||||||
|
|
||||||
error: aborting due to 18 previous errors
|
error: this expression creates a reference which is immediately dereferenced by the compiler
|
||||||
|
--> $DIR/needless_borrow.rs:97:5
|
||||||
|
|
|
||||||
|
LL | (&&()).foo();
|
||||||
|
| ^^^^^^ help: change this to: `(&())`
|
||||||
|
|
||||||
|
error: this expression creates a reference which is immediately dereferenced by the compiler
|
||||||
|
--> $DIR/needless_borrow.rs:106:5
|
||||||
|
|
|
||||||
|
LL | (&&5).foo();
|
||||||
|
| ^^^^^ help: change this to: `(&5)`
|
||||||
|
|
||||||
|
error: aborting due to 20 previous errors
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user