]> git.proxmox.com Git - rustc.git/blame - src/tools/clippy/clippy_lints/src/redundant_slicing.rs
New upstream version 1.72.1+dfsg1
[rustc.git] / src / tools / clippy / clippy_lints / src / redundant_slicing.rs
CommitLineData
cdc7bbd5 1use clippy_utils::diagnostics::span_lint_and_sugg;
a2a8927a 2use clippy_utils::get_parent_expr;
cdc7bbd5 3use clippy_utils::source::snippet_with_context;
5e7ed085 4use clippy_utils::ty::{is_type_lang_item, peel_mid_ty_refs};
f20569fa 5use if_chain::if_chain;
5e7ed085 6use rustc_ast::util::parser::PREC_PREFIX;
f20569fa 7use rustc_errors::Applicability;
cdc7bbd5 8use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability};
5e7ed085 9use rustc_lint::{LateContext, LateLintPass, Lint};
fe692bf9 10use rustc_middle::ty::Ty;
5e7ed085
FG
11use rustc_middle::ty::adjustment::{Adjust, AutoBorrow, AutoBorrowMutability};
12use rustc_middle::ty::subst::GenericArg;
f20569fa
XL
13use rustc_session::{declare_lint_pass, declare_tool_lint};
14
f20569fa 15declare_clippy_lint! {
94222f64
XL
16 /// ### What it does
17 /// Checks for redundant slicing expressions which use the full range, and
f20569fa
XL
18 /// do not change the type.
19 ///
94222f64
XL
20 /// ### Why is this bad?
21 /// It unnecessarily adds complexity to the expression.
f20569fa 22 ///
94222f64
XL
23 /// ### Known problems
24 /// If the type being sliced has an implementation of `Index<RangeFull>`
f20569fa
XL
25 /// that actually changes anything then it can't be removed. However, this would be surprising
26 /// to people reading the code and should have a note with it.
27 ///
94222f64 28 /// ### Example
f20569fa
XL
29 /// ```ignore
30 /// fn get_slice(x: &[u32]) -> &[u32] {
31 /// &x[..]
32 /// }
33 /// ```
34 /// Use instead:
35 /// ```ignore
36 /// fn get_slice(x: &[u32]) -> &[u32] {
37 /// x
38 /// }
39 /// ```
a2a8927a 40 #[clippy::version = "1.51.0"]
f20569fa
XL
41 pub REDUNDANT_SLICING,
42 complexity,
43 "redundant slicing of the whole range of a type"
44}
45
5e7ed085
FG
46declare_clippy_lint! {
47 /// ### What it does
48 /// Checks for slicing expressions which are equivalent to dereferencing the
49 /// value.
50 ///
51 /// ### Why is this bad?
52 /// Some people may prefer to dereference rather than slice.
53 ///
54 /// ### Example
55 /// ```rust
56 /// let vec = vec![1, 2, 3];
57 /// let slice = &vec[..];
58 /// ```
59 /// Use instead:
60 /// ```rust
61 /// let vec = vec![1, 2, 3];
62 /// let slice = &*vec;
63 /// ```
923072b8 64 #[clippy::version = "1.61.0"]
5e7ed085
FG
65 pub DEREF_BY_SLICING,
66 restriction,
67 "slicing instead of dereferencing"
68}
69
70declare_lint_pass!(RedundantSlicing => [REDUNDANT_SLICING, DEREF_BY_SLICING]);
71
72static REDUNDANT_SLICING_LINT: (&Lint, &str) = (REDUNDANT_SLICING, "redundant slicing of the whole range");
73static DEREF_BY_SLICING_LINT: (&Lint, &str) = (DEREF_BY_SLICING, "slicing when dereferencing would work");
f20569fa 74
5099ac24 75impl<'tcx> LateLintPass<'tcx> for RedundantSlicing {
f20569fa 76 fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
a2a8927a 77 if expr.span.from_expansion() {
f20569fa
XL
78 return;
79 }
80
cdc7bbd5 81 let ctxt = expr.span.ctxt();
f20569fa 82 if_chain! {
cdc7bbd5
XL
83 if let ExprKind::AddrOf(BorrowKind::Ref, mutability, addressee) = expr.kind;
84 if addressee.span.ctxt() == ctxt;
f20569fa
XL
85 if let ExprKind::Index(indexed, range) = addressee.kind;
86 if is_type_lang_item(cx, cx.typeck_results().expr_ty_adjusted(range), LangItem::RangeFull);
f20569fa 87 then {
5e7ed085
FG
88 let (expr_ty, expr_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(expr));
89 let (indexed_ty, indexed_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(indexed));
90 let parent_expr = get_parent_expr(cx, expr);
91 let needs_parens_for_prefix = parent_expr.map_or(false, |parent| {
92 parent.precedence().order() > PREC_PREFIX
93 });
f20569fa 94 let mut app = Applicability::MachineApplicable;
cdc7bbd5 95
5e7ed085
FG
96 let ((lint, msg), help, sugg) = if expr_ty == indexed_ty {
97 if expr_ref_count > indexed_ref_count {
98 // Indexing takes self by reference and can't return a reference to that
99 // reference as it's a local variable. The only way this could happen is if
100 // `self` contains a reference to the `Self` type. If this occurs then the
101 // lint no longer applies as it's essentially a field access, which is not
102 // redundant.
103 return;
104 }
105 let deref_count = indexed_ref_count - expr_ref_count;
106
107 let (lint, reborrow_str, help_str) = if mutability == Mutability::Mut {
108 // The slice was used to reborrow the mutable reference.
109 (DEREF_BY_SLICING_LINT, "&mut *", "reborrow the original value instead")
110 } else if matches!(
111 parent_expr,
112 Some(Expr {
113 kind: ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, _),
114 ..
115 })
116 ) || cx.typeck_results().expr_adjustments(expr).first().map_or(false, |a| {
117 matches!(a.kind, Adjust::Borrow(AutoBorrow::Ref(_, AutoBorrowMutability::Mut { .. })))
118 }) {
119 // The slice was used to make a temporary reference.
120 (DEREF_BY_SLICING_LINT, "&*", "reborrow the original value instead")
121 } else if deref_count != 0 {
122 (DEREF_BY_SLICING_LINT, "", "dereference the original value instead")
123 } else {
124 (REDUNDANT_SLICING_LINT, "", "use the original value instead")
125 };
126
127 let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0;
128 let sugg = if (deref_count != 0 || !reborrow_str.is_empty()) && needs_parens_for_prefix {
2b03887a 129 format!("({reborrow_str}{}{snip})", "*".repeat(deref_count))
5e7ed085 130 } else {
2b03887a 131 format!("{reborrow_str}{}{snip}", "*".repeat(deref_count))
5e7ed085
FG
132 };
133
134 (lint, help_str, sugg)
135 } else if let Some(target_id) = cx.tcx.lang_items().deref_target() {
136 if let Ok(deref_ty) = cx.tcx.try_normalize_erasing_regions(
137 cx.param_env,
fe692bf9 138 Ty::new_projection(cx.tcx,target_id, cx.tcx.mk_substs(&[GenericArg::from(indexed_ty)])),
5e7ed085
FG
139 ) {
140 if deref_ty == expr_ty {
141 let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0;
142 let sugg = if needs_parens_for_prefix {
2b03887a 143 format!("(&{}{}*{snip})", mutability.prefix_str(), "*".repeat(indexed_ref_count))
5e7ed085 144 } else {
2b03887a 145 format!("&{}{}*{snip}", mutability.prefix_str(), "*".repeat(indexed_ref_count))
5e7ed085
FG
146 };
147 (DEREF_BY_SLICING_LINT, "dereference the original value instead", sugg)
148 } else {
149 return;
150 }
151 } else {
152 return;
153 }
cdc7bbd5 154 } else {
5e7ed085 155 return;
cdc7bbd5 156 };
f20569fa
XL
157
158 span_lint_and_sugg(
159 cx,
5e7ed085 160 lint,
f20569fa 161 expr.span,
5e7ed085
FG
162 msg,
163 help,
164 sugg,
f20569fa
XL
165 app,
166 );
167 }
168 }
169 }
170}