Repository: rust-lang/rust-analyzer (rust)

Issue:
`std::error:Error` is considered as object unsafe
**rust-analyzer version**: `0ae42bd42576566540a84c62e118aa823edcf2ec`

![image](https://github.com/user-attachments/assets/bd5e0b86-f4f6-4141-9386-57fb35ddbfd1)

While investigating regressions on `diesel` I found this😅 

Pull request:
fix: `std::error::Error` is object unsafe
Fixes #17998

I tried to get generic predicates of assoc function itself, not inherited from the parent here;

https://github.com/rust-lang/rust-analyzer/blob/0ae42bd42576566540a84c62e118aa823edcf2ec/crates/hir-ty/src/object_safety.rs#L420-L442

But this naive equality check approach doesn't work when the assoc function has one or more generic paramters like;

```rust
trait Foo {}
trait Bar: Foo {
    fn bar(&self);
}
```

because the generic predicates of the parent, `Bar` is `[^1.0 implements Foo]` and the generic predicates of `fn bar` is `[^1.1 implements Foo]`, which are different.

This PR implements a correct logic for filtering out parent generic predicates for this.

Patch:
diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs
index ebbe8a448c..ce5a821ea2 100644
--- a/crates/hir-ty/src/db.rs
+++ b/crates/hir-ty/src/db.rs
@@ -154,6 +154,9 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
     #[salsa::invoke(crate::lower::generic_predicates_query)]
     fn generic_predicates(&self, def: GenericDefId) -> GenericPredicates;
 
+    #[salsa::invoke(crate::lower::generic_predicates_without_parent_query)]
+    fn generic_predicates_without_parent(&self, def: GenericDefId) -> GenericPredicates;
+
     #[salsa::invoke(crate::lower::trait_environment_for_body_query)]
     #[salsa::transparent]
     fn trait_environment_for_body(&self, def: DefWithBodyId) -> Arc<TraitEnvironment>;
diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs
index 841e0216ae..213400d04a 100644
--- a/crates/hir-ty/src/lower.rs
+++ b/crates/hir-ty/src/lower.rs
@@ -1700,6 +1700,28 @@ pub(crate) fn generic_predicates_query(
     db: &dyn HirDatabase,
     def: GenericDefId,
 ) -> GenericPredicates {
+    generic_predicates_filtered_by(db, def, |_, _| true)
+}
+
+/// Resolve the where clause(s) of an item with generics,
+/// except the ones inherited from the parent
+pub(crate) fn generic_predicates_without_parent_query(
+    db: &dyn HirDatabase,
+    def: GenericDefId,
+) -> GenericPredicates {
+    generic_predicates_filtered_by(db, def, |_, d| *d == def)
+}
+
+/// Resolve the where clause(s) of an item with generics,
+/// except the ones inherited from the parent
+fn generic_predicates_filtered_by<F>(
+    db: &dyn HirDatabase,
+    def: GenericDefId,
+    filter: F,
+) -> GenericPredicates
+where
+    F: Fn(&WherePredicate, &GenericDefId) -> bool,
+{
     let resolver = def.resolver(db.upcast());
     let (impl_trait_lowering, param_lowering) = match def {
         GenericDefId::FunctionId(_) => {
@@ -1714,6 +1736,7 @@ pub(crate) fn generic_predicates_query(
 
     let mut predicates = resolver
         .where_predicates_in_scope()
+        .filter(|(pred, def)| filter(pred, def))
         .flat_map(|(pred, def)| {
             ctx.lower_where_predicate(pred, def, false).map(|p| make_binders(db, &generics, p))
         })
diff --git a/crates/hir-ty/src/object_safety.rs b/crates/hir-ty/src/object_safety.rs
index f3bbb6b04f..89bf3619a0 100644
--- a/crates/hir-ty/src/object_safety.rs
+++ b/crates/hir-ty/src/object_safety.rs
@@ -12,7 +12,7 @@ use hir_def::{
     lang_item::LangItem, AssocItemId, ConstId, FunctionId, GenericDefId, HasModule, TraitId,
     TypeAliasId,
 };
-use rustc_hash::{FxHashMap, FxHashSet};
+use rustc_hash::FxHashSet;
 use smallvec::SmallVec;
 
 use crate::{
@@ -417,30 +417,11 @@ where
         cb(MethodViolationCode::UndispatchableReceiver)?;
     }
 
-    let predicates = &*db.generic_predicates(func.into());
-    let mut parent_predicates = (*db.generic_predicates(trait_.into()))
-        .iter()
-        .map(|b| b.skip_binders().skip_binders().clone())
-        .fold(FxHashMap::default(), |mut acc, item| {
-            acc.entry(item)
-                .and_modify(|cnt| {
-                    *cnt += 1;
-                })
-                .or_insert(1);
-            acc
-        });
+    let predicates = &*db.generic_predicates_without_parent(func.into());
     let trait_self_idx = trait_self_param_idx(db.upcast(), func.into());
     for pred in predicates {
         let pred = pred.skip_binders().skip_binders();
 
-        // Skip predicates from parent, i.e. the trait that contains this method
-        if let Some(cnt) = parent_predicates.get_mut(pred) {
-            if *cnt > 0 {
-                *cnt -= 1;
-                continue;
-            }
-        }
-
         if matches!(pred, WhereClause::TypeOutlives(_)) {
             continue;
         }


Test patch:
diff --git a/crates/hir-ty/src/object_safety/tests.rs b/crates/hir-ty/src/object_safety/tests.rs
index 585313e5b9..3dc08c4619 100644
--- a/crates/hir-ty/src/object_safety/tests.rs
+++ b/crates/hir-ty/src/object_safety/tests.rs
@@ -361,3 +361,20 @@ pub trait Trait {
         [("Trait", vec![])],
     );
 }
+
+#[test]
+fn std_error_is_object_safe() {
+    check_object_safety(
+        r#"
+//- minicore: fmt, dispatch_from_dyn
+trait Erased<'a>: 'a {}
+
+pub struct Request<'a>(dyn Erased<'a> + 'a);
+
+pub trait Error: core::fmt::Debug + core::fmt::Display {
+    fn provide<'a>(&'a self, request: &mut Request<'a>);
+}
+"#,
+        [("Error", vec![])],
+    );
+}