Emit diagnostic for rest array patterns without fixed-length arrays Add a new inference diagnostic for rest array patterns on arrays whose length is not known to be fixed, plumb it through hir and ide-diagnostics, and add handler tests. AI-assisted: Changes were prepared with GitHub Copilot (GPT-5.3-Codex), then reviewed and validated with local tests.
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index bbb8c99..c063d02 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs
@@ -297,6 +297,10 @@ #[type_visitable(ignore)] has_rest: bool, }, + ArrayPatternWithoutFixedLength { + #[type_visitable(ignore)] + pat: PatId, + }, ExpectedArrayOrSlicePat { #[type_visitable(ignore)] pat: PatId,
diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs index 8703b85..f1af8a0 100644 --- a/crates/hir-ty/src/infer/pat.rs +++ b/crates/hir-ty/src/infer/pat.rs
@@ -1701,7 +1701,7 @@ // We have a variable-length pattern and don't know the array length. // This happens if we have e.g., // `let [a, b, ..] = arr` where `arr: [T; N]` where `const N: usize`. - // FIXME: Emit an error: cannot pattern-match on an array without a fixed length. + self.push_diagnostic(InferenceDiagnostic::ArrayPatternWithoutFixedLength { pat }); }; // If we get here, we must have emitted an error.
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index d482135..2d2883e 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs
@@ -101,6 +101,7 @@ } diagnostics![AnyDiagnostic<'db> -> + ArrayPatternWithoutFixedLength, AwaitOutsideOfAsync, BreakOutsideOfLoop, CannotBeDereferenced<'db>, @@ -307,6 +308,11 @@ } #[derive(Debug)] +pub struct ArrayPatternWithoutFixedLength { + pub pat: InFile<ExprOrPatPtr>, +} + +#[derive(Debug)] pub struct ExpectedArrayOrSlicePat<'db> { pub pat: InFile<ExprOrPatPtr>, pub found: Type<'db>, @@ -836,6 +842,10 @@ let pat = pat_syntax(pat)?.map(Into::into); MismatchedArrayPatLen { pat, expected, found, has_rest }.into() } + &InferenceDiagnostic::ArrayPatternWithoutFixedLength { pat } => { + let pat = pat_syntax(pat)?.map(Into::into); + ArrayPatternWithoutFixedLength { pat }.into() + } InferenceDiagnostic::ExpectedArrayOrSlicePat { pat, found } => { let pat = pat_syntax(*pat)?.map(Into::into); ExpectedArrayOrSlicePat { pat, found: Type::new(db, def, found.as_ref()) }.into()
diff --git a/crates/ide-diagnostics/src/handlers/array_pattern_without_fixed_length.rs b/crates/ide-diagnostics/src/handlers/array_pattern_without_fixed_length.rs new file mode 100644 index 0000000..e7d0868 --- /dev/null +++ b/crates/ide-diagnostics/src/handlers/array_pattern_without_fixed_length.rs
@@ -0,0 +1,46 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: array-pattern-without-fixed-length +// +// This diagnostic is triggered when a rest array pattern is matched against an +// array with a non-constant length. +pub(crate) fn array_pattern_without_fixed_length( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::ArrayPatternWithoutFixedLength, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0730"), + "cannot pattern-match on an array without a fixed length", + d.pat.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn array_pattern_without_fixed_length() { + check_diagnostics( + r#" +fn f<const N: usize>(arr: [u8; N]) { + let [_head, _tail @ ..] = arr; + //^^^^^^^^^^^^^^^^^^^ error: cannot pattern-match on an array without a fixed length +} +"#, + ); + } + + #[test] + fn fixed_length_array_pattern_is_ok() { + check_diagnostics( + r#" +fn f(arr: [u8; 3]) { + let [_head, _tail @ ..] = arr; +} +"#, + ); + } +}
diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs index 9639772..18251bc 100644 --- a/crates/ide-diagnostics/src/lib.rs +++ b/crates/ide-diagnostics/src/lib.rs
@@ -29,6 +29,7 @@ extern crate rustc_driver as _; mod handlers { + pub(crate) mod array_pattern_without_fixed_length; pub(crate) mod await_outside_of_async; pub(crate) mod bad_rtn; pub(crate) mod break_outside_of_loop; @@ -437,6 +438,11 @@ AnyDiagnostic::CannotBeDereferenced(d) => handlers::cannot_be_dereferenced::cannot_be_dereferenced(&ctx, &d), AnyDiagnostic::CannotIndexInto(d) => handlers::cannot_index_into::cannot_index_into(&ctx, &d), AnyDiagnostic::CastToUnsized(d) => handlers::invalid_cast::cast_to_unsized(&ctx, &d), + AnyDiagnostic::ArrayPatternWithoutFixedLength(d) => { + handlers::array_pattern_without_fixed_length::array_pattern_without_fixed_length( + &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),