Skip to content

Commit 4ee433d

Browse files
Merge pull request #22406 from hallmason17/feature/mut_ref_binding
feat(diagnostics): mut_ref binding feature diagnostic
2 parents 0505d4a + e1ac75b commit 4ee433d

5 files changed

Lines changed: 82 additions & 2 deletions

File tree

crates/hir-ty/src/infer.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,10 @@ pub enum InferenceDiagnostic {
487487
#[type_visitable(ignore)]
488488
kind: ExplicitDropMethodUseKind,
489489
},
490+
MutableRefBinding {
491+
#[type_visitable(ignore)]
492+
pat: PatId,
493+
},
490494
}
491495

492496
#[derive(Debug, PartialEq, Eq, Clone)]

crates/hir-ty/src/infer/pat.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -893,14 +893,14 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
893893
let user_bind_annot = BindingMode::from_annotation(binding_data.mode);
894894
let bm = match user_bind_annot {
895895
BindingMode(ByRef::No, Mutability::Mut) if let ByRef::Yes(_) = def_br => {
896-
// Only mention the experimental `mut_ref` feature if if we're in edition 2024 and
896+
// Only mention the experimental `mut_ref` feature if we're in edition 2024 and
897897
// using other experimental matching features compatible with it.
898898
if self.edition.at_least_2024()
899899
&& (self.features.ref_pat_eat_one_layer_2024
900900
|| self.features.ref_pat_eat_one_layer_2024_structural)
901901
{
902902
if !self.features.mut_ref {
903-
// FIXME: Emit an error: binding cannot be both mutable and by-reference.
903+
self.push_diagnostic(InferenceDiagnostic::MutableRefBinding { pat });
904904
}
905905

906906
BindingMode(def_br, Mutability::Mut)

crates/hir/src/diagnostics.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ diagnostics![AnyDiagnostic<'db> ->
132132
MissingMatchArms,
133133
MissingUnsafe,
134134
MovedOutOfRef<'db>,
135+
MutableRefBinding,
135136
NeedMut,
136137
NonExhaustiveLet,
137138
NonExhaustiveRecordExpr,
@@ -640,6 +641,11 @@ pub struct UnimplementedTrait<'db> {
640641
pub root_trait_predicate: Option<crate::TraitPredicate<'db>>,
641642
}
642643

644+
#[derive(Debug)]
645+
pub struct MutableRefBinding {
646+
pub pat: InFile<ExprOrPatPtr>,
647+
}
648+
643649
impl<'db> AnyDiagnostic<'db> {
644650
pub(crate) fn body_validation_diagnostic(
645651
db: &'db dyn HirDatabase,
@@ -1081,6 +1087,10 @@ impl<'db> AnyDiagnostic<'db> {
10811087
};
10821088
ExplicitDropMethodUse { expr_or_path }.into()
10831089
}
1090+
InferenceDiagnostic::MutableRefBinding { pat } => {
1091+
let pat = pat_syntax(*pat)?.map(Into::into);
1092+
MutableRefBinding { pat }.into()
1093+
}
10841094
})
10851095
}
10861096

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
2+
3+
// Diagnostic: mutable-ref
4+
//
5+
// This diagnostic is triggered when binding is taken that is both mutable and by-reference.
6+
pub(crate) fn mutable_ref_binding(
7+
ctx: &DiagnosticsContext<'_, '_>,
8+
d: &hir::MutableRefBinding,
9+
) -> Diagnostic {
10+
Diagnostic::new_with_syntax_node_ptr(
11+
ctx,
12+
DiagnosticCode::RustcHardError("E0658"),
13+
"bindings cannot be both mutable and by-reference by default in 2024 edition. add experimental #![feature(mut_ref)] for this functionality",
14+
d.pat.map(Into::into),
15+
)
16+
.stable()
17+
}
18+
19+
#[cfg(test)]
20+
mod tests {
21+
use crate::tests::check_diagnostics;
22+
23+
#[test]
24+
fn mutable_ref_binding_missing_feature() {
25+
check_diagnostics(
26+
r#"
27+
//- minicore: option
28+
#![feature(ref_pat_eat_one_layer_2024)]
29+
struct TestStruct {
30+
val: i32
31+
}
32+
fn main() {
33+
let opt_ref = &Some(TestStruct {val: 1});
34+
35+
if let Some(mut x) = opt_ref {
36+
//^^^^^ error: bindings cannot be both mutable and by-reference by default in 2024 edition. add experimental #![feature(mut_ref)] for this functionality
37+
x = &TestStruct{val: 5};
38+
}
39+
}
40+
"#,
41+
);
42+
}
43+
44+
#[test]
45+
fn mutable_ref_binding_with_feature() {
46+
check_diagnostics(
47+
r#"
48+
//- minicore: option
49+
#![feature(ref_pat_eat_one_layer_2024)]
50+
#![feature(mut_ref)]
51+
struct TestStruct {
52+
val: i32
53+
}
54+
fn main() {
55+
let opt_ref = &Some(TestStruct{val: 1});
56+
57+
if let Some(mut x) = opt_ref {
58+
x = &TestStruct{val: 5};
59+
}
60+
}
61+
"#,
62+
);
63+
}
64+
}

crates/ide-diagnostics/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ mod handlers {
6363
pub(crate) mod missing_unsafe;
6464
pub(crate) mod moved_out_of_ref;
6565
pub(crate) mod mutability_errors;
66+
pub(crate) mod mutable_ref;
6667
pub(crate) mod no_such_field;
6768
pub(crate) mod non_exhaustive_let;
6869
pub(crate) mod non_exhaustive_record_expr;
@@ -468,6 +469,7 @@ pub fn semantic_diagnostics(
468469
AnyDiagnostic::MissingMatchArms(d) => handlers::missing_match_arms::missing_match_arms(&ctx, &d),
469470
AnyDiagnostic::MissingUnsafe(d) => handlers::missing_unsafe::missing_unsafe(&ctx, &d),
470471
AnyDiagnostic::MovedOutOfRef(d) => handlers::moved_out_of_ref::moved_out_of_ref(&ctx, &d),
472+
AnyDiagnostic::MutableRefBinding(d) => handlers::mutable_ref::mutable_ref_binding(&ctx, &d),
471473
AnyDiagnostic::NeedMut(d) => match handlers::mutability_errors::need_mut(&ctx, &d) {
472474
Some(it) => it,
473475
None => continue,

0 commit comments

Comments
 (0)