]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/dereference.rs
9645f1a03be88ffb59621fb65555197930c45683
[rust.git] / clippy_lints / src / dereference.rs
1 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
2 use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
3 use clippy_utils::ty::peel_mid_ty_refs;
4 use clippy_utils::{get_parent_expr, get_parent_node, is_lint_allowed, path_to_local};
5 use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX};
6 use rustc_data_structures::fx::FxIndexMap;
7 use rustc_errors::Applicability;
8 use rustc_hir::{
9     BindingAnnotation, Body, BodyId, BorrowKind, Destination, Expr, ExprKind, HirId, MatchSource, Mutability, Node,
10     Pat, PatKind, UnOp,
11 };
12 use rustc_lint::{LateContext, LateLintPass};
13 use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
14 use rustc_middle::ty::{self, Ty, TyCtxt, TyS, TypeckResults};
15 use rustc_session::{declare_tool_lint, impl_lint_pass};
16 use rustc_span::{symbol::sym, Span};
17
18 declare_clippy_lint! {
19     /// ### What it does
20     /// Checks for explicit `deref()` or `deref_mut()` method calls.
21     ///
22     /// ### Why is this bad?
23     /// Dereferencing by `&*x` or `&mut *x` is clearer and more concise,
24     /// when not part of a method chain.
25     ///
26     /// ### Example
27     /// ```rust
28     /// use std::ops::Deref;
29     /// let a: &mut String = &mut String::from("foo");
30     /// let b: &str = a.deref();
31     /// ```
32     /// Could be written as:
33     /// ```rust
34     /// let a: &mut String = &mut String::from("foo");
35     /// let b = &*a;
36     /// ```
37     ///
38     /// This lint excludes
39     /// ```rust,ignore
40     /// let _ = d.unwrap().deref();
41     /// ```
42     #[clippy::version = "1.44.0"]
43     pub EXPLICIT_DEREF_METHODS,
44     pedantic,
45     "Explicit use of deref or deref_mut method while not in a method chain."
46 }
47
48 declare_clippy_lint! {
49     /// ### What it does
50     /// Checks for address of operations (`&`) that are going to
51     /// be dereferenced immediately by the compiler.
52     ///
53     /// ### Why is this bad?
54     /// Suggests that the receiver of the expression borrows
55     /// the expression.
56     ///
57     /// ### Example
58     /// ```rust
59     /// fn fun(_a: &i32) {}
60     ///
61     /// // Bad
62     /// let x: &i32 = &&&&&&5;
63     /// fun(&x);
64     ///
65     /// // Good
66     /// let x: &i32 = &5;
67     /// fun(x);
68     /// ```
69     #[clippy::version = "pre 1.29.0"]
70     pub NEEDLESS_BORROW,
71     style,
72     "taking a reference that is going to be automatically dereferenced"
73 }
74
75 declare_clippy_lint! {
76     /// ### What it does
77     /// Checks for `ref` bindings which create a reference to a reference.
78     ///
79     /// ### Why is this bad?
80     /// The address-of operator at the use site is clearer about the need for a reference.
81     ///
82     /// ### Example
83     /// ```rust
84     /// // Bad
85     /// let x = Some("");
86     /// if let Some(ref x) = x {
87     ///     // use `x` here
88     /// }
89     ///
90     /// // Good
91     /// let x = Some("");
92     /// if let Some(x) = x {
93     ///     // use `&x` here
94     /// }
95     /// ```
96     #[clippy::version = "1.54.0"]
97     pub REF_BINDING_TO_REFERENCE,
98     pedantic,
99     "`ref` binding to a reference"
100 }
101
102 impl_lint_pass!(Dereferencing => [
103     EXPLICIT_DEREF_METHODS,
104     NEEDLESS_BORROW,
105     REF_BINDING_TO_REFERENCE,
106 ]);
107
108 #[derive(Default)]
109 pub struct Dereferencing {
110     state: Option<(State, StateData)>,
111
112     // While parsing a `deref` method call in ufcs form, the path to the function is itself an
113     // expression. This is to store the id of that expression so it can be skipped when
114     // `check_expr` is called for it.
115     skip_expr: Option<HirId>,
116
117     /// The body the first local was found in. Used to emit lints when the traversal of the body has
118     /// been finished. Note we can't lint at the end of every body as they can be nested within each
119     /// other.
120     current_body: Option<BodyId>,
121     /// The list of locals currently being checked by the lint.
122     /// If the value is `None`, then the binding has been seen as a ref pattern, but is not linted.
123     /// This is needed for or patterns where one of the branches can be linted, but another can not
124     /// be.
125     ///
126     /// e.g. `m!(x) | Foo::Bar(ref x)`
127     ref_locals: FxIndexMap<HirId, Option<RefPat>>,
128 }
129
130 struct StateData {
131     /// Span of the top level expression
132     span: Span,
133     /// The required mutability
134     target_mut: Mutability,
135 }
136
137 enum State {
138     // Any number of deref method calls.
139     DerefMethod {
140         // The number of calls in a sequence which changed the referenced type
141         ty_changed_count: usize,
142         is_final_ufcs: bool,
143     },
144     DerefedBorrow {
145         count: u32,
146     },
147 }
148
149 // A reference operation considered by this lint pass
150 enum RefOp {
151     Method(Mutability),
152     Deref,
153     AddrOf,
154 }
155
156 struct RefPat {
157     /// Whether every usage of the binding is dereferenced.
158     always_deref: bool,
159     /// The spans of all the ref bindings for this local.
160     spans: Vec<Span>,
161     /// The applicability of this suggestion.
162     app: Applicability,
163     /// All the replacements which need to be made.
164     replacements: Vec<(Span, String)>,
165 }
166
167 impl<'tcx> LateLintPass<'tcx> for Dereferencing {
168     #[allow(clippy::too_many_lines)]
169     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
170         // Skip path expressions from deref calls. e.g. `Deref::deref(e)`
171         if Some(expr.hir_id) == self.skip_expr.take() {
172             return;
173         }
174
175         if let Some(local) = path_to_local(expr) {
176             self.check_local_usage(cx, expr, local);
177         }
178
179         // Stop processing sub expressions when a macro call is seen
180         if expr.span.from_expansion() {
181             if let Some((state, data)) = self.state.take() {
182                 report(cx, expr, state, data);
183             }
184             return;
185         }
186
187         let typeck = cx.typeck_results();
188         let (kind, sub_expr) = if let Some(x) = try_parse_ref_op(cx.tcx, typeck, expr) {
189             x
190         } else {
191             // The whole chain of reference operations has been seen
192             if let Some((state, data)) = self.state.take() {
193                 report(cx, expr, state, data);
194             }
195             return;
196         };
197
198         match (self.state.take(), kind) {
199             (None, kind) => {
200                 let parent = get_parent_node(cx.tcx, expr.hir_id);
201                 let expr_ty = typeck.expr_ty(expr);
202
203                 match kind {
204                     RefOp::Method(target_mut)
205                         if !is_lint_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id)
206                             && is_linted_explicit_deref_position(parent, expr.hir_id, expr.span) =>
207                     {
208                         self.state = Some((
209                             State::DerefMethod {
210                                 ty_changed_count: if deref_method_same_type(expr_ty, typeck.expr_ty(sub_expr)) {
211                                     0
212                                 } else {
213                                     1
214                                 },
215                                 is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)),
216                             },
217                             StateData {
218                                 span: expr.span,
219                                 target_mut,
220                             },
221                         ));
222                     },
223                     RefOp::AddrOf => {
224                         // Find the number of times the borrow is auto-derefed.
225                         let mut iter = find_adjustments(cx.tcx, typeck, expr).iter();
226                         if let Some((i, adjust)) = iter.by_ref().enumerate().find_map(|(i, adjust)| {
227                             if !matches!(adjust.kind, Adjust::Deref(_)) {
228                                 Some((i, Some(adjust)))
229                             } else if !adjust.target.is_ref() {
230                                 // Include the current deref.
231                                 Some((i + 1, None))
232                             } else {
233                                 None
234                             }
235                         }) {
236                             if i > 1 {
237                                 // If the next adjustment is a mutable borrow, then check to see if the compiler will
238                                 // insert a re-borrow here. If not, leave an extra borrow here to avoid attempting to
239                                 // move the a mutable reference.
240                                 let (i, target_mut) = if let Some(&Adjust::Borrow(AutoBorrow::Ref(_, mutability))) =
241                                     adjust.or_else(|| iter.next()).map(|a| &a.kind)
242                                 {
243                                     if matches!(mutability, AutoBorrowMutability::Mut { .. })
244                                         && !is_auto_reborrow_position(parent, expr.hir_id)
245                                     {
246                                         (i - 1, Mutability::Mut)
247                                     } else {
248                                         (i, mutability.into())
249                                     }
250                                 } else {
251                                     (
252                                         i,
253                                         iter.find_map(|adjust| match adjust.kind {
254                                             Adjust::Borrow(AutoBorrow::Ref(_, m)) => Some(m.into()),
255                                             _ => None,
256                                         })
257                                         // This default should never happen. Auto-deref always reborrows.
258                                         .unwrap_or(Mutability::Not),
259                                     )
260                                 };
261
262                                 if i > 1 {
263                                     self.state = Some((
264                                         // Subtract one for the current borrow expression, and one to cover the last
265                                         // reference which can't be removed (it's either reborrowed, or needed for
266                                         // auto-deref to happen).
267                                         State::DerefedBorrow {
268                                         count:
269                                             // Truncation here would require more than a `u32::MAX` level reference. The compiler
270                                             // does not support this.
271                                             #[allow(clippy::cast_possible_truncation)]
272                                             { i as u32 - 2 }
273                                     },
274                                         StateData {
275                                             span: expr.span,
276                                             target_mut,
277                                         },
278                                     ));
279                                 }
280                             }
281                         }
282                     },
283                     _ => (),
284                 }
285             },
286             (Some((State::DerefMethod { ty_changed_count, .. }, data)), RefOp::Method(_)) => {
287                 self.state = Some((
288                     State::DerefMethod {
289                         ty_changed_count: if deref_method_same_type(typeck.expr_ty(expr), typeck.expr_ty(sub_expr)) {
290                             ty_changed_count
291                         } else {
292                             ty_changed_count + 1
293                         },
294                         is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)),
295                     },
296                     data,
297                 ));
298             },
299             (Some((State::DerefedBorrow { count }, data)), RefOp::AddrOf) if count != 0 => {
300                 self.state = Some((State::DerefedBorrow { count: count - 1 }, data));
301             },
302
303             (Some((state, data)), _) => report(cx, expr, state, data),
304         }
305     }
306
307     fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
308         if let PatKind::Binding(BindingAnnotation::Ref, id, name, _) = pat.kind {
309             if let Some(opt_prev_pat) = self.ref_locals.get_mut(&id) {
310                 // This binding id has been seen before. Add this pattern to the list of changes.
311                 if let Some(prev_pat) = opt_prev_pat {
312                     if pat.span.from_expansion() {
313                         // Doesn't match the context of the previous pattern. Can't lint here.
314                         *opt_prev_pat = None;
315                     } else {
316                         prev_pat.spans.push(pat.span);
317                         prev_pat.replacements.push((
318                             pat.span,
319                             snippet_with_context(cx, name.span, pat.span.ctxt(), "..", &mut prev_pat.app)
320                                 .0
321                                 .into(),
322                         ));
323                     }
324                 }
325                 return;
326             }
327
328             if_chain! {
329                 if !pat.span.from_expansion();
330                 if let ty::Ref(_, tam, _) = *cx.typeck_results().pat_ty(pat).kind();
331                 // only lint immutable refs, because borrowed `&mut T` cannot be moved out
332                 if let ty::Ref(_, _, Mutability::Not) = *tam.kind();
333                 then {
334                     let mut app = Applicability::MachineApplicable;
335                     let snip = snippet_with_context(cx, name.span, pat.span.ctxt(), "..", &mut app).0;
336                     self.current_body = self.current_body.or(cx.enclosing_body);
337                     self.ref_locals.insert(
338                         id,
339                         Some(RefPat {
340                             always_deref: true,
341                             spans: vec![pat.span],
342                             app,
343                             replacements: vec![(pat.span, snip.into())],
344                         }),
345                     );
346                 }
347             }
348         }
349     }
350
351     fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
352         if Some(body.id()) == self.current_body {
353             for pat in self.ref_locals.drain(..).filter_map(|(_, x)| x) {
354                 let replacements = pat.replacements;
355                 let app = pat.app;
356                 span_lint_and_then(
357                     cx,
358                     if pat.always_deref {
359                         NEEDLESS_BORROW
360                     } else {
361                         REF_BINDING_TO_REFERENCE
362                     },
363                     pat.spans,
364                     "this pattern creates a reference to a reference",
365                     |diag| {
366                         diag.multipart_suggestion("try this", replacements, app);
367                     },
368                 );
369             }
370             self.current_body = None;
371         }
372     }
373 }
374
375 fn try_parse_ref_op<'tcx>(
376     tcx: TyCtxt<'tcx>,
377     typeck: &'tcx TypeckResults<'_>,
378     expr: &'tcx Expr<'_>,
379 ) -> Option<(RefOp, &'tcx Expr<'tcx>)> {
380     let (def_id, arg) = match expr.kind {
381         ExprKind::MethodCall(_, _, [arg], _) => (typeck.type_dependent_def_id(expr.hir_id)?, arg),
382         ExprKind::Call(
383             Expr {
384                 kind: ExprKind::Path(path),
385                 hir_id,
386                 ..
387             },
388             [arg],
389         ) => (typeck.qpath_res(path, *hir_id).opt_def_id()?, arg),
390         ExprKind::Unary(UnOp::Deref, sub_expr) if !typeck.expr_ty(sub_expr).is_unsafe_ptr() => {
391             return Some((RefOp::Deref, sub_expr));
392         },
393         ExprKind::AddrOf(BorrowKind::Ref, _, sub_expr) => return Some((RefOp::AddrOf, sub_expr)),
394         _ => return None,
395     };
396     if tcx.is_diagnostic_item(sym::deref_method, def_id) {
397         Some((RefOp::Method(Mutability::Not), arg))
398     } else if tcx.trait_of_item(def_id)? == tcx.lang_items().deref_mut_trait()? {
399         Some((RefOp::Method(Mutability::Mut), arg))
400     } else {
401         None
402     }
403 }
404
405 // Checks whether the type for a deref call actually changed the type, not just the mutability of
406 // the reference.
407 fn deref_method_same_type(result_ty: Ty<'_>, arg_ty: Ty<'_>) -> bool {
408     match (result_ty.kind(), arg_ty.kind()) {
409         (ty::Ref(_, result_ty, _), ty::Ref(_, arg_ty, _)) => TyS::same_type(result_ty, arg_ty),
410
411         // The result type for a deref method is always a reference
412         // Not matching the previous pattern means the argument type is not a reference
413         // This means that the type did change
414         _ => false,
415     }
416 }
417
418 // Checks whether the parent node is a suitable context for switching from a deref method to the
419 // deref operator.
420 fn is_linted_explicit_deref_position(parent: Option<Node<'_>>, child_id: HirId, child_span: Span) -> bool {
421     let parent = match parent {
422         Some(Node::Expr(e)) if e.span.ctxt() == child_span.ctxt() => e,
423         _ => return true,
424     };
425     match parent.kind {
426         // Leave deref calls in the middle of a method chain.
427         // e.g. x.deref().foo()
428         ExprKind::MethodCall(_, _, [self_arg, ..], _) if self_arg.hir_id == child_id => false,
429
430         // Leave deref calls resulting in a called function
431         // e.g. (x.deref())()
432         ExprKind::Call(func_expr, _) if func_expr.hir_id == child_id => false,
433
434         // Makes an ugly suggestion
435         // e.g. *x.deref() => *&*x
436         ExprKind::Unary(UnOp::Deref, _)
437         // Postfix expressions would require parens
438         | ExprKind::Match(_, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar)
439         | ExprKind::Field(..)
440         | ExprKind::Index(..)
441         | ExprKind::Err => false,
442
443         ExprKind::Box(..)
444         | ExprKind::ConstBlock(..)
445         | ExprKind::Array(_)
446         | ExprKind::Call(..)
447         | ExprKind::MethodCall(..)
448         | ExprKind::Tup(..)
449         | ExprKind::Binary(..)
450         | ExprKind::Unary(..)
451         | ExprKind::Lit(..)
452         | ExprKind::Cast(..)
453         | ExprKind::Type(..)
454         | ExprKind::DropTemps(..)
455         | ExprKind::If(..)
456         | ExprKind::Loop(..)
457         | ExprKind::Match(..)
458         | ExprKind::Let(..)
459         | ExprKind::Closure(..)
460         | ExprKind::Block(..)
461         | ExprKind::Assign(..)
462         | ExprKind::AssignOp(..)
463         | ExprKind::Path(..)
464         | ExprKind::AddrOf(..)
465         | ExprKind::Break(..)
466         | ExprKind::Continue(..)
467         | ExprKind::Ret(..)
468         | ExprKind::InlineAsm(..)
469         | ExprKind::LlvmInlineAsm(..)
470         | ExprKind::Struct(..)
471         | ExprKind::Repeat(..)
472         | ExprKind::Yield(..) => true,
473     }
474 }
475
476 /// Checks if the given expression is in a position which can be auto-reborrowed.
477 /// Note: This is only correct assuming auto-deref is already occurring.
478 fn is_auto_reborrow_position(parent: Option<Node<'_>>, child_id: HirId) -> bool {
479     match parent {
480         Some(Node::Expr(parent)) => match parent.kind {
481             ExprKind::MethodCall(..) => true,
482             ExprKind::Call(callee, _) => callee.hir_id != child_id,
483             _ => false,
484         },
485         Some(Node::Local(_)) => true,
486         _ => false,
487     }
488 }
489
490 /// Adjustments are sometimes made in the parent block rather than the expression itself.
491 fn find_adjustments<'tcx>(
492     tcx: TyCtxt<'tcx>,
493     typeck: &'tcx TypeckResults<'_>,
494     expr: &'tcx Expr<'_>,
495 ) -> &'tcx [Adjustment<'tcx>] {
496     let map = tcx.hir();
497     let mut iter = map.parent_iter(expr.hir_id);
498     let mut prev = expr;
499
500     loop {
501         match typeck.expr_adjustments(prev) {
502             [] => (),
503             a => break a,
504         };
505
506         match iter.next().map(|(_, x)| x) {
507             Some(Node::Block(_)) => {
508                 if let Some((_, Node::Expr(e))) = iter.next() {
509                     prev = e;
510                 } else {
511                     // This shouldn't happen. Blocks are always contained in an expression.
512                     break &[];
513                 }
514             },
515             Some(Node::Expr(&Expr {
516                 kind: ExprKind::Break(Destination { target_id: Ok(id), .. }, _),
517                 ..
518             })) => {
519                 if let Some(Node::Expr(e)) = map.find(id) {
520                     prev = e;
521                     iter = map.parent_iter(id);
522                 } else {
523                     // This shouldn't happen. The destination should exist.
524                     break &[];
525                 }
526             },
527             _ => break &[],
528         }
529     }
530 }
531
532 #[allow(clippy::needless_pass_by_value)]
533 fn report(cx: &LateContext<'_>, expr: &Expr<'_>, state: State, data: StateData) {
534     match state {
535         State::DerefMethod {
536             ty_changed_count,
537             is_final_ufcs,
538         } => {
539             let mut app = Applicability::MachineApplicable;
540             let (expr_str, expr_is_macro_call) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
541             let ty = cx.typeck_results().expr_ty(expr);
542             let (_, ref_count) = peel_mid_ty_refs(ty);
543             let deref_str = if ty_changed_count >= ref_count && ref_count != 0 {
544                 // a deref call changing &T -> &U requires two deref operators the first time
545                 // this occurs. One to remove the reference, a second to call the deref impl.
546                 "*".repeat(ty_changed_count + 1)
547             } else {
548                 "*".repeat(ty_changed_count)
549             };
550             let addr_of_str = if ty_changed_count < ref_count {
551                 // Check if a reborrow from &mut T -> &T is required.
552                 if data.target_mut == Mutability::Not && matches!(ty.kind(), ty::Ref(_, _, Mutability::Mut)) {
553                     "&*"
554                 } else {
555                     ""
556                 }
557             } else if data.target_mut == Mutability::Mut {
558                 "&mut "
559             } else {
560                 "&"
561             };
562
563             let expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence().order() < PREC_PREFIX {
564                 format!("({})", expr_str)
565             } else {
566                 expr_str.into_owned()
567             };
568
569             span_lint_and_sugg(
570                 cx,
571                 EXPLICIT_DEREF_METHODS,
572                 data.span,
573                 match data.target_mut {
574                     Mutability::Not => "explicit `deref` method call",
575                     Mutability::Mut => "explicit `deref_mut` method call",
576                 },
577                 "try this",
578                 format!("{}{}{}", addr_of_str, deref_str, expr_str),
579                 app,
580             );
581         },
582         State::DerefedBorrow { .. } => {
583             let mut app = Applicability::MachineApplicable;
584             let snip = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app).0;
585             span_lint_and_sugg(
586                 cx,
587                 NEEDLESS_BORROW,
588                 data.span,
589                 &format!(
590                     "this expression borrows a reference (`{}`) that is immediately dereferenced by the compiler",
591                     cx.typeck_results().expr_ty(expr),
592                 ),
593                 "change this to",
594                 snip.into(),
595                 app,
596             );
597         },
598     }
599 }
600
601 impl Dereferencing {
602     fn check_local_usage(&mut self, cx: &LateContext<'_>, e: &Expr<'_>, local: HirId) {
603         if let Some(outer_pat) = self.ref_locals.get_mut(&local) {
604             if let Some(pat) = outer_pat {
605                 // Check for auto-deref
606                 if !matches!(
607                     cx.typeck_results().expr_adjustments(e),
608                     [
609                         Adjustment {
610                             kind: Adjust::Deref(_),
611                             ..
612                         },
613                         Adjustment {
614                             kind: Adjust::Deref(_),
615                             ..
616                         },
617                         ..
618                     ]
619                 ) {
620                     match get_parent_expr(cx, e) {
621                         // Field accesses are the same no matter the number of references.
622                         Some(Expr {
623                             kind: ExprKind::Field(..),
624                             ..
625                         }) => (),
626                         Some(&Expr {
627                             span,
628                             kind: ExprKind::Unary(UnOp::Deref, _),
629                             ..
630                         }) if !span.from_expansion() => {
631                             // Remove explicit deref.
632                             let snip = snippet_with_context(cx, e.span, span.ctxt(), "..", &mut pat.app).0;
633                             pat.replacements.push((span, snip.into()));
634                         },
635                         Some(parent) if !parent.span.from_expansion() => {
636                             // Double reference might be needed at this point.
637                             if parent.precedence().order() == PREC_POSTFIX {
638                                 // Parentheses would be needed here, don't lint.
639                                 *outer_pat = None;
640                             } else {
641                                 pat.always_deref = false;
642                                 let snip = snippet_with_context(cx, e.span, parent.span.ctxt(), "..", &mut pat.app).0;
643                                 pat.replacements.push((e.span, format!("&{}", snip)));
644                             }
645                         },
646                         _ if !e.span.from_expansion() => {
647                             // Double reference might be needed at this point.
648                             pat.always_deref = false;
649                             let snip = snippet_with_applicability(cx, e.span, "..", &mut pat.app);
650                             pat.replacements.push((e.span, format!("&{}", snip)));
651                         },
652                         // Edge case for macros. The span of the identifier will usually match the context of the
653                         // binding, but not if the identifier was created in a macro. e.g. `concat_idents` and proc
654                         // macros
655                         _ => *outer_pat = None,
656                     }
657                 }
658             }
659         }
660     }
661 }