Unnamed repository; edit this file 'description' to name the repository.
feat: add diagnostic for E0529
| -rw-r--r-- | crates/hir-ty/src/infer.rs | 5 | ||||
| -rw-r--r-- | crates/hir-ty/src/infer/pat.rs | 5 | ||||
| -rw-r--r-- | crates/hir-ty/src/infer/unify.rs | 1 | ||||
| -rw-r--r-- | crates/hir-ty/src/mir/lower/pattern_matching.rs | 11 | ||||
| -rw-r--r-- | crates/hir/src/diagnostics.rs | 11 | ||||
| -rw-r--r-- | crates/ide-diagnostics/src/handlers/expected_array_or_slice_pat.rs | 50 | ||||
| -rw-r--r-- | crates/ide-diagnostics/src/lib.rs | 2 |
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) { |