use ide_db::source_change::SourceChange; use syntax::{AstNode, SyntaxKind, SyntaxNode, SyntaxToken, T}; use text_edit::TextEdit; use crate::{fix, Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: need-mut // // This diagnostic is triggered on mutating an immutable variable. pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Option { if d.span.file_id.macro_file().is_some() { // FIXME: Our infra can't handle allow from within macro expansions rn return None; } 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 mut edit_builder = TextEdit::builder(); let use_range = d.span.value.text_range(); for source in d.local.sources(ctx.sema.db) { let Some(ast) = source.name() else { continue }; // FIXME: macros edit_builder.insert(ast.value.syntax().text_range().start(), "mut ".to_owned()); } let edit = edit_builder.finish(); Some(vec![fix( "add_mut", "Change it to be mutable", SourceChange::from_text_edit(file_id, edit), use_range, )]) })(); Some( Diagnostic::new_with_syntax_node_ptr( ctx, // FIXME: `E0384` is not the only error that this diagnostic handles DiagnosticCode::RustcHardError("E0384"), format!( "cannot mutate immutable variable `{}`", d.local.name(ctx.sema.db).display(ctx.sema.db) ), d.span, ) .with_fixes(fixes), ) } // Diagnostic: unused-mut // // This diagnostic is triggered when a mutable variable isn't actually mutated. pub(crate) fn unused_mut(ctx: &DiagnosticsContext<'_>, d: &hir::UnusedMut) -> Option { 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(); let use_range = ast.value.text_range(); for source in d.local.sources(ctx.sema.db) { let ast = source.syntax(); let Some(mut_token) = token(ast, T![mut]) else { continue }; edit_builder.delete(mut_token.text_range()); if let Some(token) = mut_token.next_token() { if token.kind() == SyntaxKind::WHITESPACE { edit_builder.delete(token.text_range()); } } } let edit = edit_builder.finish(); Some(vec![fix( "remove_mut", "Remove unnecessary `mut`", SourceChange::from_text_edit(file_id, edit), use_range, )]) })(); let ast = d.local.primary_source(ctx.sema.db).syntax_ptr(); Some( Diagnostic::new_with_syntax_node_ptr( ctx, DiagnosticCode::RustcLint("unused_mut"), "variable does not need to be mutable", ast, ) .experimental() // Not supporting `#[allow(unused_mut)]` in proc macros leads to false positive. .with_fixes(fixes), ) } pub(super) fn token(parent: &SyntaxNode, kind: SyntaxKind) -> Option { parent.children_with_tokens().filter_map(|it| it.into_token()).find(|it| it.kind() == kind) } #[cfg(test)] mod tests { use crate::tests::{check_diagnostics, check_diagnostics_with_disabled, check_fix}; #[test] fn unused_mut_simple() { check_diagnostics( r#" fn f(_: i32) {} fn main() { let mut x = 2; //^^^^^ 💡 warn: variable does not need to be mutable f(x); } "#, ); } #[test] fn no_false_positive_simple() { check_diagnostics( r#" fn f(_: i32) {} fn main() { let x = 2; f(x); } "#, ); check_diagnostics( r#" fn f(_: i32) {} fn main() { let mut x = 2; x = 5; f(x); } "#, ); } #[test] fn multiple_errors_for_single_variable() { check_diagnostics( r#" fn f(_: i32) {} fn main() { let x = 2; x = 10; //^^^^^^ 💡 error: cannot mutate immutable variable `x` x = 5; //^^^^^ 💡 error: cannot mutate immutable variable `x` &mut x; //^^^^^^ 💡 error: cannot mutate immutable variable `x` f(x); } "#, ); } #[test] fn unused_mut_fix() { check_fix( r#" fn f(_: i32) {} fn main() { let mu$0t x = 2; f(x); } "#, r#" fn f(_: i32) {} fn main() { let x = 2; f(x); } "#, ); check_fix( r#" fn f(_: i32) {} fn main() { let ((mu$0t x, _) | (_, mut x)) = (2, 3); f(x); } "#, r#" fn f(_: i32) {} fn main() { let ((x, _) | (_, x)) = (2, 3); f(x); } "#, ); } #[test] fn need_mut_fix() { check_fix( r#" fn f(_: i32) {} fn main() { let x = 2; x$0 = 5; f(x); } "#, r#" fn f(_: i32) {} fn main() { let mut x = 2; x = 5; f(x); } "#, ); check_fix( r#" fn f(_: i32) {} fn main() { let ((x, _) | (_, x)) = (2, 3); x =$0 4; f(x); } "#, r#" fn f(_: i32) {} fn main() { let ((mut x, _) | (_, mut x)) = (2, 3); x = 4; f(x); } "#, ); check_fix( r#" struct Foo(i32); impl Foo { fn foo(self) { self = Fo$0o(5); } } "#, r#" struct Foo(i32); impl Foo { fn foo(mut self) { self = Foo(5); } } "#, ); } #[test] fn need_mut_fix_not_applicable_on_ref() { check_diagnostics( r#" fn main() { let ref x = 2; x = &5; //^^^^^^ error: cannot mutate immutable variable `x` } "#, ); check_diagnostics( r#" fn main() { let ref mut x = 2; x = &mut 5; //^^^^^^^^^^ error: cannot mutate immutable variable `x` } "#, ); } #[test] fn field_mutate() { check_diagnostics( r#" fn f(_: i32) {} fn main() { let mut x = (2, 7); //^^^^^ 💡 warn: variable does not need to be mutable f(x.1); } "#, ); check_diagnostics( r#" fn f(_: i32) {} fn main() { let mut x = (2, 7); x.0 = 5; f(x.1); } "#, ); check_diagnostics( r#" fn f(_: i32) {} fn main() { let x = (2, 7); x.0 = 5; //^^^^^^^ 💡 error: cannot mutate immutable variable `x` f(x.1); } "#, ); } #[test] fn mutable_reference() { check_diagnostics( r#" fn main() { let mut x = &mut 2; //^^^^^ 💡 warn: variable does not need to be mutable *x = 5; } "#, ); check_diagnostics( r#" fn main() { let x = 2; &mut x; //^^^^^^ 💡 error: cannot mutate immutable variable `x` } "#, ); check_diagnostics( r#" fn main() { let x_own = 2; let ref mut x_ref = x_own; //^^^^^^^^^^^^^ 💡 error: cannot mutate immutable variable `x_own` _ = x_ref; } "#, ); check_diagnostics( r#" struct Foo; impl Foo { fn method(&mut self, _x: i32) {} } fn main() { let x = Foo; x.method(2); //^ 💡 error: cannot mutate immutable variable `x` } "#, ); } #[test] fn regression_14310() { check_diagnostics( r#" //- minicore: copy, builtin_impls fn clone(mut i: &!) -> ! { //^^^^^ 💡 warn: variable does not need to be mutable *i } "#, ); } #[test] fn match_closure_capture() { check_diagnostics( r#" //- minicore: option fn main() { let mut v = &mut Some(2); //^^^^^ 💡 warn: variable does not need to be mutable let _ = || match v { Some(k) => { *k = 5; } None => {} }; let v = &mut Some(2); let _ = || match v { //^ 💡 error: cannot mutate immutable variable `v` ref mut k => { *k = &mut Some(5); } }; } "#, ); } #[test] fn match_bindings() { check_diagnostics( r#" fn main() { match (2, 3) { (x, mut y) => { //^^^^^ 💡 warn: variable does not need to be mutable x = 7; //^^^^^ 💡 error: cannot mutate immutable variable `x` _ = y; } } } "#, ); } #[test] fn mutation_in_dead_code() { // This one is interesting. Dead code is not represented at all in the MIR, so // there would be no mutability error for locals in dead code. Rustc tries to // not emit `unused_mut` in this case, but since it works without `mut`, and // special casing it is not trivial, we emit it. // Update: now MIR based `unused-variable` is taking over `unused-mut` for the same reason. check_diagnostics( r#" fn main() { return; let mut x = 2; //^^^^^ 💡 warn: unused variable &mut x; } "#, ); check_diagnostics( r#" fn main() { loop {} let mut x = 2; //^^^^^ 💡 warn: unused variable &mut x; } "#, ); check_diagnostics_with_disabled( r#" enum X {} fn g() -> X { loop {} } fn f() -> ! { loop {} } fn main(b: bool) { if b { f(); } else { g(); } let mut x = 2; //^^^^^ 💡 warn: unused variable &mut x; } "#, &["remove-unnecessary-else"], ); check_diagnostics_with_disabled( r#" fn main(b: bool) { if b { loop {} } else { return; } let mut x = 2; //^^^^^ 💡 warn: unused variable &mut x; } "#, &["remove-unnecessary-else"], ); } #[test] fn initialization_is_not_mutation() { check_diagnostics( r#" fn f(_: i32) {} fn main() { let mut x; //^^^^^ 💡 warn: variable does not need to be mutable x = 5; f(x); } "#, ); check_diagnostics( r#" fn f(_: i32) {} fn main(b: bool) { let mut x; //^^^^^ 💡 warn: variable does not need to be mutable if b { x = 1; } else { x = 3; } f(x); } "#, ); check_diagnostics( r#" fn f(_: i32) {} fn main(b: bool) { let x; if b { x = 1; } x = 3; //^^^^^ 💡 error: cannot mutate immutable variable `x` f(x); } "#, ); check_diagnostics( r#" fn f(_: i32) {} fn main() { let x; loop { x = 1; //^^^^^ 💡 error: cannot mutate immutable variable `x` f(x); } } "#, ); check_diagnostics( r#" fn check(_: i32) -> bool { false } fn main() { loop { let x = 1; if check(x) { break; } let y = (1, 2); if check(y.1) { return; } let z = (1, 2); match z { (k @ 5, ref mut t) if { continue; } => { //^^^^^^^^^ 💡 error: cannot mutate immutable variable `z` *t = 5; _ = k; } _ => { let y = (1, 2); if check(y.1) { return; } } } } } "#, ); check_diagnostics( r#" fn f(_: i32) {} fn main() { loop { let mut x = 1; //^^^^^ 💡 warn: variable does not need to be mutable f(x); if let mut y = 2 { //^^^^^ 💡 warn: variable does not need to be mutable f(y); } match 3 { mut z => f(z), //^^^^^ 💡 warn: variable does not need to be mutable } } } "#, ); } #[test] fn initialization_is_not_mutation_in_loop() { check_diagnostics( r#" fn main() { let a; loop { let c @ ( mut b, //^^^^^ 💡 warn: variable does not need to be mutable mut d //^^^^^ 💡 warn: variable does not need to be mutable ); a = 1; //^^^^^ 💡 error: cannot mutate immutable variable `a` b = 1; c = (2, 3); d = 3; _ = (c, b, d); } } "#, ); } #[test] fn function_arguments_are_initialized() { check_diagnostics( r#" fn f(mut x: i32) { //^^^^^ 💡 warn: variable does not need to be mutable f(x + 2); } "#, ); check_diagnostics( r#" fn f(x: i32) { x = 5; //^^^^^ 💡 error: cannot mutate immutable variable `x` } "#, ); check_diagnostics( r#" fn f((x, y): (i32, i32)) { let t = [0; 2]; x = 5; //^^^^^ 💡 error: cannot mutate immutable variable `x` _ = x; _ = y; _ = t; } "#, ); } #[test] fn no_diagnostics_in_case_of_multiple_bounds() { check_diagnostics( r#" fn f() { let (b, a, b) = (2, 3, 5); a = 8; //^^^^^ 💡 error: cannot mutate immutable variable `a` } "#, ); } #[test] fn for_loop() { check_diagnostics( r#" //- minicore: iterators, copy fn f(x: [(i32, u8); 10]) { for (a, mut b) in x { //^^^^^ 💡 warn: variable does not need to be mutable a = 2; //^^^^^ 💡 error: cannot mutate immutable variable `a` _ = b; } } "#, ); } #[test] fn while_let() { check_diagnostics( r#" //- minicore: iterators, copy fn f(x: [(i32, u8); 10]) { let mut it = x.into_iter(); while let Some((a, mut b)) = it.next() { //^^^^^ 💡 warn: variable does not need to be mutable while let Some((c, mut d)) = it.next() { //^^^^^ 💡 warn: variable does not need to be mutable a = 2; //^^^^^ 💡 error: cannot mutate immutable variable `a` c = 2; //^^^^^ 💡 error: cannot mutate immutable variable `c` _ = (b, d); } } } "#, ); } #[test] fn index() { check_diagnostics( r#" //- minicore: coerce_unsized, index, slice fn f() { let x = [1, 2, 3]; x[2] = 5; //^^^^^^^^ 💡 error: cannot mutate immutable variable `x` let x = &mut x; //^^^^^^ 💡 error: cannot mutate immutable variable `x` let mut x = x; //^^^^^ 💡 warn: variable does not need to be mutable x[2] = 5; } "#, ); } #[test] fn overloaded_index() { check_diagnostics( r#" //- minicore: index, copy use core::ops::{Index, IndexMut}; struct Foo; impl Index for Foo { type Output = (i32, u8); fn index(&self, _index: usize) -> &(i32, u8) { &(5, 2) } } impl IndexMut for Foo { fn index_mut(&mut self, _index: usize) -> &mut (i32, u8) { &mut (5, 2) } } fn f() { let mut x = Foo; //^^^^^ 💡 warn: variable does not need to be mutable let y = &x[2]; _ = (x, y); let x = Foo; let y = &mut x[2]; //^💡 error: cannot mutate immutable variable `x` _ = (x, y); let mut x = &mut Foo; //^^^^^ 💡 warn: variable does not need to be mutable let y: &mut (i32, u8) = &mut x[2]; _ = (x, y); let x = Foo; let ref mut y = x[7]; //^ 💡 error: cannot mutate immutable variable `x` _ = (x, y); let (ref mut y, _) = x[3]; //^ 💡 error: cannot mutate immutable variable `x` _ = y; match x[10] { //^ 💡 error: cannot mutate immutable variable `x` (ref y, 5) => _ = y, (_, ref mut y) => _ = y, } let mut x = Foo; let mut i = 5; //^^^^^ 💡 warn: variable does not need to be mutable let y = &mut x[i]; _ = y; } "#, ); } #[test] fn overloaded_deref() { check_diagnostics( r#" //- minicore: deref_mut, copy use core::ops::{Deref, DerefMut}; struct Foo; impl Deref for Foo { type Target = (i32, u8); fn deref(&self) -> &(i32, u8) { &(5, 2) } } impl DerefMut for Foo { fn deref_mut(&mut self) -> &mut (i32, u8) { &mut (5, 2) } } fn f() { let mut x = Foo; //^^^^^ 💡 warn: variable does not need to be mutable let y = &*x; _ = (x, y); let x = Foo; let y = &mut *x; //^^ 💡 error: cannot mutate immutable variable `x` _ = (x, y); let x = Foo; //^ 💡 warn: unused variable let x = Foo; let y: &mut (i32, u8) = &mut x; //^^^^^^ 💡 error: cannot mutate immutable variable `x` _ = (x, y); let ref mut y = *x; //^^ 💡 error: cannot mutate immutable variable `x` _ = y; let (ref mut y, _) = *x; //^^ 💡 error: cannot mutate immutable variable `x` _ = y; match *x { //^^ 💡 error: cannot mutate immutable variable `x` (ref y, 5) => _ = y, (_, ref mut y) => _ = y, } } "#, ); } #[test] fn or_pattern() { check_diagnostics( r#" //- minicore: option fn f(_: i32) {} fn main() { let ((Some(mut x), None) | (_, Some(mut x))) = (None, Some(7)) else { return }; //^^^^^ 💡 warn: variable does not need to be mutable f(x); } "#, ); check_diagnostics( r#" struct Foo(i32); const X: Foo = Foo(5); const Y: Foo = Foo(12); const fn f(mut a: Foo) -> bool { //^^^^^ 💡 warn: variable does not need to be mutable match a { X | Y => true, _ => false, } } "#, ); } #[test] fn or_pattern_no_terminator() { check_diagnostics( r#" enum Foo { A, B, C, D } use Foo::*; fn f(inp: (Foo, Foo, Foo, Foo)) { let ((A, B, _, x) | (B, C | D, x, _)) = inp else { return; }; x = B; //^^^^^ 💡 error: cannot mutate immutable variable `x` } "#, ); } #[test] // FIXME: We should have tests for `is_ty_uninhabited_from` fn regression_14421() { check_diagnostics( r#" pub enum Tree { Node(TreeNode), Leaf(TreeLeaf), } struct Box(&T); pub struct TreeNode { pub depth: usize, pub children: [Box; 8] } pub struct TreeLeaf { pub depth: usize, pub data: u8 } pub fn test() { let mut tree = Tree::Leaf( //^^^^^^^^ 💡 warn: variable does not need to be mutable TreeLeaf { depth: 0, data: 0 } ); _ = tree; } "#, ); } #[test] fn fn_traits() { check_diagnostics( r#" //- minicore: fn fn fn_ref(mut x: impl Fn(u8) -> u8) -> u8 { //^^^^^ 💡 warn: variable does not need to be mutable x(2) } fn fn_mut(x: impl FnMut(u8) -> u8) -> u8 { x(2) //^ 💡 error: cannot mutate immutable variable `x` } fn fn_borrow_mut(mut x: &mut impl FnMut(u8) -> u8) -> u8 { //^^^^^ 💡 warn: variable does not need to be mutable x(2) } fn fn_once(mut x: impl FnOnce(u8) -> u8) -> u8 { //^^^^^ 💡 warn: variable does not need to be mutable x(2) } "#, ); } #[test] fn closure() { // FIXME: Diagnostic spans are inconsistent inside and outside closure check_diagnostics( r#" //- minicore: copy, fn struct X; impl X { fn mutate(&mut self) {} } fn f() { let x = 5; let closure1 = || { x = 2; }; //^ 💡 error: cannot mutate immutable variable `x` let _ = closure1(); //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1` let closure2 = || { x = x; }; //^ 💡 error: cannot mutate immutable variable `x` let closure3 = || { let x = 2; x = 5; //^^^^^ 💡 error: cannot mutate immutable variable `x` x }; let x = X; let closure4 = || { x.mutate(); }; //^ 💡 error: cannot mutate immutable variable `x` _ = (closure2, closure3, closure4); } "#, ); check_diagnostics( r#" //- minicore: copy, fn fn f() { let mut x = 5; //^^^^^ 💡 warn: variable does not need to be mutable let mut y = 2; y = 7; let closure = || { let mut z = 8; z = 3; let mut k = z; //^^^^^ 💡 warn: variable does not need to be mutable _ = k; }; _ = (x, closure); } "#, ); check_diagnostics( r#" //- minicore: copy, fn fn f() { let closure = || { || { || { let x = 2; || { || { x = 5; } } //^ 💡 error: cannot mutate immutable variable `x` } } }; _ = closure; } "#, ); check_diagnostics( r#" //- minicore: copy, fn fn f() { struct X; let mut x = X; //^^^^^ 💡 warn: variable does not need to be mutable let c1 = || x; let mut x = X; let c2 = || { x = X; x }; let mut x = X; let c3 = move || { x = X; }; _ = (c1, c2, c3); } "#, ); check_diagnostics( r#" //- minicore: copy, fn, deref_mut struct X(i32, i64); fn f() { let mut x = &mut 5; //^^^^^ 💡 warn: variable does not need to be mutable let closure1 = || { *x = 2; }; let _ = closure1(); //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1` let mut x = &mut 5; //^^^^^ 💡 warn: variable does not need to be mutable let closure1 = || { *x = 2; &x; }; let _ = closure1(); //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1` let mut x = &mut 5; let closure1 = || { *x = 2; &x; x = &mut 3; }; let _ = closure1(); //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1` let mut x = &mut 5; //^^^^^ 💡 warn: variable does not need to be mutable let closure1 = move || { *x = 2; }; let _ = closure1(); //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1` let mut x = &mut X(1, 2); //^^^^^ 💡 warn: variable does not need to be mutable let closure1 = || { x.0 = 2; }; let _ = closure1(); //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1` } "#, ); } #[test] fn slice_pattern() { check_diagnostics( r#" //- minicore: coerce_unsized, deref_mut, slice, copy fn x(t: &[u8]) { match t { &[a, mut b] | &[a, _, mut b] => { //^^^^^ 💡 warn: variable does not need to be mutable a = 2; //^^^^^ 💡 error: cannot mutate immutable variable `a` _ = b; } _ => {} } } "#, ); } #[test] fn boxes() { check_diagnostics( r#" //- minicore: coerce_unsized, deref_mut, slice use core::ops::{Deref, DerefMut}; use core::{marker::Unsize, ops::CoerceUnsized}; #[lang = "owned_box"] pub struct Box { inner: *mut T, } impl Box { fn new(t: T) -> Self { #[rustc_box] Box::new(t) } } impl Deref for Box { type Target = T; fn deref(&self) -> &T { &**self } } impl DerefMut for Box { fn deref_mut(&mut self) -> &mut T { &mut **self } } fn f() { let x = Box::new(5); x = Box::new(7); //^^^^^^^^^^^^^^^ 💡 error: cannot mutate immutable variable `x` let x = Box::new(5); *x = 7; //^^^^^^ 💡 error: cannot mutate immutable variable `x` let mut y = Box::new(5); //^^^^^ 💡 warn: variable does not need to be mutable *x = *y; //^^^^^^^ 💡 error: cannot mutate immutable variable `x` let x = Box::new(5); let closure = || *x = 2; //^ 💡 error: cannot mutate immutable variable `x` _ = closure; } "#, ); } #[test] fn regression_15143() { check_diagnostics( r#" trait Tr { type Ty; } struct A; impl Tr for A { type Ty = (u32, i64); } struct B { f: ::Ty, } fn main(b: B) { let f = b.f.0; f = 5; //^^^^^ 💡 error: cannot mutate immutable variable `f` } "#, ); } #[test] fn allow_unused_mut_for_identifiers_starting_with_underline() { check_diagnostics( r#" fn f(_: i32) {} fn main() { let mut _x = 2; f(_x); } "#, ); } #[test] fn respect_lint_attributes_for_unused_mut() { check_diagnostics( r#" fn f(_: i32) {} fn main() { #[allow(unused_mut)] let mut x = 2; f(x); } fn main2() { #[deny(unused_mut)] let mut x = 2; //^^^^^ 💡 error: variable does not need to be mutable f(x); } "#, ); check_diagnostics( r#" macro_rules! mac { ($($x:expr),*$(,)*) => ({ #[allow(unused_mut)] let mut vec = 2; vec }); } fn main2() { let mut x = mac![]; //^^^^^ 💡 warn: variable does not need to be mutable _ = x; } "#, ); } #[test] fn regression_15099() { check_diagnostics( r#" //- minicore: iterator, range fn f() { loop {} for _ in 0..2 {} } "#, ); } #[test] fn regression_15623() { check_diagnostics( r#" //- minicore: fn struct Foo; impl Foo { fn needs_mut(&mut self) {} } fn foo(mut foo: Foo) { let mut call_me = || { let 0 = 1 else { return }; foo.needs_mut(); }; call_me(); } "#, ); } #[test] fn regression_15670() { check_diagnostics( r#" //- minicore: fn pub struct A {} pub unsafe fn foo(a: *mut A) { let mut b = || -> *mut A { &mut *a }; //^^^^^ 💡 warn: variable does not need to be mutable let _ = b(); } "#, ); } }