Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #22193 from ChayimFriedman2/copilot/fix-22140-non-exhaustive-record-expr
feat: Emit a diagnostic for non_exhaustive struct when constructed
Chayim Refael Friedman 3 weeks ago
parent c2a4de0 · parent d2bd540 · commit 727477d
-rw-r--r--crates/hir-ty/src/infer.rs4
-rw-r--r--crates/hir-ty/src/infer/expr.rs2
-rw-r--r--crates/hir/src/diagnostics.rs9
-rw-r--r--crates/ide-diagnostics/src/handlers/non_exhaustive_record_expr.rs46
-rw-r--r--crates/ide-diagnostics/src/lib.rs4
5 files changed, 64 insertions, 1 deletions
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index 3a0f9f8048..5cccba1584 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -368,6 +368,10 @@ pub enum InferenceDiagnostic {
#[type_visitable(ignore)]
bad_value_break: bool,
},
+ NonExhaustiveRecordExpr {
+ #[type_visitable(ignore)]
+ 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 2b19c445ed..327975d766 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -997,7 +997,7 @@ impl<'db> InferenceContext<'_, 'db> {
// Prohibit struct expressions when non-exhaustive flag is set.
if self.has_applicable_non_exhaustive(variant.into()) {
- // FIXME: Emit an error.
+ self.push_diagnostic(InferenceDiagnostic::NonExhaustiveRecordExpr { expr });
}
self.check_record_expr_fields(adt_ty, expected, expr, variant, fields, base_expr);
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index 18f28541af..afafede6b9 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -77,6 +77,7 @@ diagnostics![AnyDiagnostic<'db> ->
MovedOutOfRef<'db>,
NeedMut,
NonExhaustiveLet,
+ NonExhaustiveRecordExpr,
NoSuchField,
PrivateAssocItem,
PrivateField,
@@ -316,6 +317,11 @@ pub struct NonExhaustiveLet {
}
#[derive(Debug)]
+pub struct NonExhaustiveRecordExpr {
+ pub expr: InFile<ExprOrPatPtr>,
+}
+
+#[derive(Debug)]
pub struct TypeMismatch<'db> {
pub expr_or_pat: InFile<ExprOrPatPtr>,
pub expected: Type<'db>,
@@ -726,6 +732,9 @@ impl<'db> AnyDiagnostic<'db> {
let expr = expr_syntax(expr)?;
BreakOutsideOfLoop { expr, is_break, bad_value_break }.into()
}
+ &InferenceDiagnostic::NonExhaustiveRecordExpr { expr } => {
+ NonExhaustiveRecordExpr { expr: expr_syntax(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/non_exhaustive_record_expr.rs b/crates/ide-diagnostics/src/handlers/non_exhaustive_record_expr.rs
new file mode 100644
index 0000000000..9dbce4d1f4
--- /dev/null
+++ b/crates/ide-diagnostics/src/handlers/non_exhaustive_record_expr.rs
@@ -0,0 +1,46 @@
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
+
+// Diagnostic: non-exhaustive-record-expr
+//
+// This diagnostic is triggered if a struct expression constructs a `#[non_exhaustive]`
+// struct from another crate.
+pub(crate) fn non_exhaustive_record_expr(
+ ctx: &DiagnosticsContext<'_>,
+ d: &hir::NonExhaustiveRecordExpr,
+) -> Diagnostic {
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::RustcHardError("E0639"),
+ "cannot create non-exhaustive struct using struct expression",
+ d.expr.map(|it| it.into()),
+ )
+ .stable()
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::check_diagnostics;
+
+ #[test]
+ fn reports_external_non_exhaustive_struct_literal() {
+ check_diagnostics(
+ r#"
+//- /lib.rs crate:lib
+#[non_exhaustive]
+pub struct S {
+ pub field: u32,
+}
+
+fn local_ok() {
+ let _ = S { field: 0 };
+}
+
+//- /main.rs crate:main deps:lib
+fn main() {
+ let _ = lib::S { field: 0 };
+ //^^^^^^^^^^^^^^^^^^^ error: cannot create non-exhaustive struct using struct expression
+}
+"#,
+ );
+ }
+}
diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs
index a083447335..300a6e6c7f 100644
--- a/crates/ide-diagnostics/src/lib.rs
+++ b/crates/ide-diagnostics/src/lib.rs
@@ -55,6 +55,7 @@ mod handlers {
pub(crate) mod mutability_errors;
pub(crate) mod no_such_field;
pub(crate) mod non_exhaustive_let;
+ pub(crate) mod non_exhaustive_record_expr;
pub(crate) mod parenthesized_generic_args_without_fn_trait;
pub(crate) mod private_assoc_item;
pub(crate) mod private_field;
@@ -431,6 +432,9 @@ pub fn semantic_diagnostics(
None => continue,
},
AnyDiagnostic::NonExhaustiveLet(d) => handlers::non_exhaustive_let::non_exhaustive_let(&ctx, &d),
+ AnyDiagnostic::NonExhaustiveRecordExpr(d) => {
+ handlers::non_exhaustive_record_expr::non_exhaustive_record_expr(&ctx, &d)
+ }
AnyDiagnostic::NoSuchField(d) => handlers::no_such_field::no_such_field(&ctx, &d),
AnyDiagnostic::PrivateAssocItem(d) => handlers::private_assoc_item::private_assoc_item(&ctx, &d),
AnyDiagnostic::PrivateField(d) => handlers::private_field::private_field(&ctx, &d),