1 use clippy_utils
::diagnostics
::span_lint_and_sugg
;
2 use clippy_utils
::get_parent_expr
;
3 use clippy_utils
::source
::snippet_with_context
;
4 use clippy_utils
::ty
::{is_type_lang_item, peel_mid_ty_refs}
;
5 use rustc_ast
::util
::parser
::PREC_PREFIX
;
6 use rustc_errors
::Applicability
;
7 use rustc_hir
::{BorrowKind, Expr, ExprKind, LangItem, Mutability}
;
8 use rustc_lint
::{LateContext, LateLintPass, Lint}
;
9 use rustc_middle
::ty
::adjustment
::{Adjust, AutoBorrow, AutoBorrowMutability}
;
10 use rustc_middle
::ty
::{GenericArg, Ty}
;
11 use rustc_session
::declare_lint_pass
;
13 declare_clippy_lint
! {
15 /// Checks for redundant slicing expressions which use the full range, and
16 /// do not change the type.
18 /// ### Why is this bad?
19 /// It unnecessarily adds complexity to the expression.
21 /// ### Known problems
22 /// If the type being sliced has an implementation of `Index<RangeFull>`
23 /// that actually changes anything then it can't be removed. However, this would be surprising
24 /// to people reading the code and should have a note with it.
28 /// fn get_slice(x: &[u32]) -> &[u32] {
34 /// fn get_slice(x: &[u32]) -> &[u32] {
38 #[clippy::version = "1.51.0"]
39 pub REDUNDANT_SLICING
,
41 "redundant slicing of the whole range of a type"
44 declare_clippy_lint
! {
46 /// Checks for slicing expressions which are equivalent to dereferencing the
49 /// ### Why is this bad?
50 /// Some people may prefer to dereference rather than slice.
54 /// let vec = vec![1, 2, 3];
55 /// let slice = &vec[..];
59 /// let vec = vec![1, 2, 3];
60 /// let slice = &*vec;
62 #[clippy::version = "1.61.0"]
65 "slicing instead of dereferencing"
68 declare_lint_pass
!(RedundantSlicing
=> [REDUNDANT_SLICING
, DEREF_BY_SLICING
]);
70 static REDUNDANT_SLICING_LINT
: (&Lint
, &str) = (REDUNDANT_SLICING
, "redundant slicing of the whole range");
71 static DEREF_BY_SLICING_LINT
: (&Lint
, &str) = (DEREF_BY_SLICING
, "slicing when dereferencing would work");
73 impl<'tcx
> LateLintPass
<'tcx
> for RedundantSlicing
{
74 fn check_expr(&mut self, cx
: &LateContext
<'tcx
>, expr
: &'tcx Expr
<'_
>) {
75 if expr
.span
.from_expansion() {
79 let ctxt
= expr
.span
.ctxt();
80 if let ExprKind
::AddrOf(BorrowKind
::Ref
, mutability
, addressee
) = expr
.kind
81 && addressee
.span
.ctxt() == ctxt
82 && let ExprKind
::Index(indexed
, range
, _
) = addressee
.kind
83 && is_type_lang_item(cx
, cx
.typeck_results().expr_ty_adjusted(range
), LangItem
::RangeFull
)
85 let (expr_ty
, expr_ref_count
) = peel_mid_ty_refs(cx
.typeck_results().expr_ty(expr
));
86 let (indexed_ty
, indexed_ref_count
) = peel_mid_ty_refs(cx
.typeck_results().expr_ty(indexed
));
87 let parent_expr
= get_parent_expr(cx
, expr
);
88 let needs_parens_for_prefix
= parent_expr
.map_or(false, |parent
| parent
.precedence().order() > PREC_PREFIX
);
89 let mut app
= Applicability
::MachineApplicable
;
91 let ((lint
, msg
), help
, sugg
) = if expr_ty
== indexed_ty
{
92 if expr_ref_count
> indexed_ref_count
{
93 // Indexing takes self by reference and can't return a reference to that
94 // reference as it's a local variable. The only way this could happen is if
95 // `self` contains a reference to the `Self` type. If this occurs then the
96 // lint no longer applies as it's essentially a field access, which is not
100 let deref_count
= indexed_ref_count
- expr_ref_count
;
102 let (lint
, reborrow_str
, help_str
) = if mutability
== Mutability
::Mut
{
103 // The slice was used to reborrow the mutable reference.
104 (DEREF_BY_SLICING_LINT
, "&mut *", "reborrow the original value instead")
108 kind
: ExprKind
::AddrOf(BorrowKind
::Ref
, Mutability
::Mut
, _
),
111 ) || cx
.typeck_results().expr_adjustments(expr
).first().map_or(false, |a
| {
114 Adjust
::Borrow(AutoBorrow
::Ref(_
, AutoBorrowMutability
::Mut { .. }
))
117 // The slice was used to make a temporary reference.
118 (DEREF_BY_SLICING_LINT
, "&*", "reborrow the original value instead")
119 } else if deref_count
!= 0 {
120 (DEREF_BY_SLICING_LINT
, "", "dereference the original value instead")
122 (REDUNDANT_SLICING_LINT
, "", "use the original value instead")
125 let snip
= snippet_with_context(cx
, indexed
.span
, ctxt
, "..", &mut app
).0;
126 let sugg
= if (deref_count
!= 0 || !reborrow_str
.is_empty()) && needs_parens_for_prefix
{
127 format
!("({reborrow_str}{}{snip})", "*".repeat(deref_count
))
129 format
!("{reborrow_str}{}{snip}", "*".repeat(deref_count
))
132 (lint
, help_str
, sugg
)
133 } else if let Some(target_id
) = cx
.tcx
.lang_items().deref_target() {
134 if let Ok(deref_ty
) = cx
.tcx
.try_normalize_erasing_regions(
136 Ty
::new_projection(cx
.tcx
, target_id
, cx
.tcx
.mk_args(&[GenericArg
::from(indexed_ty
)])),
138 if deref_ty
== expr_ty
{
139 let snip
= snippet_with_context(cx
, indexed
.span
, ctxt
, "..", &mut app
).0;
140 let sugg
= if needs_parens_for_prefix
{
141 format
!("(&{}{}*{snip})", mutability
.prefix_str(), "*".repeat(indexed_ref_count
))
143 format
!("&{}{}*{snip}", mutability
.prefix_str(), "*".repeat(indexed_ref_count
))
145 (DEREF_BY_SLICING_LINT
, "dereference the original value instead", sugg
)
156 span_lint_and_sugg(cx
, lint
, expr
.span
, msg
, help
, sugg
, app
);