mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-23 10:25:06 +00:00
9d3368f2c2
This PR touches a lot of parts. But the main changes are changing `hir_expand::Name` to be raw edition-dependently and only when necessary (unrelated to how the user originally wrote the identifier), and changing `is_keyword()` and `is_raw_identifier()` to be edition-aware (this was done in #17896, but the FIXMEs were fixed here). It is possible that I missed some cases, but most IDE parts should properly escape (or not escape) identifiers now. The rules of thumb are: - If we show the identifier to the user, its rawness should be determined by the edition of the edited crate. This is nice for IDE features, but really important for changes we insert to the source code. - For tests, I chose `Edition::CURRENT` (so we only have to (maybe) update tests when an edition becomes stable, to avoid churn). - For debugging tools (helper methods and logs), I used `Edition::LATEST`.
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, ctx.edition)
|
|
),
|
|
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();
|
|
}
|
|
"#,
|
|
);
|
|
}
|
|
}
|