use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::last_path_segment;
use rustc_hir::{
- intravisit, Body, Expr, ExprKind, FieldPat, FnDecl, HirId, LocalSource, MatchSource, Mutability, Pat, PatKind,
- QPath, Stmt, StmtKind,
+ intravisit, Body, Expr, ExprKind, FnDecl, HirId, Let, LocalSource, Mutability, Pat, PatKind, Stmt, StmtKind,
};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty::subst::SubstsRef;
-use rustc_middle::ty::{AdtDef, FieldDef, Ty, TyKind, VariantDef};
+use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span;
declare_clippy_lint! {
- /// **What it does:** Checks for patterns that aren't exact representations of the types
+ /// ### What it does
+ /// Checks for patterns that aren't exact representations of the types
/// they are applied to.
///
/// To satisfy this lint, you will have to adjust either the expression that is matched
/// this lint can still be used to highlight areas of interest and ensure a good understanding
/// of ownership semantics.
///
- /// **Why is this bad?** It isn't bad in general. But in some contexts it can be desirable
+ /// ### Why is this bad?
+ /// It isn't bad in general. But in some contexts it can be desirable
/// because it increases ownership hints in the code, and will guard against some changes
/// in ownership.
///
- /// **Known problems:** None.
- ///
- /// **Example:**
- ///
+ /// ### Example
/// This example shows the basic adjustments necessary to satisfy the lint. Note how
/// the matched expression is explicitly dereferenced with `*` and the `inner` variable
/// is bound to a shared borrow via `ref inner`.
/// *a += b;
/// }
/// ```
+ #[clippy::version = "1.47.0"]
pub PATTERN_TYPE_MISMATCH,
restriction,
"type of pattern does not match the expression type"
impl<'tcx> LateLintPass<'tcx> for PatternTypeMismatch {
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
- if let StmtKind::Local(ref local) = stmt.kind {
- if let Some(init) = &local.init {
- if let Some(init_ty) = cx.typeck_results().node_type_opt(init.hir_id) {
- let pat = &local.pat;
- if in_external_macro(cx.sess(), pat.span) {
- return;
- }
- let deref_possible = match local.source {
- LocalSource::Normal => DerefPossible::Possible,
- _ => DerefPossible::Impossible,
- };
- apply_lint(cx, pat, init_ty, deref_possible);
- }
+ if let StmtKind::Local(local) = stmt.kind {
+ if in_external_macro(cx.sess(), local.pat.span) {
+ return;
}
+ let deref_possible = match local.source {
+ LocalSource::Normal => DerefPossible::Possible,
+ _ => DerefPossible::Impossible,
+ };
+ apply_lint(cx, local.pat, deref_possible);
}
}
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if let ExprKind::Match(ref expr, arms, source) = expr.kind {
- match source {
- MatchSource::Normal | MatchSource::IfLetDesugar { .. } | MatchSource::WhileLetDesugar => {
- if let Some(expr_ty) = cx.typeck_results().node_type_opt(expr.hir_id) {
- 'pattern_checks: for arm in arms {
- let pat = &arm.pat;
- if in_external_macro(cx.sess(), pat.span) {
- continue 'pattern_checks;
- }
- if apply_lint(cx, pat, expr_ty, DerefPossible::Possible) {
- break 'pattern_checks;
- }
- }
- }
- },
- _ => (),
+ if let ExprKind::Match(_, arms, _) = expr.kind {
+ for arm in arms {
+ let pat = &arm.pat;
+ if apply_lint(cx, pat, DerefPossible::Possible) {
+ break;
+ }
}
}
+ if let ExprKind::Let(Let { pat, .. }) = expr.kind {
+ apply_lint(cx, pat, DerefPossible::Possible);
+ }
}
fn check_fn(
_: &'tcx FnDecl<'_>,
body: &'tcx Body<'_>,
_: Span,
- hir_id: HirId,
+ _: HirId,
) {
- if let Some(fn_sig) = cx.typeck_results().liberated_fn_sigs().get(hir_id) {
- for (param, ty) in body.params.iter().zip(fn_sig.inputs().iter()) {
- apply_lint(cx, ¶m.pat, ty, DerefPossible::Impossible);
- }
+ for param in body.params {
+ apply_lint(cx, param.pat, DerefPossible::Impossible);
}
}
}
Impossible,
}
-fn apply_lint<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, expr_ty: Ty<'tcx>, deref_possible: DerefPossible) -> bool {
- let maybe_mismatch = find_first_mismatch(cx, pat, expr_ty, Level::Top);
+fn apply_lint<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, deref_possible: DerefPossible) -> bool {
+ let maybe_mismatch = find_first_mismatch(cx, pat);
if let Some((span, mutability, level)) = maybe_mismatch {
span_lint_and_help(
cx,
}
#[allow(rustc::usage_of_ty_tykind)]
-fn find_first_mismatch<'tcx>(
- cx: &LateContext<'tcx>,
- pat: &Pat<'_>,
- ty: Ty<'tcx>,
- level: Level,
-) -> Option<(Span, Mutability, Level)> {
- if let PatKind::Ref(ref sub_pat, _) = pat.kind {
- if let TyKind::Ref(_, sub_ty, _) = ty.kind() {
- return find_first_mismatch(cx, sub_pat, sub_ty, Level::Lower);
- }
- }
-
- if let TyKind::Ref(_, _, mutability) = *ty.kind() {
- if is_non_ref_pattern(&pat.kind) {
- return Some((pat.span, mutability, level));
- }
- }
-
- if let PatKind::Struct(ref qpath, ref field_pats, _) = pat.kind {
- if let TyKind::Adt(ref adt_def, ref substs_ref) = ty.kind() {
- if let Some(variant) = get_variant(adt_def, qpath) {
- let field_defs = &variant.fields;
- return find_first_mismatch_in_struct(cx, field_pats, field_defs, substs_ref);
- }
- }
- }
-
- if let PatKind::TupleStruct(ref qpath, ref pats, _) = pat.kind {
- if let TyKind::Adt(ref adt_def, ref substs_ref) = ty.kind() {
- if let Some(variant) = get_variant(adt_def, qpath) {
- let field_defs = &variant.fields;
- let ty_iter = field_defs.iter().map(|field_def| field_def.ty(cx.tcx, substs_ref));
- return find_first_mismatch_in_tuple(cx, pats, ty_iter);
- }
- }
- }
-
- if let PatKind::Tuple(ref pats, _) = pat.kind {
- if let TyKind::Tuple(..) = ty.kind() {
- return find_first_mismatch_in_tuple(cx, pats, ty.tuple_fields());
- }
- }
-
- if let PatKind::Or(sub_pats) = pat.kind {
- for pat in sub_pats {
- let maybe_mismatch = find_first_mismatch(cx, pat, ty, level);
- if let Some(mismatch) = maybe_mismatch {
- return Some(mismatch);
- }
+fn find_first_mismatch<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>) -> Option<(Span, Mutability, Level)> {
+ let mut result = None;
+ pat.walk(|p| {
+ if result.is_some() {
+ return false;
}
- }
-
- None
-}
-
-fn get_variant<'a>(adt_def: &'a AdtDef, qpath: &QPath<'_>) -> Option<&'a VariantDef> {
- if adt_def.is_struct() {
- if let Some(variant) = adt_def.variants.iter().next() {
- return Some(variant);
+ if in_external_macro(cx.sess(), p.span) {
+ return true;
}
- }
-
- if adt_def.is_enum() {
- let pat_ident = last_path_segment(qpath).ident;
- for variant in &adt_def.variants {
- if variant.ident == pat_ident {
- return Some(variant);
- }
- }
- }
-
- None
-}
-
-fn find_first_mismatch_in_tuple<'tcx, I>(
- cx: &LateContext<'tcx>,
- pats: &[&Pat<'_>],
- ty_iter_src: I,
-) -> Option<(Span, Mutability, Level)>
-where
- I: IntoIterator<Item = Ty<'tcx>>,
-{
- let mut field_tys = ty_iter_src.into_iter();
- 'fields: for pat in pats {
- let field_ty = if let Some(ty) = field_tys.next() {
- ty
- } else {
- break 'fields;
+ let adjust_pat = match p.kind {
+ PatKind::Or([p, ..]) => p,
+ _ => p,
};
-
- let maybe_mismatch = find_first_mismatch(cx, pat, field_ty, Level::Lower);
- if let Some(mismatch) = maybe_mismatch {
- return Some(mismatch);
- }
- }
-
- None
-}
-
-fn find_first_mismatch_in_struct<'tcx>(
- cx: &LateContext<'tcx>,
- field_pats: &[FieldPat<'_>],
- field_defs: &[FieldDef],
- substs_ref: SubstsRef<'tcx>,
-) -> Option<(Span, Mutability, Level)> {
- for field_pat in field_pats {
- 'definitions: for field_def in field_defs {
- if field_pat.ident == field_def.ident {
- let field_ty = field_def.ty(cx.tcx, substs_ref);
- let pat = &field_pat.pat;
- let maybe_mismatch = find_first_mismatch(cx, pat, field_ty, Level::Lower);
- if let Some(mismatch) = maybe_mismatch {
- return Some(mismatch);
+ if let Some(adjustments) = cx.typeck_results().pat_adjustments().get(adjust_pat.hir_id) {
+ if let [first, ..] = **adjustments {
+ if let ty::Ref(.., mutability) = *first.kind() {
+ let level = if p.hir_id == pat.hir_id {
+ Level::Top
+ } else {
+ Level::Lower
+ };
+ result = Some((p.span, mutability, level));
}
- break 'definitions;
}
}
- }
-
- None
-}
-
-fn is_non_ref_pattern(pat_kind: &PatKind<'_>) -> bool {
- match pat_kind {
- PatKind::Struct(..) | PatKind::Tuple(..) | PatKind::TupleStruct(..) | PatKind::Path(..) => true,
- PatKind::Or(sub_pats) => sub_pats.iter().any(|pat| is_non_ref_pattern(&pat.kind)),
- _ => false,
- }
+ result.is_none()
+ });
+ result
}