1 use crate::utils::{last_path_segment, span_help_and_lint};
2 use rustc::lint::in_external_macro;
3 use rustc::ty::subst::SubstsRef;
4 use rustc::ty::{AdtDef, FieldDef, Ty, TyKind, VariantDef};
6 intravisit, Body, Expr, ExprKind, FieldPat, FnDecl, HirId, LocalSource, MatchSource, Mutability, Pat, PatKind,
9 use rustc_lint::{LateContext, LateLintPass, LintContext};
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",
124 "{}explicitly match against a `{}` pattern and adjust the enclosed variable bindings",
125 match (deref_possible, level) {
126 (DerefPossible::Possible, Level::Top) => "use `*` to dereference the match expression or ",
130 Mutability::Mut => "&mut _",
131 Mutability::Not => "&_",
141 #[derive(Debug, Copy, Clone)]
147 #[allow(rustc::usage_of_ty_tykind)]
148 fn find_first_mismatch<'a, 'tcx>(
149 cx: &LateContext<'a, 'tcx>,
153 ) -> Option<(Span, Mutability, Level)> {
154 if let PatKind::Ref(ref sub_pat, _) = pat.kind {
155 if let TyKind::Ref(_, sub_ty, _) = ty.kind {
156 return find_first_mismatch(cx, sub_pat, sub_ty, Level::Lower);
160 if let TyKind::Ref(_, _, mutability) = ty.kind {
161 if is_non_ref_pattern(&pat.kind) {
162 return Some((pat.span, mutability, level));
166 if let PatKind::Struct(ref qpath, ref field_pats, _) = pat.kind {
167 if let TyKind::Adt(ref adt_def, ref substs_ref) = ty.kind {
168 if let Some(variant) = get_variant(adt_def, qpath) {
169 let field_defs = &variant.fields;
170 return find_first_mismatch_in_struct(cx, field_pats, field_defs, substs_ref);
175 if let PatKind::TupleStruct(ref qpath, ref pats, _) = pat.kind {
176 if let TyKind::Adt(ref adt_def, ref substs_ref) = ty.kind {
177 if let Some(variant) = get_variant(adt_def, qpath) {
178 let field_defs = &variant.fields;
179 let ty_iter = field_defs.iter().map(|field_def| field_def.ty(cx.tcx, substs_ref));
180 return find_first_mismatch_in_tuple(cx, pats, ty_iter);
185 if let PatKind::Tuple(ref pats, _) = pat.kind {
186 if let TyKind::Tuple(..) = ty.kind {
187 return find_first_mismatch_in_tuple(cx, pats, ty.tuple_fields());
191 if let PatKind::Or(sub_pats) = pat.kind {
192 for pat in sub_pats {
193 let maybe_mismatch = find_first_mismatch(cx, pat, ty, level);
194 if let Some(mismatch) = maybe_mismatch {
195 return Some(mismatch);
203 fn get_variant<'a>(adt_def: &'a AdtDef, qpath: &QPath<'_>) -> Option<&'a VariantDef> {
204 if adt_def.is_struct() {
205 if let Some(variant) = adt_def.variants.iter().next() {
206 return Some(variant);
210 if adt_def.is_enum() {
211 let pat_ident = last_path_segment(qpath).ident;
212 for variant in &adt_def.variants {
213 if variant.ident == pat_ident {
214 return Some(variant);
222 fn find_first_mismatch_in_tuple<'a, 'tcx, I>(
223 cx: &LateContext<'a, 'tcx>,
226 ) -> Option<(Span, Mutability, Level)>
228 I: IntoIterator<Item = Ty<'tcx>>,
230 let mut field_tys = ty_iter_src.into_iter();
231 'fields: for pat in pats {
232 let field_ty = if let Some(ty) = field_tys.next() {
238 let maybe_mismatch = find_first_mismatch(cx, pat, field_ty, Level::Lower);
239 if let Some(mismatch) = maybe_mismatch {
240 return Some(mismatch);
247 fn find_first_mismatch_in_struct<'a, 'tcx>(
248 cx: &LateContext<'a, 'tcx>,
249 field_pats: &[FieldPat<'_>],
250 field_defs: &[FieldDef],
251 substs_ref: SubstsRef<'tcx>,
252 ) -> Option<(Span, Mutability, Level)> {
253 for field_pat in field_pats {
254 'definitions: for field_def in field_defs {
255 if field_pat.ident == field_def.ident {
256 let field_ty = field_def.ty(cx.tcx, substs_ref);
257 let pat = &field_pat.pat;
258 let maybe_mismatch = find_first_mismatch(cx, pat, field_ty, Level::Lower);
259 if let Some(mismatch) = maybe_mismatch {
260 return Some(mismatch);
270 fn is_non_ref_pattern(pat_kind: &PatKind<'_>) -> bool {
272 PatKind::Struct(..) | PatKind::Tuple(..) | PatKind::TupleStruct(..) | PatKind::Path(..) => true,
273 PatKind::Or(sub_pats) => sub_pats.iter().any(|pat| is_non_ref_pattern(&pat.kind)),