]>
Commit | Line | Data |
---|---|---|
cdc7bbd5 | 1 | use clippy_utils::diagnostics::span_lint_and_sugg; |
a2a8927a | 2 | use clippy_utils::get_parent_expr; |
cdc7bbd5 | 3 | use clippy_utils::source::snippet_with_context; |
5e7ed085 | 4 | use clippy_utils::ty::{is_type_lang_item, peel_mid_ty_refs}; |
f20569fa | 5 | use if_chain::if_chain; |
5e7ed085 | 6 | use rustc_ast::util::parser::PREC_PREFIX; |
f20569fa | 7 | use rustc_errors::Applicability; |
cdc7bbd5 | 8 | use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability}; |
5e7ed085 | 9 | use rustc_lint::{LateContext, LateLintPass, Lint}; |
fe692bf9 | 10 | use rustc_middle::ty::Ty; |
5e7ed085 FG |
11 | use rustc_middle::ty::adjustment::{Adjust, AutoBorrow, AutoBorrowMutability}; |
12 | use rustc_middle::ty::subst::GenericArg; | |
f20569fa XL |
13 | use rustc_session::{declare_lint_pass, declare_tool_lint}; |
14 | ||
f20569fa | 15 | declare_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 |
46 | declare_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 | ||
70 | declare_lint_pass!(RedundantSlicing => [REDUNDANT_SLICING, DEREF_BY_SLICING]); | |
71 | ||
72 | static REDUNDANT_SLICING_LINT: (&Lint, &str) = (REDUNDANT_SLICING, "redundant slicing of the whole range"); | |
73 | static DEREF_BY_SLICING_LINT: (&Lint, &str) = (DEREF_BY_SLICING, "slicing when dereferencing would work"); | |
f20569fa | 74 | |
5099ac24 | 75 | impl<'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 | } |