Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--crates/hir-ty/src/infer.rs4
-rw-r--r--crates/hir-ty/src/infer/pat.rs4
-rw-r--r--crates/hir/src/diagnostics.rs10
-rw-r--r--crates/ide-diagnostics/src/handlers/mutable_ref.rs64
-rw-r--r--crates/ide-diagnostics/src/lib.rs2
5 files changed, 82 insertions, 2 deletions
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index db437788c0..bbb8c99d85 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -487,6 +487,10 @@ pub enum InferenceDiagnostic {
#[type_visitable(ignore)]
kind: ExplicitDropMethodUseKind,
},
+ MutableRefBinding {
+ #[type_visitable(ignore)]
+ pat: PatId,
+ },
}
#[derive(Debug, PartialEq, Eq, Clone)]
diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs
index 300c0cf214..8703b85e33 100644
--- a/crates/hir-ty/src/infer/pat.rs
+++ b/crates/hir-ty/src/infer/pat.rs
@@ -893,14 +893,14 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
let user_bind_annot = BindingMode::from_annotation(binding_data.mode);
let bm = match user_bind_annot {
BindingMode(ByRef::No, Mutability::Mut) if let ByRef::Yes(_) = def_br => {
- // Only mention the experimental `mut_ref` feature if if we're in edition 2024 and
+ // Only mention the experimental `mut_ref` feature if we're in edition 2024 and
// using other experimental matching features compatible with it.
if self.edition.at_least_2024()
&& (self.features.ref_pat_eat_one_layer_2024
|| self.features.ref_pat_eat_one_layer_2024_structural)
{
if !self.features.mut_ref {
- // FIXME: Emit an error: binding cannot be both mutable and by-reference.
+ self.push_diagnostic(InferenceDiagnostic::MutableRefBinding { pat });
}
BindingMode(def_br, Mutability::Mut)
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index 6b07834231..d482135812 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -132,6 +132,7 @@ diagnostics![AnyDiagnostic<'db> ->
MissingMatchArms,
MissingUnsafe,
MovedOutOfRef<'db>,
+ MutableRefBinding,
NeedMut,
NonExhaustiveLet,
NonExhaustiveRecordExpr,
@@ -640,6 +641,11 @@ pub struct UnimplementedTrait<'db> {
pub root_trait_predicate: Option<crate::TraitPredicate<'db>>,
}
+#[derive(Debug)]
+pub struct MutableRefBinding {
+ pub pat: InFile<ExprOrPatPtr>,
+}
+
impl<'db> AnyDiagnostic<'db> {
pub(crate) fn body_validation_diagnostic(
db: &'db dyn HirDatabase,
@@ -1081,6 +1087,10 @@ impl<'db> AnyDiagnostic<'db> {
};
ExplicitDropMethodUse { expr_or_path }.into()
}
+ InferenceDiagnostic::MutableRefBinding { pat } => {
+ let pat = pat_syntax(*pat)?.map(Into::into);
+ MutableRefBinding { pat }.into()
+ }
})
}
diff --git a/crates/ide-diagnostics/src/handlers/mutable_ref.rs b/crates/ide-diagnostics/src/handlers/mutable_ref.rs
new file mode 100644
index 0000000000..b56619612d
--- /dev/null
+++ b/crates/ide-diagnostics/src/handlers/mutable_ref.rs
@@ -0,0 +1,64 @@
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
+
+// Diagnostic: mutable-ref
+//
+// This diagnostic is triggered when binding is taken that is both mutable and by-reference.
+pub(crate) fn mutable_ref_binding(
+ ctx: &DiagnosticsContext<'_, '_>,
+ d: &hir::MutableRefBinding,
+) -> Diagnostic {
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::RustcHardError("E0658"),
+ "bindings cannot be both mutable and by-reference by default in 2024 edition. add experimental #![feature(mut_ref)] for this functionality",
+ d.pat.map(Into::into),
+ )
+ .stable()
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::check_diagnostics;
+
+ #[test]
+ fn mutable_ref_binding_missing_feature() {
+ check_diagnostics(
+ r#"
+//- minicore: option
+#![feature(ref_pat_eat_one_layer_2024)]
+struct TestStruct {
+ val: i32
+}
+fn main() {
+ let opt_ref = &Some(TestStruct {val: 1});
+
+ if let Some(mut x) = opt_ref {
+ //^^^^^ error: bindings cannot be both mutable and by-reference by default in 2024 edition. add experimental #![feature(mut_ref)] for this functionality
+ x = &TestStruct{val: 5};
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn mutable_ref_binding_with_feature() {
+ check_diagnostics(
+ r#"
+//- minicore: option
+#![feature(ref_pat_eat_one_layer_2024)]
+#![feature(mut_ref)]
+struct TestStruct {
+ val: i32
+}
+fn main() {
+ let opt_ref = &Some(TestStruct{val: 1});
+
+ if let Some(mut x) = opt_ref {
+ x = &TestStruct{val: 5};
+ }
+}
+"#,
+ );
+ }
+}
diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs
index 11ee669af0..9639772094 100644
--- a/crates/ide-diagnostics/src/lib.rs
+++ b/crates/ide-diagnostics/src/lib.rs
@@ -63,6 +63,7 @@ mod handlers {
pub(crate) mod missing_unsafe;
pub(crate) mod moved_out_of_ref;
pub(crate) mod mutability_errors;
+ pub(crate) mod mutable_ref;
pub(crate) mod no_such_field;
pub(crate) mod non_exhaustive_let;
pub(crate) mod non_exhaustive_record_expr;
@@ -468,6 +469,7 @@ pub fn semantic_diagnostics(
AnyDiagnostic::MissingMatchArms(d) => handlers::missing_match_arms::missing_match_arms(&ctx, &d),
AnyDiagnostic::MissingUnsafe(d) => handlers::missing_unsafe::missing_unsafe(&ctx, &d),
AnyDiagnostic::MovedOutOfRef(d) => handlers::moved_out_of_ref::moved_out_of_ref(&ctx, &d),
+ AnyDiagnostic::MutableRefBinding(d) => handlers::mutable_ref::mutable_ref_binding(&ctx, &d),
AnyDiagnostic::NeedMut(d) => match handlers::mutability_errors::need_mut(&ctx, &d) {
Some(it) => it,
None => continue,