diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index c063d0211f15..d9486fb0eb5d 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -375,6 +375,14 @@ pub enum InferenceDiagnostic { #[type_visitable(ignore)] variant: VariantId, }, + UnionPatMustHaveExactlyOneField { + #[type_visitable(ignore)] + pat: PatId, + }, + UnionPatHasRest { + #[type_visitable(ignore)] + pat: PatId, + }, FunctionalRecordUpdateOnNonStruct { #[type_visitable(ignore)] base_expr: ExprId, diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs index f1af8a0b73a5..2583054fcdf6 100644 --- a/crates/hir-ty/src/infer/pat.rs +++ b/crates/hir-ty/src/infer/pat.rs @@ -1241,10 +1241,10 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; // Report an error if an incorrect number of fields was specified. if matches!(variant, VariantId::UnionId(_)) { if fields.len() != 1 { - // FIXME: Emit an error, unions can't have more than one field. + self.push_diagnostic(InferenceDiagnostic::UnionPatMustHaveExactlyOneField { pat }); } if has_rest_pat { - // FIXME: Emit an error, unions can't have a rest pat. + self.push_diagnostic(InferenceDiagnostic::UnionPatHasRest { pat }); } } else if !unmentioned_fields.is_empty() && !has_rest_pat { // FIXME: Emit an error. diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index 2d2883eb60e1..f21f90677285 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs @@ -174,6 +174,8 @@ diagnostics![AnyDiagnostic<'db> -> ElidedLifetimesInPath, TypeMustBeKnown<'db>, UnionExprMustHaveExactlyOneField, + UnionPatMustHaveExactlyOneField, + UnionPatHasRest, UnimplementedTrait<'db>, ]; @@ -625,6 +627,16 @@ pub struct UnionExprMustHaveExactlyOneField { pub expr: InFile, } +#[derive(Debug)] +pub struct UnionPatMustHaveExactlyOneField { + pub pat: InFile, +} + +#[derive(Debug)] +pub struct UnionPatHasRest { + pub pat: InFile, +} + #[derive(Debug)] pub struct InvalidLhsOfAssignment { pub lhs: InFile>>, @@ -942,6 +954,14 @@ impl<'db> AnyDiagnostic<'db> { let pat = pat_syntax(pat)?.map(Into::into); NonExhaustiveRecordPat { pat, variant: variant.into() }.into() } + &InferenceDiagnostic::UnionPatMustHaveExactlyOneField { pat } => { + let pat = pat_syntax(pat)?.map(Into::into); + UnionPatMustHaveExactlyOneField { pat }.into() + } + &InferenceDiagnostic::UnionPatHasRest { pat } => { + let pat = pat_syntax(pat)?.map(Into::into); + UnionPatHasRest { pat }.into() + } &InferenceDiagnostic::FunctionalRecordUpdateOnNonStruct { base_expr } => { FunctionalRecordUpdateOnNonStruct { base_expr: expr_syntax(base_expr)? }.into() } diff --git a/crates/ide-diagnostics/src/handlers/union_pat_has_rest.rs b/crates/ide-diagnostics/src/handlers/union_pat_has_rest.rs new file mode 100644 index 000000000000..933311871948 --- /dev/null +++ b/crates/ide-diagnostics/src/handlers/union_pat_has_rest.rs @@ -0,0 +1,41 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: union-pat-has-rest +// +// A union pattern uses `..`. +pub(crate) fn union_pat_has_rest( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::UnionPatHasRest, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0784"), + "union patterns cannot use `..`", + d.pat.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn union_pat_has_rest() { + check_diagnostics( + r#" +union Bird { + pigeon: u8, + turtledove: u16, +} + +fn main(bird: Bird) { + unsafe { + let Bird { pigeon: 0, .. } = bird; + //^^^^^^^^^^^^^^^^^^^^^^ error: union patterns cannot use `..` + } +} +"#, + ); + } +} diff --git a/crates/ide-diagnostics/src/handlers/union_pat_must_have_exactly_one_field.rs b/crates/ide-diagnostics/src/handlers/union_pat_must_have_exactly_one_field.rs new file mode 100644 index 000000000000..5c229211e1ff --- /dev/null +++ b/crates/ide-diagnostics/src/handlers/union_pat_must_have_exactly_one_field.rs @@ -0,0 +1,43 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: union-pat-must-have-exactly-one-field +// +// A union pattern does not have exactly one field. +pub(crate) fn union_pat_must_have_exactly_one_field( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::UnionPatMustHaveExactlyOneField, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0784"), + "union patterns should have exactly one field", + d.pat.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn union_pat_must_have_exactly_one_field() { + check_diagnostics( + r#" +union Bird { + pigeon: u8, + turtledove: u16, +} + +fn main(bird: Bird) { + unsafe { + let Bird {} = bird; + //^^^^^^^ error: union patterns should have exactly one field + let Bird { pigeon: 0, turtledove: 1 } = bird; + //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: union patterns should have exactly one field + } +} +"#, + ); + } +} diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs index 18251bc8a2dd..62b5106ada50 100644 --- a/crates/ide-diagnostics/src/lib.rs +++ b/crates/ide-diagnostics/src/lib.rs @@ -87,6 +87,8 @@ mod handlers { pub(crate) mod unimplemented_builtin_macro; pub(crate) mod unimplemented_trait; pub(crate) mod union_expr_must_have_exactly_one_field; + pub(crate) mod union_pat_has_rest; + pub(crate) mod union_pat_must_have_exactly_one_field; pub(crate) mod unreachable_label; pub(crate) mod unresolved_assoc_item; pub(crate) mod unresolved_extern_crate; @@ -544,6 +546,12 @@ pub fn semantic_diagnostics( AnyDiagnostic::TypeMustBeKnown(d) => handlers::type_must_be_known::type_must_be_known(&ctx, &d), AnyDiagnostic::PatternArgInExternFn(d) => handlers::pattern_arg_in_extern_fn::pattern_arg_in_extern_fn(&ctx, &d), AnyDiagnostic::UnionExprMustHaveExactlyOneField(d) => handlers::union_expr_must_have_exactly_one_field::union_expr_must_have_exactly_one_field(&ctx, &d), + AnyDiagnostic::UnionPatMustHaveExactlyOneField(d) => { + handlers::union_pat_must_have_exactly_one_field::union_pat_must_have_exactly_one_field(&ctx, &d) + } + AnyDiagnostic::UnionPatHasRest(d) => { + handlers::union_pat_has_rest::union_pat_has_rest(&ctx, &d) + } AnyDiagnostic::UnimplementedTrait(d) => handlers::unimplemented_trait::unimplemented_trait(&ctx, &d), AnyDiagnostic::FruInDestructuringAssignment(d) => handlers::fru_in_destructuring_assignment::fru_in_destructuring_assignment(&ctx, &d), AnyDiagnostic::ExplicitDropMethodUse(d) => handlers::explicit_drop_method_use::explicit_drop_method_use(&ctx, &d),