Unnamed repository; edit this file 'description' to name the repository.
feat: add diagnostic for E0067
hehe six seven
Ada Alakbarova 4 weeks ago
parent 5f02138 · commit 49b4cd0
-rw-r--r--crates/hir-ty/src/infer.rs3
-rw-r--r--crates/hir-ty/src/infer/expr.rs3
-rw-r--r--crates/hir/src/diagnostics.rs10
-rw-r--r--crates/ide-diagnostics/src/handlers/invalid_lhs_of_assignment.rs90
-rw-r--r--crates/ide-diagnostics/src/lib.rs2
5 files changed, 106 insertions, 2 deletions
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index 54f334b66d..e50e1372e0 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -399,6 +399,9 @@ pub enum InferenceDiagnostic {
/// Whether the `GenericArgs` contains a `Self` arg.
has_self_arg: bool,
},
+ InvalidLhsOfAssignment {
+ lhs: ExprId,
+ },
}
/// A mismatch between an expected and an inferred type.
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index e84a03a2e7..8419c54047 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -248,13 +248,12 @@ impl<'db> InferenceContext<'_, 'db> {
}
}
- #[expect(clippy::needless_return)]
pub(crate) fn check_lhs_assignable(&self, lhs: ExprId) {
if self.is_syntactic_place_expr(lhs) {
return;
}
- // FIXME: Emit diagnostic.
+ self.push_diagnostic(InferenceDiagnostic::InvalidLhsOfAssignment { lhs });
}
fn infer_expr_coerce_never(
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index 473a25379a..1009a8b31b 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -63,6 +63,7 @@ diagnostics![AnyDiagnostic<'db> ->
IncorrectCase,
InvalidCast<'db>,
InvalidDeriveTarget,
+ InvalidLhsOfAssignment,
MacroDefError,
MacroError,
MacroExpansionParseError,
@@ -472,6 +473,11 @@ pub struct GenericDefaultRefersToSelf {
pub segment: InFile<AstPtr<ast::PathSegment>>,
}
+#[derive(Debug)]
+pub struct InvalidLhsOfAssignment {
+ pub lhs: InFile<AstPtr<Either<ast::Expr, ast::Pat>>>,
+}
+
impl<'db> AnyDiagnostic<'db> {
pub(crate) fn body_validation_diagnostic(
db: &'db dyn HirDatabase,
@@ -790,6 +796,10 @@ impl<'db> AnyDiagnostic<'db> {
let expected_kind = GenericArgKind::from_id(param_id);
IncorrectGenericsOrder { provided_arg, expected_kind }.into()
}
+ &InferenceDiagnostic::InvalidLhsOfAssignment { lhs } => {
+ let lhs = expr_syntax(lhs)?;
+ InvalidLhsOfAssignment { lhs }.into()
+ }
})
}
diff --git a/crates/ide-diagnostics/src/handlers/invalid_lhs_of_assignment.rs b/crates/ide-diagnostics/src/handlers/invalid_lhs_of_assignment.rs
new file mode 100644
index 0000000000..02716f2b86
--- /dev/null
+++ b/crates/ide-diagnostics/src/handlers/invalid_lhs_of_assignment.rs
@@ -0,0 +1,90 @@
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
+
+// Diagnostic: invalid-lhs-of-assignment
+//
+// This diagnostic is triggered if the left-hand side of an assignment can't be assigned to.
+pub(crate) fn invalid_lhs_of_assignment(
+ ctx: &DiagnosticsContext<'_>,
+ d: &hir::InvalidLhsOfAssignment,
+) -> Diagnostic {
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::RustcHardError("E0067"),
+ "invalid left-hand side of assignment",
+ d.lhs.map(Into::into),
+ )
+ .stable()
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::check_diagnostics;
+
+ #[test]
+ fn unit_struct_literal() {
+ check_diagnostics(
+ r#"
+//- minicore: add
+struct Struct;
+impl core::ops::AddAssign for Struct {
+ fn add_assign(&mut self, _other: Self) {}
+}
+fn test() {
+ Struct += Struct;
+ // ^^^^^^ error: invalid left-hand side of assignment
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn struct_literal() {
+ check_diagnostics(
+ r#"
+//- minicore: add
+struct Struct { foo: i32, bar: i32 }
+impl core::ops::AddAssign for Struct {
+ fn add_assign(&mut self, _other: Self) {}
+}
+fn test() {
+ Struct { foo: 0, bar: 0 } += Struct { foo: 1, bar: 2 };
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^ error: invalid left-hand side of assignment
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn destructuring_assignment() {
+ // no diagnostic, as `=` is not a _compound_ assignment
+ check_diagnostics(
+ r#"
+//- minicore: add
+struct Struct { foo: i32, bar: i32 }
+impl core::ops::AddAssign for Struct {
+ fn add_assign(&mut self, _other: Self) {}
+}
+fn test(mut foo: i32, mut bar: i32) {
+ Struct { foo, bar } = Struct { foo: 1, bar: 2 };
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn destructuring_compound_assignment() {
+ check_diagnostics(
+ r#"
+//- minicore: add
+struct Struct { foo: i32, bar: i32 }
+impl core::ops::AddAssign for Struct {
+ fn add_assign(&mut self, _other: Self) {}
+}
+fn test(foo: i32, bar: i32) {
+ Struct { foo, bar } += Struct { foo: 1, bar: 2 };
+ // ^^^^^^^^^^^^^^^^^^^ error: invalid left-hand side of assignment
+}
+ "#,
+ );
+ }
+}
diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs
index 54af3a35f4..74f0653660 100644
--- a/crates/ide-diagnostics/src/lib.rs
+++ b/crates/ide-diagnostics/src/lib.rs
@@ -43,6 +43,7 @@ mod handlers {
pub(crate) mod incorrect_generics_order;
pub(crate) mod invalid_cast;
pub(crate) mod invalid_derive_target;
+ pub(crate) mod invalid_lhs_of_assignment;
pub(crate) mod macro_error;
pub(crate) mod malformed_derive;
pub(crate) mod mismatched_arg_count;
@@ -479,6 +480,7 @@ pub fn semantic_diagnostics(
AnyDiagnostic::MissingLifetime(d) => handlers::missing_lifetime::missing_lifetime(&ctx, &d),
AnyDiagnostic::ElidedLifetimesInPath(d) => handlers::elided_lifetimes_in_path::elided_lifetimes_in_path(&ctx, &d),
AnyDiagnostic::GenericDefaultRefersToSelf(d) => handlers::generic_default_refers_to_self::generic_default_refers_to_self(&ctx, &d),
+ AnyDiagnostic::InvalidLhsOfAssignment(d) => handlers::invalid_lhs_of_assignment::invalid_lhs_of_assignment(&ctx, &d),
};
res.push(d)
}