Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #22238 from MavenRain/fixme-array-pat-incorrect-length
ide-diagnostics: emit error for mismatched array pattern length
| -rw-r--r-- | crates/hir-ty/src/infer.rs | 10 | ||||
| -rw-r--r-- | crates/hir-ty/src/infer/pat.rs | 14 | ||||
| -rw-r--r-- | crates/hir/src/diagnostics.rs | 13 | ||||
| -rw-r--r-- | crates/ide-diagnostics/src/handlers/mismatched_array_pat_len.rs | 114 | ||||
| -rw-r--r-- | crates/ide-diagnostics/src/lib.rs | 2 |
5 files changed, 151 insertions, 2 deletions
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 09c1325c41..0e307feaa8 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -319,6 +319,16 @@ pub enum InferenceDiagnostic { #[type_visitable(ignore)] variant: VariantId, }, + MismatchedArrayPatLen { + #[type_visitable(ignore)] + pat: PatId, + #[type_visitable(ignore)] + expected: u128, + #[type_visitable(ignore)] + found: u128, + #[type_visitable(ignore)] + has_rest: bool, + }, PrivateField { #[type_visitable(ignore)] expr: ExprId, diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs index a7b82dad4f..0b2c7dd745 100644 --- a/crates/hir-ty/src/infer/pat.rs +++ b/crates/hir-ty/src/infer/pat.rs @@ -1649,7 +1649,12 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; return (None, arr_ty); } - // FIXME: Emit an error: incorrect length. + self.push_diagnostic(InferenceDiagnostic::MismatchedArrayPatLen { + pat, + expected: len, + found: min_len, + has_rest: false, + }); } else if let Some(pat_len) = len.checked_sub(min_len) { // The variable-length pattern was there, // so it has an array type with the remaining elements left as its size... @@ -1657,7 +1662,12 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; } else { // ...however, in this case, there were no remaining elements. // That is, the slice pattern requires more than the array type offers. - // FIXME: Emit an error. + self.push_diagnostic(InferenceDiagnostic::MismatchedArrayPatLen { + pat, + expected: len, + found: min_len, + has_rest: true, + }); } } else if slice.is_none() { // We have a pattern with a fixed length, diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index 3259abb536..168854de74 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs @@ -81,6 +81,7 @@ diagnostics![AnyDiagnostic<'db> -> NonExhaustiveLet, NonExhaustiveRecordExpr, NoSuchField, + MismatchedArrayPatLen, PatternArgInExternFn, PrivateAssocItem, PrivateField, @@ -233,6 +234,14 @@ pub struct MismatchedTupleStructPatArgCount { } #[derive(Debug)] +pub struct MismatchedArrayPatLen { + pub pat: InFile<ExprOrPatPtr>, + pub expected: u128, + pub found: u128, + pub has_rest: bool, +} + +#[derive(Debug)] pub struct ExpectedFunction<'db> { pub call: InFile<ExprOrPatPtr>, pub found: Type<'db>, @@ -683,6 +692,10 @@ impl<'db> AnyDiagnostic<'db> { let private = private.map(|id| Field { id, parent: variant.into() }); NoSuchField { field: expr_or_pat, private, variant }.into() } + &InferenceDiagnostic::MismatchedArrayPatLen { pat, expected, found, has_rest } => { + let pat = pat_syntax(pat)?.map(Into::into); + MismatchedArrayPatLen { pat, expected, found, has_rest }.into() + } &InferenceDiagnostic::MismatchedArgCount { call_expr, expected, found } => { MismatchedArgCount { call_expr: expr_syntax(call_expr)?, expected, found }.into() } diff --git a/crates/ide-diagnostics/src/handlers/mismatched_array_pat_len.rs b/crates/ide-diagnostics/src/handlers/mismatched_array_pat_len.rs new file mode 100644 index 0000000000..8cae405c92 --- /dev/null +++ b/crates/ide-diagnostics/src/handlers/mismatched_array_pat_len.rs @@ -0,0 +1,114 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: mismatched-array-pat-len +// +// This diagnostic is triggered when an array pattern's element count does not +// match the array's declared length. +pub(crate) fn mismatched_array_pat_len( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::MismatchedArrayPatLen, +) -> Diagnostic { + let (code, message) = if d.has_rest { + ( + "E0528", + format!( + "pattern requires at least {} element{} but array has {}", + d.found, + if d.found == 1 { "" } else { "s" }, + d.expected, + ), + ) + } else { + ( + "E0527", + format!( + "pattern requires {} element{} but array has {}", + d.found, + if d.found == 1 { "" } else { "s" }, + d.expected, + ), + ) + }; + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError(code), + message, + d.pat.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn array_pattern_too_few_elements() { + check_diagnostics( + r#" +fn f(arr: [i32; 3]) { + let [_a, _b] = arr; + //^^^^^^^^ error: pattern requires 2 elements but array has 3 +} +"#, + ); + } + + #[test] + fn array_pattern_too_many_elements() { + check_diagnostics( + r#" +fn f(arr: [i32; 2]) { + let [_a, _b, _c] = arr; + //^^^^^^^^^^^^ error: pattern requires 3 elements but array has 2 +} +"#, + ); + } + + #[test] + fn array_pattern_with_rest_too_short() { + check_diagnostics( + r#" +fn f(arr: [i32; 2]) { + let [_a, _b, _c, ..] = arr; + //^^^^^^^^^^^^^^^^ error: pattern requires at least 3 elements but array has 2 +} +"#, + ); + } + + #[test] + fn array_pattern_with_rest_ok() { + check_diagnostics( + r#" +fn f(arr: [i32; 5]) { + let [_a, _b, ..] = arr; +} +"#, + ); + } + + #[test] + fn array_pattern_exact_length_ok() { + check_diagnostics( + r#" +fn f(arr: [i32; 3]) { + let [_a, _b, _c] = arr; +} +"#, + ); + } + + #[test] + fn array_pattern_singular_element_uses_singular() { + check_diagnostics( + r#" +fn f(arr: [i32; 3]) { + let [_a] = arr; + //^^^^ error: pattern requires 1 element but array has 3 +} +"#, + ); + } +} diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs index 1d5811954c..3f803a8c11 100644 --- a/crates/ide-diagnostics/src/lib.rs +++ b/crates/ide-diagnostics/src/lib.rs @@ -47,6 +47,7 @@ mod handlers { pub(crate) mod macro_error; pub(crate) mod malformed_derive; pub(crate) mod mismatched_arg_count; + pub(crate) mod mismatched_array_pat_len; pub(crate) mod missing_fields; pub(crate) mod missing_lifetime; pub(crate) mod missing_match_arms; @@ -426,6 +427,7 @@ pub fn semantic_diagnostics( }, AnyDiagnostic::MalformedDerive(d) => handlers::malformed_derive::malformed_derive(&ctx, &d), AnyDiagnostic::MismatchedArgCount(d) => handlers::mismatched_arg_count::mismatched_arg_count(&ctx, &d), + AnyDiagnostic::MismatchedArrayPatLen(d) => handlers::mismatched_array_pat_len::mismatched_array_pat_len(&ctx, &d), AnyDiagnostic::MissingFields(d) => handlers::missing_fields::missing_fields(&ctx, &d), AnyDiagnostic::MissingMatchArms(d) => handlers::missing_match_arms::missing_match_arms(&ctx, &d), AnyDiagnostic::MissingUnsafe(d) => handlers::missing_unsafe::missing_unsafe(&ctx, &d), |