diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index 74c1b97a2e..52c1c27a7f 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs @@ -12,7 +12,7 @@ use hir_def::path::ModPath; use hir_expand::{name::Name, HirFileId, InFile}; use syntax::{ast, AstPtr, SyntaxError, SyntaxNodePtr, TextRange}; -use crate::{AssocItem, Field, Local, MacroKind, Type}; +use crate::{AssocItem, Field, Local, MacroKind, Trait, Type}; macro_rules! diagnostics { ($($diag:ident,)*) => { @@ -55,7 +55,7 @@ diagnostics![ ReplaceFilterMapNextWithFindMap, TraitImplIncorrectSafety, TraitImplMissingAssocItems, - TraitImplReduntantAssocItems, + TraitImplRedundantAssocItems, TraitImplOrphan, TypedHole, TypeMismatch, @@ -313,8 +313,8 @@ pub struct TraitImplMissingAssocItems { } #[derive(Debug, PartialEq, Eq)] -pub struct TraitImplReduntantAssocItems { +pub struct TraitImplRedundantAssocItems { pub file_id: HirFileId, - pub impl_: AstPtr, - pub reduntant: Vec<(Name, AssocItem)>, -} \ No newline at end of file + pub trait_: Trait, + pub assoc_item: (Name, AssocItem), +} diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 5e6f3c7a99..4db1a02c0c 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -693,20 +693,20 @@ impl Module { }, )); - let reduntant: Vec<_> = impl_assoc_items_scratch.iter() - .filter(|(id, name)| { - !required_items.clone().any(|(impl_name, impl_item)| { - discriminant(impl_item) == discriminant(id) && impl_name == name + let redundant = impl_assoc_items_scratch + .iter() + .filter(|(id, name)| { + !items.iter().any(|(impl_name, impl_item)| { + discriminant(impl_item) == discriminant(id) && impl_name == name + }) }) - }) - .map(|(item, name)| (name.clone(), AssocItem::from(*item))) - .collect(); - if !reduntant.is_empty() { + .map(|(item, name)| (name.clone(), AssocItem::from(*item))); + for (name, assoc_item) in redundant { acc.push( - TraitImplReduntantAssocItems { - impl_: ast_id_map.get(node.ast_id()), + TraitImplRedundantAssocItems { + trait_, file_id, - reduntant, + assoc_item: (name, assoc_item), } .into(), ) 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 new file mode 100644 index 0000000000..6aded11382 --- /dev/null +++ b/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs @@ -0,0 +1,72 @@ +use hir::{db::ExpandDatabase, Const, Function, HasSource, TypeAlias}; + +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: trait-impl-redundant-assoc_item +// +// Diagnoses redundant trait items in a trait impl. +pub(crate) fn trait_impl_redundant_assoc_item( + ctx: &DiagnosticsContext<'_>, + d: &hir::TraitImplRedundantAssocItems, +) -> Diagnostic { + let name = d.assoc_item.0.clone(); + let assoc_item = d.assoc_item.1; + let db = ctx.sema.db; + + let range = db.parse_or_expand(d.file_id).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(range), + ), + hir::AssocItem::Const(id) => ( + format!("`const {}`", name.display(db)), + Const::from(id).source(db).map(|it| it.syntax().value.text_range()).unwrap_or(range), + ), + hir::AssocItem::TypeAlias(id) => ( + format!("`type {}`", name.display(db)), + TypeAlias::from(id) + .source(db) + .map(|it| it.syntax().value.text_range()) + .unwrap_or(range), + ), + }; + + Diagnostic::new( + DiagnosticCode::RustcHardError("E0407"), + format!("{redundant_item_name} is not a member of trait `{trait_name}`"), + diagnostic_range, + ) +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn trait_with_default_value() { + check_diagnostics( + r#" +trait Marker { + const FLAG: bool = false; + fn boo(); + fn foo () {} +} +struct Foo; +impl Marker for Foo { + type T = i32; + //^^^^^^^^^^^^^ error: `type T` is not a member of trait `Marker` + + const FLAG: bool = true; + + fn bar() {} + //^^^^^^^^^^^ error: `fn bar` is not a member of trait `Marker` + + fn boo() {} +} + "#, + ) + } +} diff --git a/crates/ide-diagnostics/src/handlers/trait_impl_reduntant_assoc_item.rs b/crates/ide-diagnostics/src/handlers/trait_impl_reduntant_assoc_item.rs deleted file mode 100644 index 446ce7d9fe..0000000000 --- a/crates/ide-diagnostics/src/handlers/trait_impl_reduntant_assoc_item.rs +++ /dev/null @@ -1,56 +0,0 @@ -use hir::InFile; -use itertools::Itertools; -use syntax::{ast, AstNode}; - -use crate::{adjusted_display_range, Diagnostic, DiagnosticCode, DiagnosticsContext}; - -// Diagnostic: trait-impl-reduntant-assoc_item -// -// Diagnoses reduntant trait items in a trait impl. -pub(crate) fn trait_impl_reduntant_assoc_item( - ctx: &DiagnosticsContext<'_>, - d: &hir::TraitImplReduntantAssocItems, -) -> Diagnostic { - let reduntant = d.reduntant.iter().format_with(", ", |(name, item), f| { - f(&match *item { - hir::AssocItem::Function(_) => "`fn ", - hir::AssocItem::Const(_) => "`const ", - hir::AssocItem::TypeAlias(_) => "`type ", - })?; - f(&name.display(ctx.sema.db))?; - f(&"`") - }); - Diagnostic::new( - DiagnosticCode::RustcHardError("E0407"), - format!("{reduntant} is not a member of trait"), - adjusted_display_range::( - ctx, - InFile { file_id: d.file_id, value: d.impl_.syntax_node_ptr() }, - &|impl_| impl_.trait_().map(|t| t.syntax().text_range()), - ), - ) -} - -#[cfg(test)] -mod tests { - use crate::tests::check_diagnostics; - - #[test] - fn trait_with_default_value() { - check_diagnostics( - r#" -trait Marker { - fn boo(); -} -struct Foo; -impl Marker for Foo { - //^^^^^^ error: `type T`, `const FLAG`, `fn bar` is not a member of trait - type T = i32; - const FLAG: bool = false; - fn bar() {} - fn boo() {} -} - "#, - ) - } -} diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs index c39e572b42..6cfd5f1832 100644 --- a/crates/ide-diagnostics/src/lib.rs +++ b/crates/ide-diagnostics/src/lib.rs @@ -47,7 +47,7 @@ mod handlers { pub(crate) mod trait_impl_orphan; pub(crate) mod trait_impl_incorrect_safety; pub(crate) mod trait_impl_missing_assoc_item; - pub(crate) mod trait_impl_reduntant_assoc_item; + pub(crate) mod trait_impl_redundant_assoc_item; pub(crate) mod typed_hole; pub(crate) mod type_mismatch; pub(crate) mod unimplemented_builtin_macro; @@ -365,7 +365,7 @@ pub fn diagnostics( AnyDiagnostic::ReplaceFilterMapNextWithFindMap(d) => handlers::replace_filter_map_next_with_find_map::replace_filter_map_next_with_find_map(&ctx, &d), AnyDiagnostic::TraitImplIncorrectSafety(d) => handlers::trait_impl_incorrect_safety::trait_impl_incorrect_safety(&ctx, &d), AnyDiagnostic::TraitImplMissingAssocItems(d) => handlers::trait_impl_missing_assoc_item::trait_impl_missing_assoc_item(&ctx, &d), - AnyDiagnostic::TraitImplReduntantAssocItems(d) => handlers::trait_impl_reduntant_assoc_item::trait_impl_reduntant_assoc_item(&ctx, &d), + AnyDiagnostic::TraitImplRedundantAssocItems(d) => handlers::trait_impl_redundant_assoc_item::trait_impl_redundant_assoc_item(&ctx, &d), AnyDiagnostic::TraitImplOrphan(d) => handlers::trait_impl_orphan::trait_impl_orphan(&ctx, &d), AnyDiagnostic::TypedHole(d) => handlers::typed_hole::typed_hole(&ctx, &d), AnyDiagnostic::TypeMismatch(d) => handlers::type_mismatch::type_mismatch(&ctx, &d),