diff --git a/crates/ide-assists/src/handlers/toggle_lint_attr.rs b/crates/ide-assists/src/handlers/toggle_lint_attr.rs new file mode 100644 index 0000000000..bc0e73db43 --- /dev/null +++ b/crates/ide-assists/src/handlers/toggle_lint_attr.rs @@ -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::()?; + 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() {} + ", + ); + } +} diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs index 4682c04732..f6c3a44ddb 100644 --- a/crates/ide-assists/src/lib.rs +++ b/crates/ide-assists/src/lib.rs @@ -222,6 +222,7 @@ mod handlers { mod term_search; mod toggle_async_sugar; mod toggle_ignore; + mod toggle_lint_attr; mod toggle_macro_delimiter; mod unmerge_imports; mod unmerge_match_arm; @@ -365,6 +366,7 @@ mod handlers { toggle_async_sugar::desugar_async_into_impl_future, toggle_async_sugar::sugar_impl_future_into_async, toggle_ignore::toggle_ignore, + toggle_lint_attr::toggle_lint_attr, toggle_macro_delimiter::toggle_macro_delimiter, unmerge_match_arm::unmerge_match_arm, unmerge_imports::unmerge_imports, diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs index 91348be97e..451f5d7b47 100644 --- a/crates/ide-assists/src/tests/generated.rs +++ b/crates/ide-assists/src/tests/generated.rs @@ -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] fn doctest_toggle_macro_delimiter() { check_doc_test(