Unnamed repository; edit this file 'description' to name the repository.
feat: add diagnostic for E0436
WaterWhisperer 10 days ago
parent 43e333a · commit 459a2e9
-rw-r--r--crates/hir-ty/src/infer.rs4
-rw-r--r--crates/hir-ty/src/infer/expr.rs8
-rw-r--r--crates/hir/src/diagnostics.rs9
-rw-r--r--crates/ide-diagnostics/src/handlers/functional_record_update_on_non_struct.rs55
-rw-r--r--crates/ide-diagnostics/src/lib.rs2
5 files changed, 76 insertions, 2 deletions
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index d28ee4ab44..4e1ac96646 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 b1e983f274..c89150554a 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -1213,7 +1213,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(
@@ -1222,7 +1224,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,