Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #22379 from ada4a/diag-fru-in-destructuring-assignment
feat(diagnostics): add handler for functional record update in destructuring assignment
Laurențiu Nicola 3 weeks ago
parent f16060c · parent 17d5b43 · commit 2c88285
-rw-r--r--crates/hir-def/src/expr_store.rs1
-rw-r--r--crates/hir-def/src/expr_store/lower.rs10
-rw-r--r--crates/hir/src/diagnostics.rs6
-rw-r--r--crates/hir/src/lib.rs3
-rw-r--r--crates/ide-diagnostics/src/handlers/fru_in_destructuring_assignment.rs67
-rw-r--r--crates/ide-diagnostics/src/lib.rs2
6 files changed, 86 insertions, 3 deletions
diff --git a/crates/hir-def/src/expr_store.rs b/crates/hir-def/src/expr_store.rs
index 6d6e369cd6..fa33a00a80 100644
--- a/crates/hir-def/src/expr_store.rs
+++ b/crates/hir-def/src/expr_store.rs
@@ -332,6 +332,7 @@ pub enum ExpressionStoreDiagnostics {
AwaitOutsideOfAsync { node: InFile<AstPtr<ast::AwaitExpr>>, location: String },
UndeclaredLabel { node: InFile<AstPtr<ast::Lifetime>>, name: Name },
PatternArgInExternFn { node: InFile<AstPtr<ast::Pat>> },
+ FruInDestructuringAssignment { node: InFile<AstPtr<ast::Expr>> },
}
impl ExpressionStoreBuilder {
diff --git a/crates/hir-def/src/expr_store/lower.rs b/crates/hir-def/src/expr_store/lower.rs
index 33161c503e..48ccc1c0aa 100644
--- a/crates/hir-def/src/expr_store/lower.rs
+++ b/crates/hir-def/src/expr_store/lower.rs
@@ -1894,9 +1894,13 @@ impl<'db> ExprCollector<'db> {
};
let record_field_list = e.record_expr_field_list()?;
let ellipsis = record_field_list.dotdot_token().is_some();
- // We wanted to emit an error here if `record_field_list.spread().is_some()`,
- // but that's already a syntax error in rustc, so we decided not to.
- // See https://github.com/rust-lang/rust-analyzer/pull/22206#discussion_r3156097370
+ if let Some(spread) = record_field_list.spread() {
+ self.store.diagnostics.push(
+ ExpressionStoreDiagnostics::FruInDestructuringAssignment {
+ node: self.expander.in_file(AstPtr::new(&spread)),
+ },
+ );
+ }
let args = record_field_list
.fields()
.filter_map(|f| {
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index 7c128d6bce..f4a29a4bcc 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -106,6 +106,7 @@ diagnostics![AnyDiagnostic<'db> ->
CastToUnsized<'db>,
ExpectedArrayOrSlicePat<'db>,
ExpectedFunction<'db>,
+ FruInDestructuringAssignment,
FunctionalRecordUpdateOnNonStruct,
GenericDefaultRefersToSelf,
InactiveCode,
@@ -325,6 +326,11 @@ pub struct CannotBeDereferenced<'db> {
}
#[derive(Debug)]
+pub struct FruInDestructuringAssignment {
+ pub node: InFile<AstPtr<ast::Expr>>,
+}
+
+#[derive(Debug)]
pub struct FunctionalRecordUpdateOnNonStruct {
pub base_expr: InFile<ExprOrPatPtr>,
}
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index b31fac3cd2..df7743fdba 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -2424,6 +2424,9 @@ fn expr_store_diagnostics<'db>(
ExpressionStoreDiagnostics::PatternArgInExternFn { node } => {
PatternArgInExternFn { node: *node }.into()
}
+ ExpressionStoreDiagnostics::FruInDestructuringAssignment { node } => {
+ FruInDestructuringAssignment { node: *node }.into()
+ }
});
}
diff --git a/crates/ide-diagnostics/src/handlers/fru_in_destructuring_assignment.rs b/crates/ide-diagnostics/src/handlers/fru_in_destructuring_assignment.rs
new file mode 100644
index 0000000000..f8d3d80d62
--- /dev/null
+++ b/crates/ide-diagnostics/src/handlers/fru_in_destructuring_assignment.rs
@@ -0,0 +1,67 @@
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
+
+// Diagnostic: fru-in-destructuring-assignment
+//
+// This diagnostic is triggered when a destructuring assignment contains functional record update
+pub(crate) fn fru_in_destructuring_assignment(
+ ctx: &DiagnosticsContext<'_, '_>,
+ d: &hir::FruInDestructuringAssignment,
+) -> Diagnostic {
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::SyntaxError,
+ "functional record updates are not allowed in destructuring assignments",
+ d.node.map(Into::into),
+ )
+ .stable()
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::{check_diagnostics, check_diagnostics_with_disabled};
+
+ #[test]
+ fn spread_variable() {
+ check_diagnostics_with_disabled(
+ r#"
+struct Foo { bar: u32, baz: u32 }
+fn test(f: Foo, g: Foo, mut bar: u32, mut baz: u32) {
+ Foo { ..g } = f;
+ // ^ error: functional record updates are not allowed in destructuring assignments
+ Foo { bar, ..g } = f;
+ // ^ error: functional record updates are not allowed in destructuring assignments
+ Foo { bar, baz, ..g } = f;
+ // ^ error: functional record updates are not allowed in destructuring assignments
+}
+ "#,
+ // We don't end up using neither `bar` nor `baz`
+ &["unused_variables"],
+ );
+ }
+
+ #[test]
+ fn spread_default() {
+ check_diagnostics(
+ r#"
+struct Foo { bar: u32, baz: u32 }
+fn test(f: Foo) {
+ Foo { ..Default::default() } = f;
+ // ^^^^^^^^^^^^^^^^^^ error: functional record updates are not allowed in destructuring assignments
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn spread_struct() {
+ check_diagnostics(
+ r#"
+struct Foo { bar: u32, baz: u32 }
+fn test(f: Foo) {
+ Foo { ..Foo { bar: 0, baz: 0 } } = f;
+ // ^^^^^^^^^^^^^^^^^^^^^^ error: functional record updates are not allowed in destructuring assignments
+}
+ "#,
+ );
+ }
+}
diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs
index 459728592f..d38780ede2 100644
--- a/crates/ide-diagnostics/src/lib.rs
+++ b/crates/ide-diagnostics/src/lib.rs
@@ -37,6 +37,7 @@ mod handlers {
pub(crate) mod elided_lifetimes_in_path;
pub(crate) mod expected_array_or_slice_pat;
pub(crate) mod expected_function;
+ pub(crate) mod fru_in_destructuring_assignment;
pub(crate) mod functional_record_update_on_non_struct;
pub(crate) mod generic_args_prohibited;
pub(crate) mod generic_default_refers_to_self;
@@ -533,6 +534,7 @@ pub fn semantic_diagnostics(
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::UnimplementedTrait(d) => handlers::unimplemented_trait::unimplemented_trait(&ctx, &d),
+ AnyDiagnostic::FruInDestructuringAssignment(d) => handlers::fru_in_destructuring_assignment::fru_in_destructuring_assignment(&ctx, &d),
};
res.push(d)
}