2023-02-26 06:34:41 -06:00
|
|
|
use ide_db::source_change::SourceChange;
|
|
|
|
use syntax::{AstNode, SyntaxKind, SyntaxNode, SyntaxToken, T};
|
|
|
|
use text_edit::TextEdit;
|
|
|
|
|
|
|
|
use crate::{fix, Diagnostic, DiagnosticsContext, Severity};
|
2023-02-21 13:00:38 -06:00
|
|
|
|
|
|
|
// Diagnostic: need-mut
|
|
|
|
//
|
|
|
|
// This diagnostic is triggered on mutating an immutable variable.
|
|
|
|
pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Diagnostic {
|
2023-02-26 06:34:41 -06:00
|
|
|
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 };
|
|
|
|
edit_builder.insert(ast.syntax().text_range().start(), "mut ".to_string());
|
|
|
|
}
|
|
|
|
let edit = edit_builder.finish();
|
|
|
|
Some(vec![fix(
|
|
|
|
"remove_mut",
|
|
|
|
"Remove unnecessary `mut`",
|
|
|
|
SourceChange::from_text_edit(file_id, edit),
|
|
|
|
use_range,
|
|
|
|
)])
|
|
|
|
})();
|
2023-02-21 13:00:38 -06:00
|
|
|
Diagnostic::new(
|
|
|
|
"need-mut",
|
|
|
|
format!("cannot mutate immutable variable `{}`", d.local.name(ctx.sema.db)),
|
|
|
|
ctx.sema.diagnostics_display_range(d.span.clone()).range,
|
|
|
|
)
|
2023-02-26 06:34:41 -06:00
|
|
|
.with_fixes(fixes)
|
2023-02-21 13:00:38 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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) -> Diagnostic {
|
2023-02-26 06:34:41 -06:00
|
|
|
let ast = d.local.primary_source(ctx.sema.db).syntax_ptr();
|
|
|
|
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();
|
2023-02-21 13:00:38 -06:00
|
|
|
Diagnostic::new(
|
|
|
|
"unused-mut",
|
|
|
|
"remove this `mut`",
|
2023-02-26 06:34:41 -06:00
|
|
|
ctx.sema.diagnostics_display_range(ast).range,
|
2023-02-21 13:00:38 -06:00
|
|
|
)
|
|
|
|
.severity(Severity::WeakWarning)
|
2023-02-26 06:34:41 -06:00
|
|
|
.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)
|
2023-02-21 13:00:38 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2023-02-26 06:34:41 -06:00
|
|
|
use crate::tests::{check_diagnostics, check_fix};
|
2023-02-21 13:00:38 -06:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn unused_mut_simple() {
|
|
|
|
check_diagnostics(
|
|
|
|
r#"
|
|
|
|
fn f(_: i32) {}
|
|
|
|
fn main() {
|
|
|
|
let mut x = 2;
|
2023-02-26 06:34:41 -06:00
|
|
|
//^^^^^ 💡 weak: remove this `mut`
|
2023-02-21 13:00:38 -06:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-02-26 06:34:41 -06:00
|
|
|
#[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`
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-02-21 13:00:38 -06:00
|
|
|
#[test]
|
|
|
|
fn field_mutate() {
|
|
|
|
check_diagnostics(
|
|
|
|
r#"
|
|
|
|
fn f(_: i32) {}
|
|
|
|
fn main() {
|
|
|
|
let mut x = (2, 7);
|
2023-02-26 06:34:41 -06:00
|
|
|
//^^^^^ 💡 weak: remove this `mut`
|
2023-02-21 13:00:38 -06:00
|
|
|
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;
|
2023-02-26 06:34:41 -06:00
|
|
|
//^^^^^^^ 💡 error: cannot mutate immutable variable `x`
|
2023-02-21 13:00:38 -06:00
|
|
|
f(x.1);
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn mutable_reference() {
|
|
|
|
check_diagnostics(
|
|
|
|
r#"
|
|
|
|
fn main() {
|
|
|
|
let mut x = &mut 2;
|
2023-02-26 06:34:41 -06:00
|
|
|
//^^^^^ 💡 weak: remove this `mut`
|
2023-02-21 13:00:38 -06:00
|
|
|
*x = 5;
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
check_diagnostics(
|
|
|
|
r#"
|
|
|
|
fn main() {
|
|
|
|
let x = 2;
|
|
|
|
&mut x;
|
2023-02-26 06:34:41 -06:00
|
|
|
//^^^^^^ 💡 error: cannot mutate immutable variable `x`
|
2023-02-21 13:00:38 -06:00
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
check_diagnostics(
|
|
|
|
r#"
|
|
|
|
fn main() {
|
|
|
|
let x_own = 2;
|
|
|
|
let ref mut x_ref = x_own;
|
2023-02-26 06:34:41 -06:00
|
|
|
//^^^^^^^^^^^^^ 💡 error: cannot mutate immutable variable `x_own`
|
2023-02-21 13:00:38 -06:00
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
check_diagnostics(
|
|
|
|
r#"
|
|
|
|
struct Foo;
|
|
|
|
impl Foo {
|
|
|
|
fn method(&mut self, x: i32) {}
|
|
|
|
}
|
|
|
|
fn main() {
|
|
|
|
let x = Foo;
|
|
|
|
x.method(2);
|
2023-02-26 06:34:41 -06:00
|
|
|
//^ 💡 error: cannot mutate immutable variable `x`
|
2023-02-21 13:00:38 -06:00
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn match_bindings() {
|
|
|
|
check_diagnostics(
|
|
|
|
r#"
|
|
|
|
fn main() {
|
|
|
|
match (2, 3) {
|
|
|
|
(x, mut y) => {
|
2023-02-26 06:34:41 -06:00
|
|
|
//^^^^^ 💡 weak: remove this `mut`
|
2023-02-21 13:00:38 -06:00
|
|
|
x = 7;
|
2023-02-26 06:34:41 -06:00
|
|
|
//^^^^^ 💡 error: cannot mutate immutable variable `x`
|
2023-02-21 13:00:38 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[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 mutablility 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.
|
|
|
|
check_diagnostics(
|
|
|
|
r#"
|
|
|
|
fn main() {
|
|
|
|
return;
|
|
|
|
let mut x = 2;
|
2023-02-26 06:34:41 -06:00
|
|
|
//^^^^^ 💡 weak: remove this `mut`
|
2023-02-21 13:00:38 -06:00
|
|
|
&mut x;
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
check_diagnostics(
|
|
|
|
r#"
|
|
|
|
fn main() {
|
|
|
|
loop {}
|
|
|
|
let mut x = 2;
|
2023-02-26 06:34:41 -06:00
|
|
|
//^^^^^ 💡 weak: remove this `mut`
|
2023-02-21 13:00:38 -06:00
|
|
|
&mut x;
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
check_diagnostics(
|
|
|
|
r#"
|
|
|
|
enum X {}
|
|
|
|
fn g() -> X {
|
|
|
|
loop {}
|
|
|
|
}
|
|
|
|
fn f() -> ! {
|
|
|
|
loop {}
|
|
|
|
}
|
|
|
|
fn main(b: bool) {
|
|
|
|
if b {
|
|
|
|
f();
|
|
|
|
} else {
|
|
|
|
g();
|
|
|
|
}
|
|
|
|
let mut x = 2;
|
2023-02-26 06:34:41 -06:00
|
|
|
//^^^^^ 💡 weak: remove this `mut`
|
2023-02-21 13:00:38 -06:00
|
|
|
&mut x;
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
check_diagnostics(
|
|
|
|
r#"
|
|
|
|
fn main(b: bool) {
|
|
|
|
if b {
|
|
|
|
loop {}
|
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
let mut x = 2;
|
2023-02-26 06:34:41 -06:00
|
|
|
//^^^^^ 💡 weak: remove this `mut`
|
2023-02-21 13:00:38 -06:00
|
|
|
&mut x;
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn initialization_is_not_mutation() {
|
|
|
|
check_diagnostics(
|
|
|
|
r#"
|
|
|
|
fn f(_: i32) {}
|
|
|
|
fn main() {
|
|
|
|
let mut x;
|
2023-02-26 06:34:41 -06:00
|
|
|
//^^^^^ 💡 weak: remove this `mut`
|
2023-02-21 13:00:38 -06:00
|
|
|
x = 5;
|
|
|
|
f(x);
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
check_diagnostics(
|
|
|
|
r#"
|
|
|
|
fn f(_: i32) {}
|
|
|
|
fn main(b: bool) {
|
|
|
|
let mut x;
|
2023-02-26 06:34:41 -06:00
|
|
|
//^^^^^ 💡 weak: remove this `mut`
|
2023-02-21 13:00:38 -06:00
|
|
|
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;
|
2023-02-26 06:34:41 -06:00
|
|
|
//^^^^^ 💡 error: cannot mutate immutable variable `x`
|
2023-02-21 13:00:38 -06:00
|
|
|
f(x);
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
check_diagnostics(
|
|
|
|
r#"
|
|
|
|
fn f(_: i32) {}
|
|
|
|
fn main() {
|
|
|
|
let x;
|
|
|
|
loop {
|
|
|
|
x = 1;
|
2023-02-26 06:34:41 -06:00
|
|
|
//^^^^^ 💡 error: cannot mutate immutable variable `x`
|
2023-02-21 13:00:38 -06:00
|
|
|
f(x);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
check_diagnostics(
|
|
|
|
r#"
|
|
|
|
fn f(_: i32) {}
|
|
|
|
fn main() {
|
|
|
|
loop {
|
|
|
|
let mut x = 1;
|
2023-02-26 06:34:41 -06:00
|
|
|
//^^^^^ 💡 weak: remove this `mut`
|
2023-02-21 13:00:38 -06:00
|
|
|
f(x);
|
|
|
|
if let mut y = 2 {
|
2023-02-26 06:34:41 -06:00
|
|
|
//^^^^^ 💡 weak: remove this `mut`
|
2023-02-21 13:00:38 -06:00
|
|
|
f(y);
|
|
|
|
}
|
|
|
|
match 3 {
|
|
|
|
mut z => f(z),
|
2023-02-26 06:34:41 -06:00
|
|
|
//^^^^^ 💡 weak: remove this `mut`
|
2023-02-21 13:00:38 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-02-26 06:34:41 -06:00
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn function_arguments_are_initialized() {
|
|
|
|
check_diagnostics(
|
|
|
|
r#"
|
|
|
|
fn f(mut x: i32) {
|
|
|
|
//^^^^^ 💡 weak: remove this `mut`
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
check_diagnostics(
|
|
|
|
r#"
|
|
|
|
fn f(x: i32) {
|
|
|
|
x = 5;
|
|
|
|
//^^^^^ 💡 error: cannot mutate immutable variable `x`
|
|
|
|
}
|
2023-03-02 01:48:50 -06:00
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn for_loop() {
|
|
|
|
check_diagnostics(
|
|
|
|
r#"
|
|
|
|
//- minicore: iterators
|
|
|
|
fn f(x: [(i32, u8); 10]) {
|
|
|
|
for (a, mut b) in x {
|
|
|
|
//^^^^^ 💡 weak: remove this `mut`
|
|
|
|
a = 2;
|
|
|
|
//^^^^^ 💡 error: cannot mutate immutable variable `a`
|
|
|
|
}
|
|
|
|
}
|
2023-02-21 13:00:38 -06:00
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|