Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #22309 from WaterWhisperer/diag-E0436
feat: add diagnostic for E0436
| -rw-r--r-- | crates/hir-ty/src/infer.rs | 4 | ||||
| -rw-r--r-- | crates/hir-ty/src/infer/expr.rs | 8 | ||||
| -rw-r--r-- | crates/hir/src/diagnostics.rs | 9 | ||||
| -rw-r--r-- | crates/ide-diagnostics/src/handlers/functional_record_update_on_non_struct.rs | 55 | ||||
| -rw-r--r-- | crates/ide-diagnostics/src/lib.rs | 2 |
5 files changed, 76 insertions, 2 deletions
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 14da4288b5..e7f3827d84 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -389,6 +389,10 @@ pub enum InferenceDiagnostic { #[type_visitable(ignore)] expr: ExprId, }, + FunctionalRecordUpdateOnNonStruct { + #[type_visitable(ignore)] + base_expr: ExprId, + }, MismatchedArgCount { #[type_visitable(ignore)] call_expr: ExprId, diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index 3ccf7e5cc8..34fbaf6980 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -1214,7 +1214,9 @@ impl<'db> InferenceContext<'_, 'db> { // Check the base_expr, regardless of a bad expected adt_ty, so we can get // type errors on that expression, too. self.infer_expr_no_expect(base_expr, ExprIsRead::Yes); - // FIXME: Emit an error: functional update syntax on non-struct. + self.push_diagnostic( + InferenceDiagnostic::FunctionalRecordUpdateOnNonStruct { base_expr }, + ); } } else { self.infer_expr_suptype_coerce_never( @@ -1223,7 +1225,9 @@ impl<'db> InferenceContext<'_, 'db> { ExprIsRead::Yes, ); if !matches!(adt_id, AdtId::StructId(_)) { - // FIXME: Emit an error: functional update syntax on non-struct. + self.push_diagnostic( + InferenceDiagnostic::FunctionalRecordUpdateOnNonStruct { base_expr }, + ); } } } diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index b34c7b20c3..d56d4b7431 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs @@ -104,6 +104,7 @@ diagnostics![AnyDiagnostic<'db> -> BreakOutsideOfLoop, CastToUnsized<'db>, ExpectedFunction<'db>, + FunctionalRecordUpdateOnNonStruct, GenericDefaultRefersToSelf, InactiveCode, IncoherentImpl, @@ -302,6 +303,11 @@ pub struct ExpectedFunction<'db> { } #[derive(Debug)] +pub struct FunctionalRecordUpdateOnNonStruct { + pub base_expr: InFile<ExprOrPatPtr>, +} + +#[derive(Debug)] pub struct UnresolvedField<'db> { pub expr: InFile<ExprOrPatPtr>, pub receiver: Type<'db>, @@ -855,6 +861,9 @@ impl<'db> AnyDiagnostic<'db> { &InferenceDiagnostic::NonExhaustiveRecordExpr { expr } => { NonExhaustiveRecordExpr { expr: expr_syntax(expr)? }.into() } + &InferenceDiagnostic::FunctionalRecordUpdateOnNonStruct { base_expr } => { + FunctionalRecordUpdateOnNonStruct { base_expr: expr_syntax(base_expr)? }.into() + } InferenceDiagnostic::TypedHole { expr, expected } => { let expr = expr_syntax(*expr)?; TypedHole { expr, expected: Type::new(db, def, expected.as_ref()) }.into() diff --git a/crates/ide-diagnostics/src/handlers/functional_record_update_on_non_struct.rs b/crates/ide-diagnostics/src/handlers/functional_record_update_on_non_struct.rs new file mode 100644 index 0000000000..8b5a235bfb --- /dev/null +++ b/crates/ide-diagnostics/src/handlers/functional_record_update_on_non_struct.rs @@ -0,0 +1,55 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: functional-record-update-on-non-struct +// +// This diagnostic is triggered when functional record update syntax is used on +// something other than a struct. +pub(crate) fn functional_record_update_on_non_struct( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::FunctionalRecordUpdateOnNonStruct, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0436"), + "functional record update syntax requires a struct", + d.base_expr.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn enum_variant_record_update() { + check_diagnostics( + r#" +enum E { + V { x: i32, y: i32 }, +} + +fn f(e: E) { + let _ = E::V { x: 0, ..e }; + //^ error: functional record update syntax requires a struct +} +"#, + ); + } + + #[test] + fn struct_record_update() { + check_diagnostics( + r#" +struct S { + x: i32, + y: i32, +} + +fn f(s: S) { + let _ = S { x: 0, ..s }; +} +"#, + ); + } +} diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs index e2e465e26c..300b5e824d 100644 --- a/crates/ide-diagnostics/src/lib.rs +++ b/crates/ide-diagnostics/src/lib.rs @@ -35,6 +35,7 @@ mod handlers { pub(crate) mod duplicate_field; pub(crate) mod elided_lifetimes_in_path; pub(crate) mod expected_function; + pub(crate) mod functional_record_update_on_non_struct; pub(crate) mod generic_args_prohibited; pub(crate) mod generic_default_refers_to_self; pub(crate) mod inactive_code; @@ -425,6 +426,7 @@ pub fn semantic_diagnostics( 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::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) { Some(it) => it, None => continue, |