Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--crates/hir-ty/src/infer.rs5
-rw-r--r--crates/hir-ty/src/infer/pat.rs5
-rw-r--r--crates/hir-ty/src/infer/unify.rs1
-rw-r--r--crates/hir-ty/src/mir/lower/pattern_matching.rs11
-rw-r--r--crates/hir/src/diagnostics.rs11
-rw-r--r--crates/ide-diagnostics/src/handlers/expected_array_or_slice_pat.rs50
-rw-r--r--crates/ide-diagnostics/src/lib.rs2
7 files changed, 83 insertions, 2 deletions
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index 48c0429700..1185073316 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -295,6 +295,11 @@ pub enum InferenceDiagnostic {
#[type_visitable(ignore)]
has_rest: bool,
},
+ ExpectedArrayOrSlicePat {
+ #[type_visitable(ignore)]
+ pat: PatId,
+ found: StoredTy,
+ },
DuplicateField {
#[type_visitable(ignore)]
field: ExprOrPatId,
diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs
index ca2a0d87f7..f21438647c 100644
--- a/crates/hir-ty/src/infer/pat.rs
+++ b/crates/hir-ty/src/infer/pat.rs
@@ -1620,7 +1620,10 @@ https://doc.rust-lang.org/reference/types.html#trait-objects";
TyKind::Slice(element_ty) => (element_ty, Some(expected), expected),
// The expected type must be an array or slice, but was neither, so error.
_ => {
- // FIXME: Emit an error: expected an array or a slice.
+ self.push_diagnostic(InferenceDiagnostic::ExpectedArrayOrSlicePat {
+ pat,
+ found: expected.store(),
+ });
let err = self.types.types.error;
(err, Some(err), err)
}
diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs
index 168da95600..f9ad76b0c1 100644
--- a/crates/hir-ty/src/infer/unify.rs
+++ b/crates/hir-ty/src/infer/unify.rs
@@ -578,6 +578,7 @@ pub(super) mod resolve_completely {
self.resolve_completely(diagnostic);
if let InferenceDiagnostic::ExpectedFunction { found: ty, .. }
+ | InferenceDiagnostic::ExpectedArrayOrSlicePat { found: ty, .. }
| InferenceDiagnostic::UnresolvedField { receiver: ty, .. }
| InferenceDiagnostic::UnresolvedMethodCall { receiver: ty, .. } = diagnostic
&& ty.as_ref().references_non_lt_error()
diff --git a/crates/hir-ty/src/mir/lower/pattern_matching.rs b/crates/hir-ty/src/mir/lower/pattern_matching.rs
index e7fa036f23..c924c5bdf0 100644
--- a/crates/hir-ty/src/mir/lower/pattern_matching.rs
+++ b/crates/hir-ty/src/mir/lower/pattern_matching.rs
@@ -248,9 +248,18 @@ impl<'db> MirLowerCtx<'_, 'db> {
(current, current_else)
}
Pat::Slice { prefix, slice, suffix } => {
+ let pat_ty = self.infer.pat_ty(pattern);
+ // FIXME: MIR lowering should be skipped for bodies with inference errors. Once
+ // that happens, this recovery for invalid slice patterns can be removed.
+ if !matches!(pat_ty.kind(), TyKind::Array(..) | TyKind::Slice(_)) {
+ return Err(MirLowerError::TypeError(
+ "non array or slice type matched with slice pattern",
+ ));
+ }
+
if mode == MatchingMode::Check {
// emit runtime length check for slice
- if let TyKind::Slice(_) = self.infer.pat_ty(pattern).kind() {
+ if let TyKind::Slice(_) = pat_ty.kind() {
let pattern_len = prefix.len() + suffix.len();
let place_len: Place = self
.temp(Ty::new_usize(self.interner()), current, pattern.into())?
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index d56d4b7431..a044f24587 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -103,6 +103,7 @@ diagnostics![AnyDiagnostic<'db> ->
AwaitOutsideOfAsync,
BreakOutsideOfLoop,
CastToUnsized<'db>,
+ ExpectedArrayOrSlicePat<'db>,
ExpectedFunction<'db>,
FunctionalRecordUpdateOnNonStruct,
GenericDefaultRefersToSelf,
@@ -297,6 +298,12 @@ pub struct MismatchedArrayPatLen {
}
#[derive(Debug)]
+pub struct ExpectedArrayOrSlicePat<'db> {
+ pub pat: InFile<ExprOrPatPtr>,
+ pub found: Type<'db>,
+}
+
+#[derive(Debug)]
pub struct ExpectedFunction<'db> {
pub call: InFile<ExprOrPatPtr>,
pub found: Type<'db>,
@@ -777,6 +784,10 @@ impl<'db> AnyDiagnostic<'db> {
let pat = pat_syntax(pat)?.map(Into::into);
MismatchedArrayPatLen { pat, expected, found, has_rest }.into()
}
+ InferenceDiagnostic::ExpectedArrayOrSlicePat { pat, found } => {
+ let pat = pat_syntax(*pat)?.map(Into::into);
+ ExpectedArrayOrSlicePat { pat, found: Type::new(db, def, found.as_ref()) }.into()
+ }
&InferenceDiagnostic::DuplicateField { field: expr, variant } => {
let expr_or_pat = match expr {
ExprOrPatId::ExprId(expr) => {
diff --git a/crates/ide-diagnostics/src/handlers/expected_array_or_slice_pat.rs b/crates/ide-diagnostics/src/handlers/expected_array_or_slice_pat.rs
new file mode 100644
index 0000000000..ab2c3ccd12
--- /dev/null
+++ b/crates/ide-diagnostics/src/handlers/expected_array_or_slice_pat.rs
@@ -0,0 +1,50 @@
+use hir::HirDisplay;
+
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
+
+// Diagnostic: expected-array-or-slice-pat
+//
+// This diagnostic is triggered when an array or slice pattern is matched
+// against a type that is neither an array nor a slice.
+pub(crate) fn expected_array_or_slice_pat(
+ ctx: &DiagnosticsContext<'_, '_>,
+ d: &hir::ExpectedArrayOrSlicePat<'_>,
+) -> Diagnostic {
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::RustcHardError("E0529"),
+ format!(
+ "expected an array or slice, found {}",
+ d.found.display(ctx.sema.db, ctx.display_target)
+ ),
+ d.pat.map(Into::into),
+ )
+ .stable()
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::check_diagnostics;
+
+ #[test]
+ fn expected_array_or_slice() {
+ check_diagnostics(
+ r#"
+fn f([_a, _b]: i32) {}
+ //^^^^^^^^ error: expected an array or slice, found i32
+"#,
+ );
+ }
+
+ #[test]
+ fn expected_array_or_slice_let_pattern() {
+ check_diagnostics(
+ r#"
+fn f(x: i32) {
+ let [_a, _b] = x;
+ //^^^^^^^^ error: expected an array or slice, found i32
+}
+"#,
+ );
+ }
+}
diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs
index 300b5e824d..aa8dc02de6 100644
--- a/crates/ide-diagnostics/src/lib.rs
+++ b/crates/ide-diagnostics/src/lib.rs
@@ -34,6 +34,7 @@ mod handlers {
pub(crate) mod break_outside_of_loop;
pub(crate) mod duplicate_field;
pub(crate) mod elided_lifetimes_in_path;
+ pub(crate) mod expected_array_or_slice_pat;
pub(crate) mod expected_function;
pub(crate) mod functional_record_update_on_non_struct;
pub(crate) mod generic_args_prohibited;
@@ -425,6 +426,7 @@ pub fn semantic_diagnostics(
let d = match diag {
AnyDiagnostic::AwaitOutsideOfAsync(d) => handlers::await_outside_of_async::await_outside_of_async(&ctx, &d),
AnyDiagnostic::CastToUnsized(d) => handlers::invalid_cast::cast_to_unsized(&ctx, &d),
+ AnyDiagnostic::ExpectedArrayOrSlicePat(d) => handlers::expected_array_or_slice_pat::expected_array_or_slice_pat(&ctx, &d),
AnyDiagnostic::ExpectedFunction(d) => handlers::expected_function::expected_function(&ctx, &d),
AnyDiagnostic::FunctionalRecordUpdateOnNonStruct(d) => handlers::functional_record_update_on_non_struct::functional_record_update_on_non_struct(&ctx, &d),
AnyDiagnostic::InactiveCode(d) => match handlers::inactive_code::inactive_code(&ctx, &d) {