Unnamed repository; edit this file 'description' to name the repository.
| -rw-r--r-- | crates/hir-def/src/expr_store.rs | 1 | ||||
| -rw-r--r-- | crates/hir-def/src/expr_store/lower.rs | 10 | ||||
| -rw-r--r-- | crates/hir/src/diagnostics.rs | 6 | ||||
| -rw-r--r-- | crates/hir/src/lib.rs | 3 | ||||
| -rw-r--r-- | crates/ide-diagnostics/src/handlers/fru_in_destructuring_assignment.rs | 67 | ||||
| -rw-r--r-- | crates/ide-diagnostics/src/lib.rs | 2 |
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) } |