Add ide-assist: toggle_lint_attr

Change lint attribute level.

Example
---
```
#[$0allow(dead_code)]
fn foo() {}
```
->
```
#[expect(dead_code)]
fn foo() {}
```

---

```
#[$0forbid(dead_code)]
fn foo() {}
```
->
```
#[deny(dead_code)]
fn foo() {}
```
This commit is contained in:
A4-Tacks 2025-08-28 18:37:52 +08:00
parent 1f4e5e82ff
commit d1523c41a5
No known key found for this signature in database
GPG key ID: 86AC1F526BA06668
3 changed files with 148 additions and 0 deletions

View file

@ -0,0 +1,116 @@
use ide_db::assists::AssistId;
use syntax::{AstNode, ast};
use crate::{AssistContext, Assists};
// Assist: toggle_lint_attr
//
// Change lint attribute level.
//
// ```
// #[$0allow(dead_code)]
// fn foo() {}
// ```
// ->
// ```
// #[expect(dead_code)]
// fn foo() {}
// ```
//
// ---
//
// ```
// #[$0forbid(dead_code)]
// fn foo() {}
// ```
// ->
// ```
// #[deny(dead_code)]
// fn foo() {}
// ```
pub(crate) fn toggle_lint_attr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
let path = ctx.find_node_at_offset::<ast::Path>()?;
let name = path.as_single_name_ref()?;
let _meta = ast::Meta::cast(path.syntax().parent()?)?;
let target = name.syntax().text_range();
for &toggled_name in toggled_lint(&name)? {
acc.add(
AssistId::refactor("toggle_lint_attr"),
format!("Replace `{name}` to `{toggled_name}`"),
target,
|builder| {
builder.replace(target, toggled_name);
},
)?;
}
Some(())
}
fn toggled_lint(name: &ast::NameRef) -> Option<&[&'static str]> {
Some(match name.text().as_str() {
"allow" => &["expect", "warn"],
"expect" => &["allow"],
"warn" => &["deny", "allow"],
"deny" => &["forbid", "warn"],
"forbid" => &["deny"],
_ => return None,
})
}
#[cfg(test)]
mod tests {
use crate::tests::{check_assist, check_assist_not_applicable};
use super::*;
#[test]
fn test_toggle_lint_attr() {
check_assist(
toggle_lint_attr,
"
#[$0allow(unused_mut)]
fn foo() {}
",
"
#[expect(unused_mut)]
fn foo() {}
",
);
check_assist(
toggle_lint_attr,
"
#[$0expect(unused_mut)]
fn foo() {}
",
"
#[allow(unused_mut)]
fn foo() {}
",
);
check_assist(
toggle_lint_attr,
"
#[$0forbid(unused_mut)]
fn foo() {}
",
"
#[deny(unused_mut)]
fn foo() {}
",
);
}
#[test]
fn test_toggle_lint_attr_not_applicable_other_attr() {
check_assist_not_applicable(
toggle_lint_attr,
"
#[$0inline(never)]
fn foo() {}
",
);
}
}

View file

@ -222,6 +222,7 @@ mod handlers {
mod term_search; mod term_search;
mod toggle_async_sugar; mod toggle_async_sugar;
mod toggle_ignore; mod toggle_ignore;
mod toggle_lint_attr;
mod toggle_macro_delimiter; mod toggle_macro_delimiter;
mod unmerge_imports; mod unmerge_imports;
mod unmerge_match_arm; mod unmerge_match_arm;
@ -365,6 +366,7 @@ mod handlers {
toggle_async_sugar::desugar_async_into_impl_future, toggle_async_sugar::desugar_async_into_impl_future,
toggle_async_sugar::sugar_impl_future_into_async, toggle_async_sugar::sugar_impl_future_into_async,
toggle_ignore::toggle_ignore, toggle_ignore::toggle_ignore,
toggle_lint_attr::toggle_lint_attr,
toggle_macro_delimiter::toggle_macro_delimiter, toggle_macro_delimiter::toggle_macro_delimiter,
unmerge_match_arm::unmerge_match_arm, unmerge_match_arm::unmerge_match_arm,
unmerge_imports::unmerge_imports, unmerge_imports::unmerge_imports,

View file

@ -3389,6 +3389,36 @@ fn arithmetics {
) )
} }
#[test]
fn doctest_toggle_lint_attr() {
check_doc_test(
"toggle_lint_attr",
r#####"
#[$0allow(dead_code)]
fn foo() {}
"#####,
r#####"
#[expect(dead_code)]
fn foo() {}
"#####,
)
}
#[test]
fn doctest_toggle_lint_attr_1() {
check_doc_test(
"toggle_lint_attr",
r#####"
#[$0forbid(dead_code)]
fn foo() {}
"#####,
r#####"
#[deny(dead_code)]
fn foo() {}
"#####,
)
}
#[test] #[test]
fn doctest_toggle_macro_delimiter() { fn doctest_toggle_macro_delimiter() {
check_doc_test( check_doc_test(