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
| -rw-r--r-- | crates/hir-ty/src/infer.rs | 4 | ||||
| -rw-r--r-- | crates/hir-ty/src/infer/expr.rs | 2 | ||||
| -rw-r--r-- | crates/hir/src/diagnostics.rs | 9 | ||||
| -rw-r--r-- | crates/ide-diagnostics/src/handlers/non_exhaustive_record_expr.rs | 46 | ||||
| -rw-r--r-- | crates/ide-diagnostics/src/lib.rs | 4 |
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), |