Unnamed repository; edit this file 'description' to name the repository.
| -rw-r--r-- | crates/hir-ty/src/infer.rs | 6 | ||||
| -rw-r--r-- | crates/hir-ty/src/infer/pat.rs | 4 | ||||
| -rw-r--r-- | crates/hir/src/diagnostics.rs | 11 | ||||
| -rw-r--r-- | crates/ide-diagnostics/src/handlers/non_exhaustive_record_pat.rs | 79 | ||||
| -rw-r--r-- | crates/ide-diagnostics/src/lib.rs | 4 |
5 files changed, 102 insertions, 2 deletions
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 8f17776d02..6aa647694e 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -365,6 +365,12 @@ pub enum InferenceDiagnostic { #[type_visitable(ignore)] expr: ExprId, }, + NonExhaustiveRecordPat { + #[type_visitable(ignore)] + pat: PatId, + #[type_visitable(ignore)] + variant: VariantId, + }, 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 8e87d22a45..a42d00786e 100644 --- a/crates/hir-ty/src/infer/pat.rs +++ b/crates/hir-ty/src/infer/pat.rs @@ -1155,7 +1155,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; fn check_record_pat_fields( &mut self, adt_ty: Ty<'db>, - _pat: PatId, + pat: PatId, variant: VariantId, fields: &[RecordFieldPat], has_rest_pat: bool, @@ -1233,7 +1233,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; // Require `..` if struct has non_exhaustive attribute. let non_exhaustive = self.has_applicable_non_exhaustive(variant.into()); if non_exhaustive && !has_rest_pat { - // FIXME: Emit an error. + self.push_diagnostic(InferenceDiagnostic::NonExhaustiveRecordPat { pat, variant }); } // Report an error if an incorrect number of fields was specified. diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index 77a17f1c76..9e4b929dd5 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs @@ -129,6 +129,7 @@ diagnostics![AnyDiagnostic<'db> -> NeedMut, NonExhaustiveLet, NonExhaustiveRecordExpr, + NonExhaustiveRecordPat, NoSuchField, MismatchedArrayPatLen, DuplicateField, @@ -407,6 +408,12 @@ pub struct NonExhaustiveRecordExpr { } #[derive(Debug)] +pub struct NonExhaustiveRecordPat { + pub pat: InFile<ExprOrPatPtr>, + pub variant: Variant, +} + +#[derive(Debug)] pub struct TypeMismatch<'db> { pub expr_or_pat: InFile<ExprOrPatPtr>, pub expected: Type<'db>, @@ -882,6 +889,10 @@ impl<'db> AnyDiagnostic<'db> { &InferenceDiagnostic::NonExhaustiveRecordExpr { expr } => { NonExhaustiveRecordExpr { expr: expr_syntax(expr)? }.into() } + &InferenceDiagnostic::NonExhaustiveRecordPat { pat, variant } => { + let pat = pat_syntax(pat)?.map(Into::into); + NonExhaustiveRecordPat { pat, variant: variant.into() }.into() + } &InferenceDiagnostic::FunctionalRecordUpdateOnNonStruct { base_expr } => { FunctionalRecordUpdateOnNonStruct { base_expr: expr_syntax(base_expr)? }.into() } diff --git a/crates/ide-diagnostics/src/handlers/non_exhaustive_record_pat.rs b/crates/ide-diagnostics/src/handlers/non_exhaustive_record_pat.rs new file mode 100644 index 0000000000..ea587e6037 --- /dev/null +++ b/crates/ide-diagnostics/src/handlers/non_exhaustive_record_pat.rs @@ -0,0 +1,79 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: non-exhaustive-record-pat +// +// This diagnostic is triggered if a record pattern destructures a `#[non_exhaustive]` +// struct or enum variant from another crate without `..`. +pub(crate) fn non_exhaustive_record_pat( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::NonExhaustiveRecordPat, +) -> Diagnostic { + let item = match d.variant { + hir::Variant::Struct(_) => "struct", + hir::Variant::Union(_) => "union", + hir::Variant::EnumVariant(_) => "variant", + }; + + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0638"), + format!("`..` required with {item} marked as non-exhaustive"), + d.pat.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn reports_external_non_exhaustive_struct_pattern_without_rest() { + check_diagnostics( + r#" +//- /lib.rs crate:lib +#[non_exhaustive] +pub struct S { + pub field: u32, +} + +fn local_ok(s: S) { + let S { field } = s; + let _ = field; +} + +//- /main.rs crate:main deps:lib +fn main(s: lib::S) { + let lib::S { field } = s; + //^^^^^^^^^^^^^^^^ error: `..` required with struct marked as non-exhaustive + let _ = field; +} +"#, + ); + } + + #[test] + fn reports_external_non_exhaustive_variant_pattern_without_rest() { + check_diagnostics( + r#" +//- /lib.rs crate:lib +pub enum E { + #[non_exhaustive] + V { field: u32 }, +} + +fn local_ok(e: E) { + let E::V { field } = e; + let _ = field; +} + +//- /main.rs crate:main deps:lib +fn main(e: lib::E) { + let lib::E::V { field } = e; + //^^^^^^^^^^^^^^^^^^^ error: `..` required with variant marked as non-exhaustive + let _ = field; +} +"#, + ); + } +} diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs index 967b965f51..0809054a2e 100644 --- a/crates/ide-diagnostics/src/lib.rs +++ b/crates/ide-diagnostics/src/lib.rs @@ -61,6 +61,7 @@ mod handlers { pub(crate) mod no_such_field; pub(crate) mod non_exhaustive_let; pub(crate) mod non_exhaustive_record_expr; + pub(crate) mod non_exhaustive_record_pat; pub(crate) mod parenthesized_generic_args_without_fn_trait; pub(crate) mod pattern_arg_in_extern_fn; pub(crate) mod private_assoc_item; @@ -467,6 +468,9 @@ pub fn semantic_diagnostics( AnyDiagnostic::NonExhaustiveRecordExpr(d) => { handlers::non_exhaustive_record_expr::non_exhaustive_record_expr(&ctx, &d) } + AnyDiagnostic::NonExhaustiveRecordPat(d) => { + handlers::non_exhaustive_record_pat::non_exhaustive_record_pat(&ctx, &d) + } AnyDiagnostic::NoSuchField(d) => handlers::no_such_field::no_such_field(&ctx, &d), AnyDiagnostic::DuplicateField(d) => handlers::duplicate_field::duplicate_field(&ctx, &d), AnyDiagnostic::PrivateAssocItem(d) => handlers::private_assoc_item::private_assoc_item(&ctx, &d), |