-
Notifications
You must be signed in to change notification settings - Fork 1.7k
redundant_slicing
precedence fix
#7976
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -2,9 +2,9 @@ | |||||
|
||||||
#![allow(clippy::module_name_repetitions)] | ||||||
|
||||||
use crate::line_span; | ||||||
use crate::{get_parent_expr, line_span}; | ||||||
use rustc_errors::Applicability; | ||||||
use rustc_hir::{Expr, ExprKind}; | ||||||
use rustc_hir::{BinOpKind, Expr, ExprKind, MatchSource}; | ||||||
use rustc_lint::{LateContext, LintContext}; | ||||||
use rustc_span::hygiene; | ||||||
use rustc_span::{BytePos, Pos, Span, SyntaxContext}; | ||||||
|
@@ -306,6 +306,199 @@ pub fn snippet_with_context( | |||||
) | ||||||
} | ||||||
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] | ||||||
pub enum ExprPosition { | ||||||
xFrednet marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
// Also includes `return`, `yield`, `break` and closures | ||||||
Paren, | ||||||
AssignmentRhs, | ||||||
AssignmentLhs, | ||||||
RangeLhs, | ||||||
RangeRhs, | ||||||
OrLhs, | ||||||
OrRhs, | ||||||
AndLhs, | ||||||
AndRhs, | ||||||
Let, | ||||||
EqLhs, | ||||||
EqRhs, | ||||||
BitOrLhs, | ||||||
BitOrRhs, | ||||||
BitXorLhs, | ||||||
BitXorRhs, | ||||||
BitAndLhs, | ||||||
BitAndRhs, | ||||||
ShiftLhs, | ||||||
ShiftRhs, | ||||||
AddLhs, | ||||||
AddRhs, | ||||||
MulLhs, | ||||||
MulRhs, | ||||||
// Also includes type ascription | ||||||
Cast, | ||||||
Prefix, | ||||||
Postfix, | ||||||
} | ||||||
|
||||||
/// Extracts a snippet of the given expression taking into account the `SyntaxContext` the snippet | ||||||
/// needs to be taken from. Parenthesis will be added if needed to place the snippet in the target | ||||||
/// precedence level. Returns a placeholder (`(..)`) if a snippet can't be extracted (e.g. an | ||||||
/// invalid span). | ||||||
/// | ||||||
/// The `SyntaxContext` of the expression will be walked up to the given target context (usually | ||||||
/// from the parent expression) before extracting a snippet. This allows getting the call to a macro | ||||||
/// rather than the expression from expanding the macro. e.g. In the expression `&vec![]` taking a | ||||||
/// snippet of the chile of the borrow expression will get a snippet of what `vec![]` expands in to. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Did you mean child here? Otherwise, I'm not sure what chile is referring to :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I swear I know how to type. |
||||||
/// With the target context set to the same as the borrow expression, this will get a snippet of the | ||||||
/// call to the macro. | ||||||
/// | ||||||
/// The applicability will be modified in two ways: | ||||||
/// * If a snippet can't be extracted it will be changed from `MachineApplicable` or | ||||||
/// `MaybeIncorrect` to `HasPlaceholders`. | ||||||
/// * If the snippet is taken from a macro expansion then it will be changed from | ||||||
/// `MachineApplicable` to `MaybeIncorrect`. | ||||||
pub fn snippet_expr( | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The tests don't have any instance where the parenthesis are added by this function. Can you please add some test cases to ensure that this works correctly. |
||||||
cx: &LateContext<'_>, | ||||||
expr: &Expr<'_>, | ||||||
target_position: ExprPosition, | ||||||
ctxt: SyntaxContext, | ||||||
app: &mut Applicability, | ||||||
) -> String { | ||||||
let (snip, is_mac_call) = snippet_with_context(cx, expr.span, ctxt, "(..)", app); | ||||||
|
||||||
match snip { | ||||||
Cow::Borrowed(snip) => snip.to_owned(), | ||||||
Cow::Owned(snip) if is_mac_call => snip, | ||||||
Cow::Owned(mut snip) => { | ||||||
let ctxt = expr.span.ctxt(); | ||||||
|
||||||
// Attempt to determine if parenthesis are needed base on the target position. The snippet may have | ||||||
// parenthesis already, so attempt to find those. | ||||||
// TODO: Remove parenthesis if they aren't needed at the target position. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can create an issue for this enhancement, once the PR is merged 🙃 |
||||||
let needs_paren = match expr.peel_drop_temps().kind { | ||||||
ExprKind::Binary(_, lhs, rhs) | ||||||
if (ctxt == lhs.span.ctxt() && expr.span.lo() != lhs.span.lo()) | ||||||
|| (ctxt == rhs.span.ctxt() && expr.span.hi() != rhs.span.hi()) => | ||||||
xFrednet marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
{ | ||||||
false | ||||||
}, | ||||||
ExprKind::Binary(op, ..) => match op.node { | ||||||
BinOpKind::Add | BinOpKind::Sub => target_position > ExprPosition::AddLhs, | ||||||
BinOpKind::Mul | BinOpKind::Div | BinOpKind::Rem => target_position > ExprPosition::MulLhs, | ||||||
BinOpKind::And => target_position > ExprPosition::AndLhs, | ||||||
BinOpKind::Or => target_position > ExprPosition::OrLhs, | ||||||
BinOpKind::BitXor => target_position > ExprPosition::BitXorLhs, | ||||||
BinOpKind::BitAnd => target_position > ExprPosition::BitAndLhs, | ||||||
BinOpKind::BitOr => target_position > ExprPosition::BitOrLhs, | ||||||
BinOpKind::Shl | BinOpKind::Shr => target_position > ExprPosition::ShiftLhs, | ||||||
BinOpKind::Eq | BinOpKind::Lt | BinOpKind::Le | BinOpKind::Ne | BinOpKind::Gt | BinOpKind::Ge => { | ||||||
target_position > ExprPosition::EqLhs | ||||||
}, | ||||||
}, | ||||||
ExprKind::Box(..) | ExprKind::Unary(..) | ExprKind::AddrOf(..) if snip.starts_with('(') => false, | ||||||
ExprKind::Box(..) | ExprKind::Unary(..) | ExprKind::AddrOf(..) => { | ||||||
target_position > ExprPosition::Prefix | ||||||
}, | ||||||
ExprKind::Let(..) if snip.starts_with('(') => false, | ||||||
ExprKind::Let(..) => target_position > ExprPosition::Let, | ||||||
ExprKind::Cast(lhs, rhs) | ||||||
if (ctxt == lhs.span.ctxt() && expr.span.lo() != lhs.span.lo()) | ||||||
|| (ctxt == rhs.span.ctxt() && expr.span.hi() != rhs.span.hi()) => | ||||||
{ | ||||||
false | ||||||
}, | ||||||
Comment on lines
+403
to
+408
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. NIT: This can be combined with the first case ( |
||||||
ExprKind::Cast(..) | ExprKind::Type(..) => target_position > ExprPosition::Cast, | ||||||
|
||||||
ExprKind::Closure(..) | ||||||
| ExprKind::Break(..) | ||||||
| ExprKind::Ret(..) | ||||||
| ExprKind::Yield(..) | ||||||
| ExprKind::Assign(..) | ||||||
| ExprKind::AssignOp(..) => target_position > ExprPosition::AssignmentRhs, | ||||||
|
||||||
// Postfix operators, or expression with braces of some form | ||||||
ExprKind::Array(_) | ||||||
| ExprKind::Call(..) | ||||||
| ExprKind::ConstBlock(_) | ||||||
| ExprKind::MethodCall(..) | ||||||
| ExprKind::Tup(..) | ||||||
| ExprKind::Lit(..) | ||||||
| ExprKind::DropTemps(_) | ||||||
| ExprKind::If(..) | ||||||
| ExprKind::Loop(..) | ||||||
| ExprKind::Match(..) | ||||||
| ExprKind::Block(..) | ||||||
| ExprKind::Field(..) | ||||||
| ExprKind::Index(..) | ||||||
| ExprKind::Path(_) | ||||||
| ExprKind::Continue(_) | ||||||
| ExprKind::InlineAsm(_) | ||||||
| ExprKind::LlvmInlineAsm(_) | ||||||
| ExprKind::Struct(..) | ||||||
| ExprKind::Repeat(..) | ||||||
| ExprKind::Err => false, | ||||||
}; | ||||||
|
||||||
if needs_paren { | ||||||
snip.insert(0, '('); | ||||||
snip.push(')'); | ||||||
} | ||||||
snip | ||||||
}, | ||||||
} | ||||||
} | ||||||
|
||||||
/// Gets which position the expression is in relative to it's parent. Defaults to `Paren` if the | ||||||
/// parent node is not an expression. | ||||||
pub fn position_of_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> ExprPosition { | ||||||
match get_parent_expr(cx, expr) { | ||||||
None => ExprPosition::Paren, | ||||||
Some(parent) => match parent.kind { | ||||||
ExprKind::DropTemps(_) => position_of_expr(cx, parent), | ||||||
ExprKind::Binary(op, lhs, _) => match (op.node, expr.hir_id == lhs.hir_id) { | ||||||
(BinOpKind::Add | BinOpKind::Sub, true) => ExprPosition::AddLhs, | ||||||
(BinOpKind::Add | BinOpKind::Sub, false) => ExprPosition::AddRhs, | ||||||
(BinOpKind::Mul | BinOpKind::Div | BinOpKind::Rem, true) => ExprPosition::MulLhs, | ||||||
(BinOpKind::Mul | BinOpKind::Div | BinOpKind::Rem, false) => ExprPosition::MulRhs, | ||||||
(BinOpKind::And, true) => ExprPosition::AndLhs, | ||||||
(BinOpKind::And, false) => ExprPosition::AndRhs, | ||||||
(BinOpKind::Or, true) => ExprPosition::OrLhs, | ||||||
(BinOpKind::Or, false) => ExprPosition::OrRhs, | ||||||
(BinOpKind::BitXor, true) => ExprPosition::BitXorLhs, | ||||||
(BinOpKind::BitXor, false) => ExprPosition::BitXorRhs, | ||||||
(BinOpKind::BitAnd, true) => ExprPosition::BitAndLhs, | ||||||
(BinOpKind::BitAnd, false) => ExprPosition::BitAndRhs, | ||||||
(BinOpKind::BitOr, true) => ExprPosition::BitOrLhs, | ||||||
(BinOpKind::BitOr, false) => ExprPosition::BitOrRhs, | ||||||
(BinOpKind::Shl | BinOpKind::Shr, true) => ExprPosition::ShiftLhs, | ||||||
(BinOpKind::Shl | BinOpKind::Shr, false) => ExprPosition::ShiftRhs, | ||||||
( | ||||||
BinOpKind::Eq | BinOpKind::Lt | BinOpKind::Le | BinOpKind::Ne | BinOpKind::Gt | BinOpKind::Ge, | ||||||
true, | ||||||
) => ExprPosition::EqLhs, | ||||||
( | ||||||
BinOpKind::Eq | BinOpKind::Lt | BinOpKind::Le | BinOpKind::Ne | BinOpKind::Gt | BinOpKind::Ge, | ||||||
false, | ||||||
) => ExprPosition::EqRhs, | ||||||
}, | ||||||
ExprKind::Unary(..) | ExprKind::AddrOf(..) => ExprPosition::Prefix, | ||||||
ExprKind::Cast(..) | ExprKind::Type(..) => ExprPosition::Cast, | ||||||
ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == expr.hir_id => { | ||||||
ExprPosition::AssignmentLhs | ||||||
}, | ||||||
ExprKind::Assign(..) | ExprKind::AssignOp(..) => ExprPosition::AssignmentRhs, | ||||||
ExprKind::Call(e, _) | ExprKind::MethodCall(_, _, [e, ..], _) | ExprKind::Index(e, _) | ||||||
if expr.hir_id == e.hir_id => | ||||||
{ | ||||||
ExprPosition::Postfix | ||||||
}, | ||||||
ExprKind::Match(_, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar) | ExprKind::Field(..) => { | ||||||
ExprPosition::Postfix | ||||||
}, | ||||||
_ => ExprPosition::Paren, | ||||||
}, | ||||||
} | ||||||
} | ||||||
|
||||||
/// Walks the span up to the target context, thereby returning the macro call site if the span is | ||||||
/// inside a macro expansion, or the original span if it is not. Note this will return `None` in the | ||||||
/// case of the span being in a macro expansion, but the target context is from expanding a macro | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
// run-rustfix | ||
#![allow(unused)] | ||
#![warn(clippy::redundant_slicing)] | ||
|
||
fn main() { | ||
let slice: &[u32] = &[0]; | ||
let _ = slice; | ||
|
||
let v = vec![0]; | ||
let _ = &v[..]; // Changes the type | ||
let _ = (&v[..]); // Outer borrow is redundant | ||
|
||
static S: &[u8] = &[0, 1, 2]; | ||
let err = &mut &*S; // Should reborrow instead of slice | ||
|
||
let mut vec = vec![0]; | ||
let mut_slice = &mut *vec; | ||
let _ = &mut *mut_slice; // Should reborrow instead of slice | ||
|
||
macro_rules! m { | ||
($e:expr) => { | ||
$e | ||
}; | ||
} | ||
let _ = slice; | ||
|
||
macro_rules! m2 { | ||
($e:expr) => { | ||
&$e[..] | ||
}; | ||
} | ||
let _ = m2!(slice); // Don't lint in a macro | ||
|
||
trait T { | ||
fn f(&mut self); | ||
} | ||
impl T for &'_ [u32] { | ||
fn f(&mut self) {} | ||
} | ||
|
||
// Reborrow, `f` takes a mutable reference to the slice | ||
(&*slice).f(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,34 +1,40 @@ | ||
error: redundant slicing of the whole range | ||
--> $DIR/redundant_slicing.rs:6:13 | ||
--> $DIR/redundant_slicing.rs:7:13 | ||
| | ||
LL | let _ = &slice[..]; | ||
| ^^^^^^^^^^ help: use the original value instead: `slice` | ||
| | ||
= note: `-D clippy::redundant-slicing` implied by `-D warnings` | ||
|
||
error: redundant slicing of the whole range | ||
--> $DIR/redundant_slicing.rs:10:13 | ||
--> $DIR/redundant_slicing.rs:11:13 | ||
| | ||
LL | let _ = &(&v[..])[..]; // Outer borrow is redundant | ||
| ^^^^^^^^^^^^^ help: use the original value instead: `(&v[..])` | ||
|
||
error: redundant slicing of the whole range | ||
--> $DIR/redundant_slicing.rs:13:20 | ||
--> $DIR/redundant_slicing.rs:14:20 | ||
| | ||
LL | let err = &mut &S[..]; // Should reborrow instead of slice | ||
| ^^^^^^ help: reborrow the original value instead: `&*S` | ||
|
||
error: redundant slicing of the whole range | ||
--> $DIR/redundant_slicing.rs:17:13 | ||
--> $DIR/redundant_slicing.rs:18:13 | ||
| | ||
LL | let _ = &mut mut_slice[..]; // Should reborrow instead of slice | ||
| ^^^^^^^^^^^^^^^^^^ help: reborrow the original value instead: `&mut *mut_slice` | ||
|
||
error: redundant slicing of the whole range | ||
--> $DIR/redundant_slicing.rs:24:13 | ||
--> $DIR/redundant_slicing.rs:25:13 | ||
| | ||
LL | let _ = &m!(slice)[..]; | ||
| ^^^^^^^^^^^^^^ help: use the original value instead: `slice` | ||
|
||
error: aborting due to 5 previous errors | ||
error: redundant slicing of the whole range | ||
--> $DIR/redundant_slicing.rs:42:5 | ||
| | ||
LL | (&slice[..]).f(); | ||
| ^^^^^^^^^^^^ help: reborrow the original value instead: `(&*slice)` | ||
|
||
error: aborting due to 6 previous errors | ||
|
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Really, nit picky, but I thought I mention it since, there are some other adjustments/comments :)