1 use crate::utils::{last_path_segment, span_lint_and_help};
3 intravisit, Body, Expr, ExprKind, FieldPat, FnDecl, HirId, LocalSource, MatchSource, Mutability, Pat, PatKind,
6 use rustc_lint::{LateContext, LateLintPass, LintContext};
7 use rustc_middle::lint::in_external_macro;
8 use rustc_middle::ty::subst::SubstsRef;
9 use rustc_middle::ty::{AdtDef, FieldDef, Ty, TyKind, VariantDef};
10 use rustc_session::{declare_lint_pass, declare_tool_lint};
11 use rustc_span::source_map::Span;
13 declare_clippy_lint! {
14 /// **What it does:** Checks for patterns that aren't exact representations of the types
15 /// they are applied to.
17 /// **Why is this bad?** It isn't bad in general. But in some contexts it can be desirable
18 /// because it increases ownership hints in the code, and will guard against some changes
21 /// **Known problems:** None.
27 /// let value = &Some(Box::new(23));
29 /// Some(inner) => println!("{}", inner),
30 /// None => println!("none"),
34 /// let value = &Some(Box::new(23));
36 /// Some(ref inner) => println!("{}", inner),
37 /// None => println!("none"),
40 pub PATTERN_TYPE_MISMATCH,
42 "type of pattern does not match the expression type"
45 declare_lint_pass!(PatternTypeMismatch => [PATTERN_TYPE_MISMATCH]);
47 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PatternTypeMismatch {
48 fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, stmt: &'tcx Stmt<'_>) {
49 if let StmtKind::Local(ref local) = stmt.kind {
50 if let Some(init) = &local.init {
51 if let Some(init_ty) = cx.tables.node_type_opt(init.hir_id) {
53 if in_external_macro(cx.sess(), pat.span) {
56 let deref_possible = match local.source {
57 LocalSource::Normal => DerefPossible::Possible,
58 _ => DerefPossible::Impossible,
60 apply_lint(cx, pat, init_ty, deref_possible);
66 fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
67 if let ExprKind::Match(ref expr, arms, source) = expr.kind {
69 MatchSource::Normal | MatchSource::IfLetDesugar { .. } | MatchSource::WhileLetDesugar => {
70 if let Some(expr_ty) = cx.tables.node_type_opt(expr.hir_id) {
71 'pattern_checks: for arm in arms {
73 if in_external_macro(cx.sess(), pat.span) {
74 continue 'pattern_checks;
76 if apply_lint(cx, pat, expr_ty, DerefPossible::Possible) {
77 break 'pattern_checks;
89 cx: &LateContext<'a, 'tcx>,
90 _: intravisit::FnKind<'tcx>,
96 if let Some(fn_sig) = cx.tables.liberated_fn_sigs().get(hir_id) {
97 for (param, ty) in body.params.iter().zip(fn_sig.inputs().iter()) {
98 apply_lint(cx, ¶m.pat, ty, DerefPossible::Impossible);
104 #[derive(Debug, Clone, Copy)]
110 fn apply_lint<'a, 'tcx>(
111 cx: &LateContext<'a, 'tcx>,
114 deref_possible: DerefPossible,
116 let maybe_mismatch = find_first_mismatch(cx, pat, expr_ty, Level::Top);
117 if let Some((span, mutability, level)) = maybe_mismatch {
120 PATTERN_TYPE_MISMATCH,
122 "type of pattern does not match the expression type",
125 "{}explicitly match against a `{}` pattern and adjust the enclosed variable bindings",
126 match (deref_possible, level) {
127 (DerefPossible::Possible, Level::Top) => "use `*` to dereference the match expression or ",
131 Mutability::Mut => "&mut _",
132 Mutability::Not => "&_",
142 #[derive(Debug, Copy, Clone)]
148 #[allow(rustc::usage_of_ty_tykind)]
149 fn find_first_mismatch<'a, 'tcx>(
150 cx: &LateContext<'a, 'tcx>,
154 ) -> Option<(Span, Mutability, Level)> {
155 if let PatKind::Ref(ref sub_pat, _) = pat.kind {
156 if let TyKind::Ref(_, sub_ty, _) = ty.kind {
157 return find_first_mismatch(cx, sub_pat, sub_ty, Level::Lower);
161 if let TyKind::Ref(_, _, mutability) = ty.kind {
162 if is_non_ref_pattern(&pat.kind) {
163 return Some((pat.span, mutability, level));
167 if let PatKind::Struct(ref qpath, ref field_pats, _) = pat.kind {
168 if let TyKind::Adt(ref adt_def, ref substs_ref) = ty.kind {
169 if let Some(variant) = get_variant(adt_def, qpath) {
170 let field_defs = &variant.fields;
171 return find_first_mismatch_in_struct(cx, field_pats, field_defs, substs_ref);
176 if let PatKind::TupleStruct(ref qpath, ref pats, _) = pat.kind {
177 if let TyKind::Adt(ref adt_def, ref substs_ref) = ty.kind {
178 if let Some(variant) = get_variant(adt_def, qpath) {
179 let field_defs = &variant.fields;
180 let ty_iter = field_defs.iter().map(|field_def| field_def.ty(cx.tcx, substs_ref));
181 return find_first_mismatch_in_tuple(cx, pats, ty_iter);
186 if let PatKind::Tuple(ref pats, _) = pat.kind {
187 if let TyKind::Tuple(..) = ty.kind {
188 return find_first_mismatch_in_tuple(cx, pats, ty.tuple_fields());
192 if let PatKind::Or(sub_pats) = pat.kind {
193 for pat in sub_pats {
194 let maybe_mismatch = find_first_mismatch(cx, pat, ty, level);
195 if let Some(mismatch) = maybe_mismatch {
196 return Some(mismatch);
204 fn get_variant<'a>(adt_def: &'a AdtDef, qpath: &QPath<'_>) -> Option<&'a VariantDef> {
205 if adt_def.is_struct() {
206 if let Some(variant) = adt_def.variants.iter().next() {
207 return Some(variant);
211 if adt_def.is_enum() {
212 let pat_ident = last_path_segment(qpath).ident;
213 for variant in &adt_def.variants {
214 if variant.ident == pat_ident {
215 return Some(variant);
223 fn find_first_mismatch_in_tuple<'a, 'tcx, I>(
224 cx: &LateContext<'a, 'tcx>,
227 ) -> Option<(Span, Mutability, Level)>
229 I: IntoIterator<Item = Ty<'tcx>>,
231 let mut field_tys = ty_iter_src.into_iter();
232 'fields: for pat in pats {
233 let field_ty = if let Some(ty) = field_tys.next() {
239 let maybe_mismatch = find_first_mismatch(cx, pat, field_ty, Level::Lower);
240 if let Some(mismatch) = maybe_mismatch {
241 return Some(mismatch);
248 fn find_first_mismatch_in_struct<'a, 'tcx>(
249 cx: &LateContext<'a, 'tcx>,
250 field_pats: &[FieldPat<'_>],
251 field_defs: &[FieldDef],
252 substs_ref: SubstsRef<'tcx>,
253 ) -> Option<(Span, Mutability, Level)> {
254 for field_pat in field_pats {
255 'definitions: for field_def in field_defs {
256 if field_pat.ident == field_def.ident {
257 let field_ty = field_def.ty(cx.tcx, substs_ref);
258 let pat = &field_pat.pat;
259 let maybe_mismatch = find_first_mismatch(cx, pat, field_ty, Level::Lower);
260 if let Some(mismatch) = maybe_mismatch {
261 return Some(mismatch);
271 fn is_non_ref_pattern(pat_kind: &PatKind<'_>) -> bool {
273 PatKind::Struct(..) | PatKind::Tuple(..) | PatKind::TupleStruct(..) | PatKind::Path(..) => true,
274 PatKind::Or(sub_pats) => sub_pats.iter().any(|pat| is_non_ref_pattern(&pat.kind)),