1 use clippy_utils::diagnostics::span_lint_and_help;
3 intravisit, Body, Expr, ExprKind, FnDecl, HirId, LocalSource, Mutability, Pat, PatKind, Stmt, StmtKind,
5 use rustc_lint::{LateContext, LateLintPass, LintContext};
6 use rustc_middle::lint::in_external_macro;
8 use rustc_session::{declare_lint_pass, declare_tool_lint};
9 use rustc_span::source_map::Span;
11 declare_clippy_lint! {
13 /// Checks for patterns that aren't exact representations of the types
14 /// they are applied to.
16 /// To satisfy this lint, you will have to adjust either the expression that is matched
17 /// against or the pattern itself, as well as the bindings that are introduced by the
18 /// adjusted patterns. For matching you will have to either dereference the expression
19 /// with the `*` operator, or amend the patterns to explicitly match against `&<pattern>`
20 /// or `&mut <pattern>` depending on the reference mutability. For the bindings you need
21 /// to use the inverse. You can leave them as plain bindings if you wish for the value
22 /// to be copied, but you must use `ref mut <variable>` or `ref <variable>` to construct
23 /// a reference into the matched structure.
25 /// If you are looking for a way to learn about ownership semantics in more detail, it
26 /// is recommended to look at IDE options available to you to highlight types, lifetimes
27 /// and reference semantics in your code. The available tooling would expose these things
28 /// in a general way even outside of the various pattern matching mechanics. Of course
29 /// this lint can still be used to highlight areas of interest and ensure a good understanding
30 /// of ownership semantics.
32 /// ### Why is this bad?
33 /// It isn't bad in general. But in some contexts it can be desirable
34 /// because it increases ownership hints in the code, and will guard against some changes
38 /// This example shows the basic adjustments necessary to satisfy the lint. Note how
39 /// the matched expression is explicitly dereferenced with `*` and the `inner` variable
40 /// is bound to a shared borrow via `ref inner`.
44 /// let value = &Some(Box::new(23));
46 /// Some(inner) => println!("{}", inner),
47 /// None => println!("none"),
51 /// let value = &Some(Box::new(23));
53 /// Some(ref inner) => println!("{}", inner),
54 /// None => println!("none"),
58 /// The following example demonstrates one of the advantages of the more verbose style.
59 /// Note how the second version uses `ref mut a` to explicitly declare `a` a shared mutable
60 /// borrow, while `b` is simply taken by value. This ensures that the loop body cannot
61 /// accidentally modify the wrong part of the structure.
65 /// let mut values = vec![(2, 3), (3, 4)];
66 /// for (a, b) in &mut values {
71 /// let mut values = vec![(2, 3), (3, 4)];
72 /// for &mut (ref mut a, b) in &mut values {
76 #[clippy::version = "1.47.0"]
77 pub PATTERN_TYPE_MISMATCH,
79 "type of pattern does not match the expression type"
82 declare_lint_pass!(PatternTypeMismatch => [PATTERN_TYPE_MISMATCH]);
84 impl<'tcx> LateLintPass<'tcx> for PatternTypeMismatch {
85 fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
86 if let StmtKind::Local(local) = stmt.kind {
87 if in_external_macro(cx.sess(), local.pat.span) {
90 let deref_possible = match local.source {
91 LocalSource::Normal => DerefPossible::Possible,
92 _ => DerefPossible::Impossible,
94 apply_lint(cx, local.pat, deref_possible);
98 fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
99 if let ExprKind::Match(_, arms, _) = expr.kind {
102 if apply_lint(cx, pat, DerefPossible::Possible) {
107 if let ExprKind::Let(let_pat, ..) = expr.kind {
108 apply_lint(cx, let_pat, DerefPossible::Possible);
114 cx: &LateContext<'tcx>,
115 _: intravisit::FnKind<'tcx>,
117 body: &'tcx Body<'_>,
121 for param in body.params {
122 apply_lint(cx, param.pat, DerefPossible::Impossible);
127 #[derive(Debug, Clone, Copy)]
133 fn apply_lint<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, deref_possible: DerefPossible) -> bool {
134 let maybe_mismatch = find_first_mismatch(cx, pat);
135 if let Some((span, mutability, level)) = maybe_mismatch {
138 PATTERN_TYPE_MISMATCH,
140 "type of pattern does not match the expression type",
143 "{}explicitly match against a `{}` pattern and adjust the enclosed variable bindings",
144 match (deref_possible, level) {
145 (DerefPossible::Possible, Level::Top) => "use `*` to dereference the match expression or ",
149 Mutability::Mut => "&mut _",
150 Mutability::Not => "&_",
160 #[derive(Debug, Copy, Clone)]
166 #[allow(rustc::usage_of_ty_tykind)]
167 fn find_first_mismatch<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>) -> Option<(Span, Mutability, Level)> {
168 let mut result = None;
170 if result.is_some() {
173 if in_external_macro(cx.sess(), p.span) {
176 let adjust_pat = match p.kind {
177 PatKind::Or([p, ..]) => p,
180 if let Some(adjustments) = cx.typeck_results().pat_adjustments().get(adjust_pat.hir_id) {
181 if let [first, ..] = **adjustments {
182 if let ty::Ref(.., mutability) = *first.kind() {
183 let level = if p.hir_id == pat.hir_id {
188 result = Some((p.span, mutability, level));