mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-08 11:18:48 +00:00
1261 lines
27 KiB
Rust
1261 lines
27 KiB
Rust
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<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 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<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();
|
|
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<SyntaxToken> {
|
|
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<usize> for Foo {
|
|
type Output = (i32, u8);
|
|
fn index(&self, _index: usize) -> &(i32, u8) {
|
|
&(5, 2)
|
|
}
|
|
}
|
|
impl IndexMut<usize> 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>(&T);
|
|
|
|
pub struct TreeNode {
|
|
pub depth: usize,
|
|
pub children: [Box<Tree>; 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<T: ?Sized> {
|
|
inner: *mut T,
|
|
}
|
|
impl<T> Box<T> {
|
|
fn new(t: T) -> Self {
|
|
#[rustc_box]
|
|
Box::new(t)
|
|
}
|
|
}
|
|
|
|
impl<T: ?Sized> Deref for Box<T> {
|
|
type Target = T;
|
|
|
|
fn deref(&self) -> &T {
|
|
&**self
|
|
}
|
|
}
|
|
|
|
impl<T: ?Sized> DerefMut for Box<T> {
|
|
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<T: Tr> {
|
|
f: <T as Tr>::Ty,
|
|
}
|
|
|
|
fn main(b: B<A>) {
|
|
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();
|
|
}
|
|
"#,
|
|
);
|
|
}
|
|
}
|