about summary refs log tree commit diff
diff options
context:
space:
mode:
authorYoung-Flash <871946895@qq.com>2024-01-01 11:17:06 +0800
committerYoung-Flash <871946895@qq.com>2024-01-01 11:17:06 +0800
commit613774e33123c4b3acc397c697def7a55e405653 (patch)
treeae404b3c5c5b54765590cdb355473c176efe0de8
parentcf52c4b2b3367ae7355ef23393e2eae1d37de723 (diff)
downloadrust-613774e33123c4b3acc397c697def7a55e405653.tar.gz
rust-613774e33123c4b3acc397c697def7a55e405653.zip
feat: add quickfix for redundant_assoc_item diagnostic
-rw-r--r--crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs115
1 files changed, 90 insertions, 25 deletions
diff --git a/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs b/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs
index c202264bb56..9992a0f82c9 100644
--- a/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs
+++ b/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs
@@ -1,5 +1,12 @@
-use hir::{Const, Function, HasSource, TypeAlias};
-use ide_db::base_db::FileRange;
+use hir::{db::ExpandDatabase, Const, Function, HasSource, HirDisplay, TypeAlias};
+use ide_db::{
+    assists::{Assist, AssistId, AssistKind},
+    base_db::FileRange,
+    label::Label,
+    source_change::SourceChangeBuilder,
+};
+use syntax::{AstNode, SyntaxKind};
+use text_edit::TextRange;
 
 use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
 
@@ -10,35 +17,48 @@ pub(crate) fn trait_impl_redundant_assoc_item(
     ctx: &DiagnosticsContext<'_>,
     d: &hir::TraitImplRedundantAssocItems,
 ) -> Diagnostic {
+    let db = ctx.sema.db;
     let name = d.assoc_item.0.clone();
+    let redundant_assoc_item_name = name.display(db);
     let assoc_item = d.assoc_item.1;
-    let db = ctx.sema.db;
 
     let default_range = d.impl_.syntax_node_ptr().text_range();
     let trait_name = d.trait_.name(db).to_smol_str();
 
-    let (redundant_item_name, diagnostic_range) = match assoc_item {
-        hir::AssocItem::Function(id) => (
-            format!("`fn {}`", name.display(db)),
-            Function::from(id)
-                .source(db)
-                .map(|it| it.syntax().value.text_range())
-                .unwrap_or(default_range),
-        ),
-        hir::AssocItem::Const(id) => (
-            format!("`const {}`", name.display(db)),
-            Const::from(id)
-                .source(db)
-                .map(|it| it.syntax().value.text_range())
-                .unwrap_or(default_range),
-        ),
-        hir::AssocItem::TypeAlias(id) => (
-            format!("`type {}`", name.display(db)),
-            TypeAlias::from(id)
-                .source(db)
-                .map(|it| it.syntax().value.text_range())
-                .unwrap_or(default_range),
-        ),
+    let (redundant_item_name, diagnostic_range, redundant_item_def) = match assoc_item {
+        hir::AssocItem::Function(id) => {
+            let function = Function::from(id);
+            (
+                format!("`fn {}`", redundant_assoc_item_name),
+                function
+                    .source(db)
+                    .map(|it| it.syntax().value.text_range())
+                    .unwrap_or(default_range),
+                format!("\n    {};", function.display(db).to_string()),
+            )
+        }
+        hir::AssocItem::Const(id) => {
+            let constant = Const::from(id);
+            (
+                format!("`const {}`", redundant_assoc_item_name),
+                constant
+                    .source(db)
+                    .map(|it| it.syntax().value.text_range())
+                    .unwrap_or(default_range),
+                format!("\n    {};", constant.display(db).to_string()),
+            )
+        }
+        hir::AssocItem::TypeAlias(id) => {
+            let type_alias = TypeAlias::from(id);
+            (
+                format!("`type {}`", redundant_assoc_item_name),
+                type_alias
+                    .source(db)
+                    .map(|it| it.syntax().value.text_range())
+                    .unwrap_or(default_range),
+                format!("\n    type {};", type_alias.name(ctx.sema.db).to_smol_str()),
+            )
+        }
     };
 
     Diagnostic::new(
@@ -46,6 +66,51 @@ pub(crate) fn trait_impl_redundant_assoc_item(
         format!("{redundant_item_name} is not a member of trait `{trait_name}`"),
         FileRange { file_id: d.file_id.file_id().unwrap(), range: diagnostic_range },
     )
+    .with_fixes(quickfix_for_redundant_assoc_item(
+        ctx,
+        d,
+        redundant_item_def,
+        diagnostic_range,
+    ))
+}
+
+/// add assoc item into the trait def body
+fn quickfix_for_redundant_assoc_item(
+    ctx: &DiagnosticsContext<'_>,
+    d: &hir::TraitImplRedundantAssocItems,
+    redundant_item_def: String,
+    range: TextRange,
+) -> Option<Vec<Assist>> {
+    let add_assoc_item_def = |builder: &mut SourceChangeBuilder| -> Option<()> {
+        let db = ctx.sema.db;
+        let root = db.parse_or_expand(d.file_id);
+        // don't modify trait def in outer crate
+        let current_crate = ctx.sema.scope(&d.impl_.syntax_node_ptr().to_node(&root))?.krate();
+        let trait_def_crate = d.trait_.module(db).krate();
+        if trait_def_crate != current_crate {
+            return None;
+        }
+        let trait_def = d.trait_.source(db)?.value;
+        let where_to_insert = trait_def
+            .syntax()
+            .descendants_with_tokens()
+            .find(|it| it.kind() == SyntaxKind::L_CURLY)
+            .map(|it| it.text_range())?;
+
+        Some(builder.insert(where_to_insert.end(), redundant_item_def))
+    };
+    let file_id = d.file_id.file_id()?;
+    let mut source_change_builder = SourceChangeBuilder::new(file_id);
+    add_assoc_item_def(&mut source_change_builder)?;
+
+    Some(vec![Assist {
+        id: AssistId("add assoc item def into trait def", AssistKind::QuickFix),
+        label: Label::new("Add assoc item def into trait def".to_string()),
+        group: None,
+        target: range,
+        source_change: Some(source_change_builder.finish()),
+        trigger_signature_help: false,
+    }])
 }
 
 #[cfg(test)]