Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide-diagnostics/src/handlers/mutability_errors.rs')
-rw-r--r--crates/ide-diagnostics/src/handlers/mutability_errors.rs55
1 files changed, 38 insertions, 17 deletions
diff --git a/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/crates/ide-diagnostics/src/handlers/mutability_errors.rs
index 6fa0e7a5a8..1397979144 100644
--- a/crates/ide-diagnostics/src/handlers/mutability_errors.rs
+++ b/crates/ide-diagnostics/src/handlers/mutability_errors.rs
@@ -1,6 +1,7 @@
+use hir::db::ExpandDatabase;
use ide_db::source_change::SourceChange;
-use syntax::{AstNode, SyntaxKind, SyntaxNode, SyntaxToken, T};
-use text_edit::TextEdit;
+use ide_db::text_edit::TextEdit;
+use syntax::{ast, AstNode, SyntaxKind, SyntaxNode, SyntaxNodePtr, SyntaxToken, T};
use crate::{fix, Diagnostic, DiagnosticCode, DiagnosticsContext};
@@ -8,18 +9,27 @@ use crate::{fix, Diagnostic, DiagnosticCode, DiagnosticsContext};
//
// This diagnostic is triggered on mutating an immutable variable.
pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Option<Diagnostic> {
- if d.span.file_id.macro_file().is_some() {
- // FIXME: Our infra can't handle allow from within macro expansions rn
- return None;
- }
+ let root = ctx.sema.db.parse_or_expand(d.span.file_id);
+ let node = d.span.value.to_node(&root);
+ let mut span = d.span;
+ if let Some(parent) = node.parent() {
+ if ast::BinExpr::can_cast(parent.kind()) {
+ // In case of an assignment, the diagnostic is provided on the variable name.
+ // We want to expand it to include the whole assignment, but only when this
+ // is an ordinary assignment, not a destructuring assignment. So, the direct
+ // parent is an assignment expression.
+ span = d.span.with_value(SyntaxNodePtr::new(&parent));
+ }
+ };
+
let fixes = (|| {
if d.local.is_ref(ctx.sema.db) {
// There is no simple way to add `mut` to `ref x` and `ref mut x`
return None;
}
- let file_id = d.span.file_id.file_id()?;
+ let file_id = span.file_id.file_id()?;
let mut edit_builder = TextEdit::builder();
- let use_range = d.span.value.text_range();
+ let use_range = span.value.text_range();
for source in d.local.sources(ctx.sema.db) {
let Some(ast) = source.name() else { continue };
// FIXME: macros
@@ -33,6 +43,7 @@ pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Option
use_range,
)])
})();
+
Some(
Diagnostic::new_with_syntax_node_ptr(
ctx,
@@ -42,7 +53,7 @@ pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Option
"cannot mutate immutable variable `{}`",
d.local.name(ctx.sema.db).display(ctx.sema.db, ctx.edition)
),
- d.span,
+ span,
)
.with_fixes(fixes),
)
@@ -53,10 +64,6 @@ pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Option
// This diagnostic is triggered when a mutable variable isn't actually mutated.
pub(crate) fn unused_mut(ctx: &DiagnosticsContext<'_>, d: &hir::UnusedMut) -> Option<Diagnostic> {
let ast = d.local.primary_source(ctx.sema.db).syntax_ptr();
- if ast.file_id.macro_file().is_some() {
- // FIXME: Our infra can't handle allow from within macro expansions rn
- return None;
- }
let fixes = (|| {
let file_id = ast.file_id.file_id()?;
let mut edit_builder = TextEdit::builder();
@@ -937,7 +944,6 @@ fn fn_once(mut x: impl FnOnce(u8) -> u8) -> u8 {
#[test]
fn closure() {
- // FIXME: Diagnostic spans are inconsistent inside and outside closure
check_diagnostics(
r#"
//- minicore: copy, fn
@@ -950,11 +956,11 @@ fn fn_once(mut x: impl FnOnce(u8) -> u8) -> u8 {
fn f() {
let x = 5;
let closure1 = || { x = 2; };
- //^ 💡 error: cannot mutate immutable variable `x`
+ //^^^^^ 💡 error: cannot mutate immutable variable `x`
let _ = closure1();
//^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1`
let closure2 = || { x = x; };
- //^ 💡 error: cannot mutate immutable variable `x`
+ //^^^^^ 💡 error: cannot mutate immutable variable `x`
let closure3 = || {
let x = 2;
x = 5;
@@ -996,7 +1002,7 @@ fn f() {
|| {
let x = 2;
|| { || { x = 5; } }
- //^ 💡 error: cannot mutate immutable variable `x`
+ //^^^^^ 💡 error: cannot mutate immutable variable `x`
}
}
};
@@ -1283,4 +1289,19 @@ fn main() {
"#,
);
}
+
+ #[test]
+ fn destructuring_assignment_needs_mut() {
+ check_diagnostics(
+ r#"
+//- minicore: fn
+
+fn main() {
+ let mut var = 1;
+ let mut func = || (var,) = (2,);
+ func();
+}
+ "#,
+ );
+ }
}