mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-13 12:36:47 +00:00
Auto merge of #4841 - phaylon:pattern-type-mismatch, r=flip1995
Added restriction lint: pattern-type-mismatch changelog: Added a new restriction lint `pattern-type-mismatch`. This lint is especially helpful for beginners learning about the magic behind pattern matching. (This explanation might be worth to include in the next changelog.)
This commit is contained in:
commit
57cdf2dc16
@ -1588,6 +1588,7 @@ Released 2018-09-13
|
||||
[`panicking_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap
|
||||
[`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl
|
||||
[`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite
|
||||
[`pattern_type_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#pattern_type_mismatch
|
||||
[`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma
|
||||
[`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence
|
||||
[`print_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_literal
|
||||
|
@ -268,6 +268,7 @@ mod overflow_check_conditional;
|
||||
mod panic_unimplemented;
|
||||
mod partialeq_ne_impl;
|
||||
mod path_buf_push_overwrite;
|
||||
mod pattern_type_mismatch;
|
||||
mod precedence;
|
||||
mod ptr;
|
||||
mod ptr_offset_with_cast;
|
||||
@ -741,6 +742,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
&panic_unimplemented::UNREACHABLE,
|
||||
&partialeq_ne_impl::PARTIALEQ_NE_IMPL,
|
||||
&path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE,
|
||||
&pattern_type_mismatch::PATTERN_TYPE_MISMATCH,
|
||||
&precedence::PRECEDENCE,
|
||||
&ptr::CMP_NULL,
|
||||
&ptr::MUT_FROM_REF,
|
||||
@ -1065,6 +1067,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_early_pass(|| box unnested_or_patterns::UnnestedOrPatterns);
|
||||
store.register_late_pass(|| box macro_use::MacroUseImports::default());
|
||||
store.register_late_pass(|| box map_identity::MapIdentity);
|
||||
store.register_late_pass(|| box pattern_type_mismatch::PatternTypeMismatch);
|
||||
|
||||
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
|
||||
LintId::of(&arithmetic::FLOAT_ARITHMETIC),
|
||||
@ -1098,6 +1101,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&panic_unimplemented::TODO),
|
||||
LintId::of(&panic_unimplemented::UNIMPLEMENTED),
|
||||
LintId::of(&panic_unimplemented::UNREACHABLE),
|
||||
LintId::of(&pattern_type_mismatch::PATTERN_TYPE_MISMATCH),
|
||||
LintId::of(&shadow::SHADOW_REUSE),
|
||||
LintId::of(&shadow::SHADOW_SAME),
|
||||
LintId::of(&strings::STRING_ADD),
|
||||
|
311
clippy_lints/src/pattern_type_mismatch.rs
Normal file
311
clippy_lints/src/pattern_type_mismatch.rs
Normal file
@ -0,0 +1,311 @@
|
||||
use crate::utils::{last_path_segment, span_lint_and_help};
|
||||
use rustc_hir::{
|
||||
intravisit, Body, Expr, ExprKind, FieldPat, FnDecl, HirId, LocalSource, MatchSource, Mutability, Pat, PatKind,
|
||||
QPath, 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_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
|
||||
/// they are applied to.
|
||||
///
|
||||
/// To satisfy this lint, you will have to adjust either the expression that is matched
|
||||
/// against or the pattern itself, as well as the bindings that are introduced by the
|
||||
/// adjusted patterns. For matching you will have to either dereference the expression
|
||||
/// with the `*` operator, or amend the patterns to explicitly match against `&<pattern>`
|
||||
/// or `&mut <pattern>` depending on the reference mutability. For the bindings you need
|
||||
/// to use the inverse. You can leave them as plain bindings if you wish for the value
|
||||
/// to be copied, but you must use `ref mut <variable>` or `ref <variable>` to construct
|
||||
/// a reference into the matched structure.
|
||||
///
|
||||
/// If you are looking for a way to learn about ownership semantics in more detail, it
|
||||
/// is recommended to look at IDE options available to you to highlight types, lifetimes
|
||||
/// and reference semantics in your code. The available tooling would expose these things
|
||||
/// in a general way even outside of the various pattern matching mechanics. Of course
|
||||
/// 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
|
||||
/// because it increases ownership hints in the code, and will guard against some changes
|
||||
/// in ownership.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **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`.
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// // Bad
|
||||
/// let value = &Some(Box::new(23));
|
||||
/// match value {
|
||||
/// Some(inner) => println!("{}", inner),
|
||||
/// None => println!("none"),
|
||||
/// }
|
||||
///
|
||||
/// // Good
|
||||
/// let value = &Some(Box::new(23));
|
||||
/// match *value {
|
||||
/// Some(ref inner) => println!("{}", inner),
|
||||
/// None => println!("none"),
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// The following example demonstrates one of the advantages of the more verbose style.
|
||||
/// Note how the second version uses `ref mut a` to explicitly declare `a` a shared mutable
|
||||
/// borrow, while `b` is simply taken by value. This ensures that the loop body cannot
|
||||
/// accidentally modify the wrong part of the structure.
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// // Bad
|
||||
/// let mut values = vec![(2, 3), (3, 4)];
|
||||
/// for (a, b) in &mut values {
|
||||
/// *a += *b;
|
||||
/// }
|
||||
///
|
||||
/// // Good
|
||||
/// let mut values = vec![(2, 3), (3, 4)];
|
||||
/// for &mut (ref mut a, b) in &mut values {
|
||||
/// *a += b;
|
||||
/// }
|
||||
/// ```
|
||||
pub PATTERN_TYPE_MISMATCH,
|
||||
restriction,
|
||||
"type of pattern does not match the expression type"
|
||||
}
|
||||
|
||||
declare_lint_pass!(PatternTypeMismatch => [PATTERN_TYPE_MISMATCH]);
|
||||
|
||||
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.tables().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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.tables().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;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_fn(
|
||||
&mut self,
|
||||
cx: &LateContext<'tcx>,
|
||||
_: intravisit::FnKind<'tcx>,
|
||||
_: &'tcx FnDecl<'_>,
|
||||
body: &'tcx Body<'_>,
|
||||
_: Span,
|
||||
hir_id: HirId,
|
||||
) {
|
||||
if let Some(fn_sig) = cx.tables().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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum DerefPossible {
|
||||
Possible,
|
||||
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);
|
||||
if let Some((span, mutability, level)) = maybe_mismatch {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
PATTERN_TYPE_MISMATCH,
|
||||
span,
|
||||
"type of pattern does not match the expression type",
|
||||
None,
|
||||
&format!(
|
||||
"{}explicitly match against a `{}` pattern and adjust the enclosed variable bindings",
|
||||
match (deref_possible, level) {
|
||||
(DerefPossible::Possible, Level::Top) => "use `*` to dereference the match expression or ",
|
||||
_ => "",
|
||||
},
|
||||
match mutability {
|
||||
Mutability::Mut => "&mut _",
|
||||
Mutability::Not => "&_",
|
||||
},
|
||||
),
|
||||
);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
enum Level {
|
||||
Top,
|
||||
Lower,
|
||||
}
|
||||
|
||||
#[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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 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 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);
|
||||
}
|
||||
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,
|
||||
}
|
||||
}
|
@ -1697,6 +1697,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
|
||||
deprecation: None,
|
||||
module: "path_buf_push_overwrite",
|
||||
},
|
||||
Lint {
|
||||
name: "pattern_type_mismatch",
|
||||
group: "restriction",
|
||||
desc: "type of pattern does not match the expression type",
|
||||
deprecation: None,
|
||||
module: "pattern_type_mismatch",
|
||||
},
|
||||
Lint {
|
||||
name: "possible_missing_comma",
|
||||
group: "correctness",
|
||||
|
40
tests/ui/pattern_type_mismatch/mutability.rs
Normal file
40
tests/ui/pattern_type_mismatch/mutability.rs
Normal file
@ -0,0 +1,40 @@
|
||||
#![allow(clippy::all)]
|
||||
#![warn(clippy::pattern_type_mismatch)]
|
||||
|
||||
fn main() {}
|
||||
|
||||
fn should_lint() {
|
||||
let value = &Some(23);
|
||||
match value {
|
||||
Some(_) => (),
|
||||
_ => (),
|
||||
}
|
||||
|
||||
let value = &mut Some(23);
|
||||
match value {
|
||||
Some(_) => (),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn should_not_lint() {
|
||||
let value = &Some(23);
|
||||
match value {
|
||||
&Some(_) => (),
|
||||
_ => (),
|
||||
}
|
||||
match *value {
|
||||
Some(_) => (),
|
||||
_ => (),
|
||||
}
|
||||
|
||||
let value = &mut Some(23);
|
||||
match value {
|
||||
&mut Some(_) => (),
|
||||
_ => (),
|
||||
}
|
||||
match *value {
|
||||
Some(_) => (),
|
||||
_ => (),
|
||||
}
|
||||
}
|
19
tests/ui/pattern_type_mismatch/mutability.stderr
Normal file
19
tests/ui/pattern_type_mismatch/mutability.stderr
Normal file
@ -0,0 +1,19 @@
|
||||
error: type of pattern does not match the expression type
|
||||
--> $DIR/mutability.rs:9:9
|
||||
|
|
||||
LL | Some(_) => (),
|
||||
| ^^^^^^^
|
||||
|
|
||||
= note: `-D clippy::pattern-type-mismatch` implied by `-D warnings`
|
||||
= help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
|
||||
|
||||
error: type of pattern does not match the expression type
|
||||
--> $DIR/mutability.rs:15:9
|
||||
|
|
||||
LL | Some(_) => (),
|
||||
| ^^^^^^^
|
||||
|
|
||||
= help: use `*` to dereference the match expression or explicitly match against a `&mut _` pattern and adjust the enclosed variable bindings
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
24
tests/ui/pattern_type_mismatch/pattern_alternatives.rs
Normal file
24
tests/ui/pattern_type_mismatch/pattern_alternatives.rs
Normal file
@ -0,0 +1,24 @@
|
||||
#![allow(clippy::all)]
|
||||
#![warn(clippy::pattern_type_mismatch)]
|
||||
|
||||
fn main() {}
|
||||
|
||||
fn alternatives() {
|
||||
enum Value<'a> {
|
||||
Unused,
|
||||
A(&'a Option<i32>),
|
||||
B,
|
||||
}
|
||||
let ref_value = &Value::A(&Some(23));
|
||||
|
||||
// not ok
|
||||
if let Value::B | Value::A(_) = ref_value {}
|
||||
if let &Value::B | &Value::A(Some(_)) = ref_value {}
|
||||
if let Value::B | Value::A(Some(_)) = *ref_value {}
|
||||
|
||||
// ok
|
||||
if let &Value::B | &Value::A(_) = ref_value {}
|
||||
if let Value::B | Value::A(_) = *ref_value {}
|
||||
if let &Value::B | &Value::A(&Some(_)) = ref_value {}
|
||||
if let Value::B | Value::A(&Some(_)) = *ref_value {}
|
||||
}
|
27
tests/ui/pattern_type_mismatch/pattern_alternatives.stderr
Normal file
27
tests/ui/pattern_type_mismatch/pattern_alternatives.stderr
Normal file
@ -0,0 +1,27 @@
|
||||
error: type of pattern does not match the expression type
|
||||
--> $DIR/pattern_alternatives.rs:15:12
|
||||
|
|
||||
LL | if let Value::B | Value::A(_) = ref_value {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `-D clippy::pattern-type-mismatch` implied by `-D warnings`
|
||||
= help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
|
||||
|
||||
error: type of pattern does not match the expression type
|
||||
--> $DIR/pattern_alternatives.rs:16:34
|
||||
|
|
||||
LL | if let &Value::B | &Value::A(Some(_)) = ref_value {}
|
||||
| ^^^^^^^
|
||||
|
|
||||
= help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
|
||||
|
||||
error: type of pattern does not match the expression type
|
||||
--> $DIR/pattern_alternatives.rs:17:32
|
||||
|
|
||||
LL | if let Value::B | Value::A(Some(_)) = *ref_value {}
|
||||
| ^^^^^^^
|
||||
|
|
||||
= help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
45
tests/ui/pattern_type_mismatch/pattern_structs.rs
Normal file
45
tests/ui/pattern_type_mismatch/pattern_structs.rs
Normal file
@ -0,0 +1,45 @@
|
||||
#![allow(clippy::all)]
|
||||
#![warn(clippy::pattern_type_mismatch)]
|
||||
|
||||
fn main() {}
|
||||
|
||||
fn struct_types() {
|
||||
struct Struct<'a> {
|
||||
ref_inner: &'a Option<i32>,
|
||||
}
|
||||
let ref_value = &Struct { ref_inner: &Some(42) };
|
||||
|
||||
// not ok
|
||||
let Struct { .. } = ref_value;
|
||||
if let &Struct { ref_inner: Some(_) } = ref_value {}
|
||||
if let Struct { ref_inner: Some(_) } = *ref_value {}
|
||||
|
||||
// ok
|
||||
let &Struct { .. } = ref_value;
|
||||
let Struct { .. } = *ref_value;
|
||||
if let &Struct { ref_inner: &Some(_) } = ref_value {}
|
||||
if let Struct { ref_inner: &Some(_) } = *ref_value {}
|
||||
}
|
||||
|
||||
fn struct_enum_variants() {
|
||||
enum StructEnum<'a> {
|
||||
Empty,
|
||||
Var { inner_ref: &'a Option<i32> },
|
||||
}
|
||||
let ref_value = &StructEnum::Var { inner_ref: &Some(42) };
|
||||
|
||||
// not ok
|
||||
if let StructEnum::Var { .. } = ref_value {}
|
||||
if let StructEnum::Var { inner_ref: Some(_) } = ref_value {}
|
||||
if let &StructEnum::Var { inner_ref: Some(_) } = ref_value {}
|
||||
if let StructEnum::Var { inner_ref: Some(_) } = *ref_value {}
|
||||
if let StructEnum::Empty = ref_value {}
|
||||
|
||||
// ok
|
||||
if let &StructEnum::Var { .. } = ref_value {}
|
||||
if let StructEnum::Var { .. } = *ref_value {}
|
||||
if let &StructEnum::Var { inner_ref: &Some(_) } = ref_value {}
|
||||
if let StructEnum::Var { inner_ref: &Some(_) } = *ref_value {}
|
||||
if let &StructEnum::Empty = ref_value {}
|
||||
if let StructEnum::Empty = *ref_value {}
|
||||
}
|
67
tests/ui/pattern_type_mismatch/pattern_structs.stderr
Normal file
67
tests/ui/pattern_type_mismatch/pattern_structs.stderr
Normal file
@ -0,0 +1,67 @@
|
||||
error: type of pattern does not match the expression type
|
||||
--> $DIR/pattern_structs.rs:13:9
|
||||
|
|
||||
LL | let Struct { .. } = ref_value;
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `-D clippy::pattern-type-mismatch` implied by `-D warnings`
|
||||
= help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
|
||||
|
||||
error: type of pattern does not match the expression type
|
||||
--> $DIR/pattern_structs.rs:14:33
|
||||
|
|
||||
LL | if let &Struct { ref_inner: Some(_) } = ref_value {}
|
||||
| ^^^^^^^
|
||||
|
|
||||
= help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
|
||||
|
||||
error: type of pattern does not match the expression type
|
||||
--> $DIR/pattern_structs.rs:15:32
|
||||
|
|
||||
LL | if let Struct { ref_inner: Some(_) } = *ref_value {}
|
||||
| ^^^^^^^
|
||||
|
|
||||
= help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
|
||||
|
||||
error: type of pattern does not match the expression type
|
||||
--> $DIR/pattern_structs.rs:32:12
|
||||
|
|
||||
LL | if let StructEnum::Var { .. } = ref_value {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
|
||||
|
||||
error: type of pattern does not match the expression type
|
||||
--> $DIR/pattern_structs.rs:33:12
|
||||
|
|
||||
LL | if let StructEnum::Var { inner_ref: Some(_) } = ref_value {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
|
||||
|
||||
error: type of pattern does not match the expression type
|
||||
--> $DIR/pattern_structs.rs:34:42
|
||||
|
|
||||
LL | if let &StructEnum::Var { inner_ref: Some(_) } = ref_value {}
|
||||
| ^^^^^^^
|
||||
|
|
||||
= help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
|
||||
|
||||
error: type of pattern does not match the expression type
|
||||
--> $DIR/pattern_structs.rs:35:41
|
||||
|
|
||||
LL | if let StructEnum::Var { inner_ref: Some(_) } = *ref_value {}
|
||||
| ^^^^^^^
|
||||
|
|
||||
= help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
|
||||
|
||||
error: type of pattern does not match the expression type
|
||||
--> $DIR/pattern_structs.rs:36:12
|
||||
|
|
||||
LL | if let StructEnum::Empty = ref_value {}
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
|
||||
|
||||
error: aborting due to 8 previous errors
|
||||
|
57
tests/ui/pattern_type_mismatch/pattern_tuples.rs
Normal file
57
tests/ui/pattern_type_mismatch/pattern_tuples.rs
Normal file
@ -0,0 +1,57 @@
|
||||
#![allow(clippy::all)]
|
||||
#![warn(clippy::pattern_type_mismatch)]
|
||||
|
||||
fn main() {}
|
||||
|
||||
fn tuple_types() {
|
||||
struct TupleStruct<'a>(&'a Option<i32>);
|
||||
let ref_value = &TupleStruct(&Some(42));
|
||||
|
||||
// not ok
|
||||
let TupleStruct(_) = ref_value;
|
||||
if let &TupleStruct(Some(_)) = ref_value {}
|
||||
if let TupleStruct(Some(_)) = *ref_value {}
|
||||
|
||||
// ok
|
||||
let &TupleStruct(_) = ref_value;
|
||||
let TupleStruct(_) = *ref_value;
|
||||
if let &TupleStruct(&Some(_)) = ref_value {}
|
||||
if let TupleStruct(&Some(_)) = *ref_value {}
|
||||
}
|
||||
|
||||
fn tuple_enum_variants() {
|
||||
enum TupleEnum<'a> {
|
||||
Empty,
|
||||
Var(&'a Option<i32>),
|
||||
}
|
||||
let ref_value = &TupleEnum::Var(&Some(42));
|
||||
|
||||
// not ok
|
||||
if let TupleEnum::Var(_) = ref_value {}
|
||||
if let &TupleEnum::Var(Some(_)) = ref_value {}
|
||||
if let TupleEnum::Var(Some(_)) = *ref_value {}
|
||||
if let TupleEnum::Empty = ref_value {}
|
||||
|
||||
// ok
|
||||
if let &TupleEnum::Var(_) = ref_value {}
|
||||
if let TupleEnum::Var(_) = *ref_value {}
|
||||
if let &TupleEnum::Var(&Some(_)) = ref_value {}
|
||||
if let TupleEnum::Var(&Some(_)) = *ref_value {}
|
||||
if let &TupleEnum::Empty = ref_value {}
|
||||
if let TupleEnum::Empty = *ref_value {}
|
||||
}
|
||||
|
||||
fn plain_tuples() {
|
||||
let ref_value = &(&Some(23), &Some(42));
|
||||
|
||||
// not ok
|
||||
let (_a, _b) = ref_value;
|
||||
if let &(_a, Some(_)) = ref_value {}
|
||||
if let (_a, Some(_)) = *ref_value {}
|
||||
|
||||
// ok
|
||||
let &(_a, _b) = ref_value;
|
||||
let (_a, _b) = *ref_value;
|
||||
if let &(_a, &Some(_)) = ref_value {}
|
||||
if let (_a, &Some(_)) = *ref_value {}
|
||||
}
|
83
tests/ui/pattern_type_mismatch/pattern_tuples.stderr
Normal file
83
tests/ui/pattern_type_mismatch/pattern_tuples.stderr
Normal file
@ -0,0 +1,83 @@
|
||||
error: type of pattern does not match the expression type
|
||||
--> $DIR/pattern_tuples.rs:11:9
|
||||
|
|
||||
LL | let TupleStruct(_) = ref_value;
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `-D clippy::pattern-type-mismatch` implied by `-D warnings`
|
||||
= help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
|
||||
|
||||
error: type of pattern does not match the expression type
|
||||
--> $DIR/pattern_tuples.rs:12:25
|
||||
|
|
||||
LL | if let &TupleStruct(Some(_)) = ref_value {}
|
||||
| ^^^^^^^
|
||||
|
|
||||
= help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
|
||||
|
||||
error: type of pattern does not match the expression type
|
||||
--> $DIR/pattern_tuples.rs:13:24
|
||||
|
|
||||
LL | if let TupleStruct(Some(_)) = *ref_value {}
|
||||
| ^^^^^^^
|
||||
|
|
||||
= help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
|
||||
|
||||
error: type of pattern does not match the expression type
|
||||
--> $DIR/pattern_tuples.rs:30:12
|
||||
|
|
||||
LL | if let TupleEnum::Var(_) = ref_value {}
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
|
||||
|
||||
error: type of pattern does not match the expression type
|
||||
--> $DIR/pattern_tuples.rs:31:28
|
||||
|
|
||||
LL | if let &TupleEnum::Var(Some(_)) = ref_value {}
|
||||
| ^^^^^^^
|
||||
|
|
||||
= help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
|
||||
|
||||
error: type of pattern does not match the expression type
|
||||
--> $DIR/pattern_tuples.rs:32:27
|
||||
|
|
||||
LL | if let TupleEnum::Var(Some(_)) = *ref_value {}
|
||||
| ^^^^^^^
|
||||
|
|
||||
= help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
|
||||
|
||||
error: type of pattern does not match the expression type
|
||||
--> $DIR/pattern_tuples.rs:33:12
|
||||
|
|
||||
LL | if let TupleEnum::Empty = ref_value {}
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
|
||||
|
||||
error: type of pattern does not match the expression type
|
||||
--> $DIR/pattern_tuples.rs:48:9
|
||||
|
|
||||
LL | let (_a, _b) = ref_value;
|
||||
| ^^^^^^^^
|
||||
|
|
||||
= help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
|
||||
|
||||
error: type of pattern does not match the expression type
|
||||
--> $DIR/pattern_tuples.rs:49:18
|
||||
|
|
||||
LL | if let &(_a, Some(_)) = ref_value {}
|
||||
| ^^^^^^^
|
||||
|
|
||||
= help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
|
||||
|
||||
error: type of pattern does not match the expression type
|
||||
--> $DIR/pattern_tuples.rs:50:17
|
||||
|
|
||||
LL | if let (_a, Some(_)) = *ref_value {}
|
||||
| ^^^^^^^
|
||||
|
|
||||
= help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
|
||||
|
||||
error: aborting due to 10 previous errors
|
||||
|
146
tests/ui/pattern_type_mismatch/syntax.rs
Normal file
146
tests/ui/pattern_type_mismatch/syntax.rs
Normal file
@ -0,0 +1,146 @@
|
||||
#![allow(clippy::all)]
|
||||
#![warn(clippy::pattern_type_mismatch)]
|
||||
|
||||
fn main() {}
|
||||
|
||||
fn syntax_match() {
|
||||
let ref_value = &Some(&Some(42));
|
||||
|
||||
// not ok
|
||||
match ref_value {
|
||||
Some(_) => (),
|
||||
None => (),
|
||||
}
|
||||
|
||||
// ok
|
||||
match ref_value {
|
||||
&Some(_) => (),
|
||||
&None => (),
|
||||
}
|
||||
match *ref_value {
|
||||
Some(_) => (),
|
||||
None => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn syntax_if_let() {
|
||||
let ref_value = &Some(42);
|
||||
|
||||
// not ok
|
||||
if let Some(_) = ref_value {}
|
||||
|
||||
// ok
|
||||
if let &Some(_) = ref_value {}
|
||||
if let Some(_) = *ref_value {}
|
||||
}
|
||||
|
||||
fn syntax_while_let() {
|
||||
let ref_value = &Some(42);
|
||||
|
||||
// not ok
|
||||
while let Some(_) = ref_value {
|
||||
break;
|
||||
}
|
||||
|
||||
// ok
|
||||
while let &Some(_) = ref_value {
|
||||
break;
|
||||
}
|
||||
while let Some(_) = *ref_value {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fn syntax_for() {
|
||||
let ref_value = &Some(23);
|
||||
let slice = &[(2, 3), (4, 2)];
|
||||
|
||||
// not ok
|
||||
for (_a, _b) in slice.iter() {}
|
||||
|
||||
// ok
|
||||
for &(_a, _b) in slice.iter() {}
|
||||
}
|
||||
|
||||
fn syntax_let() {
|
||||
let ref_value = &(2, 3);
|
||||
|
||||
// not ok
|
||||
let (_n, _m) = ref_value;
|
||||
|
||||
// ok
|
||||
let &(_n, _m) = ref_value;
|
||||
let (_n, _m) = *ref_value;
|
||||
}
|
||||
|
||||
fn syntax_fn() {
|
||||
// not ok
|
||||
fn foo((_a, _b): &(i32, i32)) {}
|
||||
|
||||
// ok
|
||||
fn foo_ok_1(&(_a, _b): &(i32, i32)) {}
|
||||
}
|
||||
|
||||
fn syntax_closure() {
|
||||
fn foo<F>(f: F)
|
||||
where
|
||||
F: FnOnce(&(i32, i32)),
|
||||
{
|
||||
}
|
||||
|
||||
// not ok
|
||||
foo(|(_a, _b)| ());
|
||||
|
||||
// ok
|
||||
foo(|&(_a, _b)| ());
|
||||
}
|
||||
|
||||
fn macro_with_expression() {
|
||||
macro_rules! matching_macro {
|
||||
($e:expr) => {
|
||||
$e
|
||||
};
|
||||
}
|
||||
let value = &Some(23);
|
||||
|
||||
// not ok
|
||||
matching_macro!(match value {
|
||||
Some(_) => (),
|
||||
_ => (),
|
||||
});
|
||||
|
||||
// ok
|
||||
matching_macro!(match value {
|
||||
&Some(_) => (),
|
||||
_ => (),
|
||||
});
|
||||
matching_macro!(match *value {
|
||||
Some(_) => (),
|
||||
_ => (),
|
||||
});
|
||||
}
|
||||
|
||||
fn macro_expansion() {
|
||||
macro_rules! matching_macro {
|
||||
($e:expr) => {
|
||||
// not ok
|
||||
match $e {
|
||||
Some(_) => (),
|
||||
_ => (),
|
||||
}
|
||||
|
||||
// ok
|
||||
match $e {
|
||||
&Some(_) => (),
|
||||
_ => (),
|
||||
}
|
||||
match *$e {
|
||||
Some(_) => (),
|
||||
_ => (),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let value = &Some(23);
|
||||
matching_macro!(value);
|
||||
}
|
79
tests/ui/pattern_type_mismatch/syntax.stderr
Normal file
79
tests/ui/pattern_type_mismatch/syntax.stderr
Normal file
@ -0,0 +1,79 @@
|
||||
error: type of pattern does not match the expression type
|
||||
--> $DIR/syntax.rs:11:9
|
||||
|
|
||||
LL | Some(_) => (),
|
||||
| ^^^^^^^
|
||||
|
|
||||
= note: `-D clippy::pattern-type-mismatch` implied by `-D warnings`
|
||||
= help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
|
||||
|
||||
error: type of pattern does not match the expression type
|
||||
--> $DIR/syntax.rs:30:12
|
||||
|
|
||||
LL | if let Some(_) = ref_value {}
|
||||
| ^^^^^^^
|
||||
|
|
||||
= help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
|
||||
|
||||
error: type of pattern does not match the expression type
|
||||
--> $DIR/syntax.rs:41:15
|
||||
|
|
||||
LL | while let Some(_) = ref_value {
|
||||
| ^^^^^^^
|
||||
|
|
||||
= help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
|
||||
|
||||
error: type of pattern does not match the expression type
|
||||
--> $DIR/syntax.rs:59:9
|
||||
|
|
||||
LL | for (_a, _b) in slice.iter() {}
|
||||
| ^^^^^^^^
|
||||
|
|
||||
= help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
|
||||
|
||||
error: type of pattern does not match the expression type
|
||||
--> $DIR/syntax.rs:69:9
|
||||
|
|
||||
LL | let (_n, _m) = ref_value;
|
||||
| ^^^^^^^^
|
||||
|
|
||||
= help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
|
||||
|
||||
error: type of pattern does not match the expression type
|
||||
--> $DIR/syntax.rs:78:12
|
||||
|
|
||||
LL | fn foo((_a, _b): &(i32, i32)) {}
|
||||
| ^^^^^^^^
|
||||
|
|
||||
= help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
|
||||
|
||||
error: type of pattern does not match the expression type
|
||||
--> $DIR/syntax.rs:92:10
|
||||
|
|
||||
LL | foo(|(_a, _b)| ());
|
||||
| ^^^^^^^^
|
||||
|
|
||||
= help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
|
||||
|
||||
error: type of pattern does not match the expression type
|
||||
--> $DIR/syntax.rs:108:9
|
||||
|
|
||||
LL | Some(_) => (),
|
||||
| ^^^^^^^
|
||||
|
|
||||
= help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
|
||||
|
||||
error: type of pattern does not match the expression type
|
||||
--> $DIR/syntax.rs:128:17
|
||||
|
|
||||
LL | Some(_) => (),
|
||||
| ^^^^^^^
|
||||
...
|
||||
LL | matching_macro!(value);
|
||||
| ----------------------- in this macro invocation
|
||||
|
|
||||
= help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
|
||||
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: aborting due to 9 previous errors
|
||||
|
Loading…
Reference in New Issue
Block a user