]>
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}; |
5e7ed085 | 5 | use rustc_ast::util::parser::PREC_PREFIX; |
f20569fa | 6 | use rustc_errors::Applicability; |
cdc7bbd5 | 7 | use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability}; |
5e7ed085 FG |
8 | use rustc_lint::{LateContext, LateLintPass, Lint}; |
9 | use rustc_middle::ty::adjustment::{Adjust, AutoBorrow, AutoBorrowMutability}; | |
add651ee | 10 | use rustc_middle::ty::{GenericArg, Ty}; |
4b012472 | 11 | use rustc_session::declare_lint_pass; |
f20569fa | 12 | |
f20569fa | 13 | declare_clippy_lint! { |
94222f64 XL |
14 | /// ### What it does |
15 | /// Checks for redundant slicing expressions which use the full range, and | |
f20569fa XL |
16 | /// do not change the type. |
17 | /// | |
94222f64 XL |
18 | /// ### Why is this bad? |
19 | /// It unnecessarily adds complexity to the expression. | |
f20569fa | 20 | /// |
94222f64 XL |
21 | /// ### Known problems |
22 | /// If the type being sliced has an implementation of `Index<RangeFull>` | |
f20569fa XL |
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. | |
25 | /// | |
94222f64 | 26 | /// ### Example |
f20569fa XL |
27 | /// ```ignore |
28 | /// fn get_slice(x: &[u32]) -> &[u32] { | |
29 | /// &x[..] | |
30 | /// } | |
31 | /// ``` | |
32 | /// Use instead: | |
33 | /// ```ignore | |
34 | /// fn get_slice(x: &[u32]) -> &[u32] { | |
35 | /// x | |
36 | /// } | |
37 | /// ``` | |
a2a8927a | 38 | #[clippy::version = "1.51.0"] |
f20569fa XL |
39 | pub REDUNDANT_SLICING, |
40 | complexity, | |
41 | "redundant slicing of the whole range of a type" | |
42 | } | |
43 | ||
5e7ed085 FG |
44 | declare_clippy_lint! { |
45 | /// ### What it does | |
46 | /// Checks for slicing expressions which are equivalent to dereferencing the | |
47 | /// value. | |
48 | /// | |
31ef2f64 | 49 | /// ### Why restrict this? |
5e7ed085 FG |
50 | /// Some people may prefer to dereference rather than slice. |
51 | /// | |
52 | /// ### Example | |
ed00b5ec | 53 | /// ```no_run |
5e7ed085 FG |
54 | /// let vec = vec![1, 2, 3]; |
55 | /// let slice = &vec[..]; | |
56 | /// ``` | |
57 | /// Use instead: | |
ed00b5ec | 58 | /// ```no_run |
5e7ed085 FG |
59 | /// let vec = vec![1, 2, 3]; |
60 | /// let slice = &*vec; | |
61 | /// ``` | |
923072b8 | 62 | #[clippy::version = "1.61.0"] |
5e7ed085 FG |
63 | pub DEREF_BY_SLICING, |
64 | restriction, | |
65 | "slicing instead of dereferencing" | |
66 | } | |
67 | ||
68 | declare_lint_pass!(RedundantSlicing => [REDUNDANT_SLICING, DEREF_BY_SLICING]); | |
69 | ||
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"); | |
f20569fa | 72 | |
5099ac24 | 73 | impl<'tcx> LateLintPass<'tcx> for RedundantSlicing { |
f20569fa | 74 | fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { |
a2a8927a | 75 | if expr.span.from_expansion() { |
f20569fa XL |
76 | return; |
77 | } | |
78 | ||
cdc7bbd5 | 79 | let ctxt = expr.span.ctxt(); |
4b012472 FG |
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) | |
84 | { | |
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; | |
cdc7bbd5 | 90 | |
4b012472 FG |
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 | |
97 | // redundant. | |
98 | return; | |
99 | } | |
100 | let deref_count = indexed_ref_count - expr_ref_count; | |
5e7ed085 | 101 | |
4b012472 FG |
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") | |
105 | } else if matches!( | |
106 | parent_expr, | |
107 | Some(Expr { | |
108 | kind: ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, _), | |
109 | .. | |
110 | }) | |
111 | ) || cx.typeck_results().expr_adjustments(expr).first().map_or(false, |a| { | |
112 | matches!( | |
113 | a.kind, | |
114 | Adjust::Borrow(AutoBorrow::Ref(_, AutoBorrowMutability::Mut { .. })) | |
115 | ) | |
116 | }) { | |
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") | |
121 | } else { | |
122 | (REDUNDANT_SLICING_LINT, "", "use the original value instead") | |
123 | }; | |
5e7ed085 | 124 | |
4b012472 FG |
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)) | |
128 | } else { | |
129 | format!("{reborrow_str}{}{snip}", "*".repeat(deref_count)) | |
130 | }; | |
5e7ed085 | 131 | |
4b012472 FG |
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( | |
135 | cx.param_env, | |
136 | Ty::new_projection(cx.tcx, target_id, cx.tcx.mk_args(&[GenericArg::from(indexed_ty)])), | |
137 | ) { | |
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)) | |
5e7ed085 | 142 | } else { |
4b012472 FG |
143 | format!("&{}{}*{snip}", mutability.prefix_str(), "*".repeat(indexed_ref_count)) |
144 | }; | |
145 | (DEREF_BY_SLICING_LINT, "dereference the original value instead", sugg) | |
5e7ed085 FG |
146 | } else { |
147 | return; | |
148 | } | |
cdc7bbd5 | 149 | } else { |
5e7ed085 | 150 | return; |
4b012472 FG |
151 | } |
152 | } else { | |
153 | return; | |
154 | }; | |
f20569fa | 155 | |
4b012472 | 156 | span_lint_and_sugg(cx, lint, expr.span, msg, help, sugg, app); |
f20569fa XL |
157 | } |
158 | } | |
159 | } |