Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #22406 from hallmason17/feature/mut_ref_binding
feat(diagnostics): mut_ref binding feature diagnostic
| -rw-r--r-- | crates/hir-ty/src/infer.rs | 4 | ||||
| -rw-r--r-- | crates/hir-ty/src/infer/pat.rs | 4 | ||||
| -rw-r--r-- | crates/hir/src/diagnostics.rs | 10 | ||||
| -rw-r--r-- | crates/ide-diagnostics/src/handlers/mutable_ref.rs | 64 | ||||
| -rw-r--r-- | crates/ide-diagnostics/src/lib.rs | 2 |
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, |