Unnamed repository; edit this file 'description' to name the repository.
feat: add diagnostic for E0638
WaterWhisperer 3 weeks ago
parent 8d1edc4 · commit 25d1673
-rw-r--r--crates/hir-ty/src/infer.rs6
-rw-r--r--crates/hir-ty/src/infer/pat.rs4
-rw-r--r--crates/hir/src/diagnostics.rs11
-rw-r--r--crates/ide-diagnostics/src/handlers/non_exhaustive_record_pat.rs79
-rw-r--r--crates/ide-diagnostics/src/lib.rs4
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),