about summary refs log tree commit diff
path: root/src/tools/rust-analyzer/crates/ide
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-08-29 13:24:54 +0000
committerbors <bors@rust-lang.org>2024-08-29 13:24:54 +0000
commit28142e4018279d75ff51d31e6b65eb2405f8f338 (patch)
tree07a2507568d52b35005f46e24a2dad34a8b5556a /src/tools/rust-analyzer/crates/ide
parent23bea2b2736ac69091df5069282d12887a4d9e4f (diff)
parenta54a7a8a6afb6d67a5a66806068e60f55a0947e6 (diff)
downloadrust-28142e4018279d75ff51d31e6b65eb2405f8f338.tar.gz
rust-28142e4018279d75ff51d31e6b65eb2405f8f338.zip
Auto merge of #17814 - ShoyuVanilla:object-safety, r=Veykril
feat: Implement object safety and its hovering hint

Resolves #17779

- [x] Fill missing implementations
- [x] Hover rendering
- [x] Implement object safety's own test suite, like layout
- [x] Add test cases (from rustc maybe)
- [x] Clean up ugly codes
- [x] Add doc string
Diffstat (limited to 'src/tools/rust-analyzer/crates/ide')
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover/render.rs74
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover/tests.rs24
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs4
3 files changed, 94 insertions, 8 deletions
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
index 3e41b42be44..ead2d45847d 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
@@ -4,7 +4,8 @@ use std::{mem, ops::Not};
 use either::Either;
 use hir::{
     Adt, AsAssocItem, AsExternAssocItem, CaptureKind, HasCrate, HasSource, HirDisplay, Layout,
-    LayoutError, Name, Semantics, Trait, Type, TypeInfo,
+    LayoutError, MethodViolationCode, Name, ObjectSafetyViolation, Semantics, Trait, Type,
+    TypeInfo,
 };
 use ide_db::{
     base_db::SourceDatabase,
@@ -526,6 +527,14 @@ pub(super) fn definition(
         _ => None,
     };
 
+    let object_safety_info = if let Definition::Trait(it) = def {
+        let mut object_safety_info = String::new();
+        render_object_safety(db, &mut object_safety_info, it.object_safety(db));
+        Some(object_safety_info)
+    } else {
+        None
+    };
+
     let mut desc = String::new();
     if let Some(notable_traits) = render_notable_trait_comment(db, notable_traits, edition) {
         desc.push_str(&notable_traits);
@@ -535,6 +544,10 @@ pub(super) fn definition(
         desc.push_str(&layout_info);
         desc.push('\n');
     }
+    if let Some(object_safety_info) = object_safety_info {
+        desc.push_str(&object_safety_info);
+        desc.push('\n');
+    }
     desc.push_str(&label);
     if let Some(value) = value {
         desc.push_str(" = ");
@@ -964,3 +977,62 @@ fn keyword_hints(
         _ => KeywordHint::new(token.text().to_owned(), format!("{}_keyword", token.text())),
     }
 }
+
+fn render_object_safety(
+    db: &RootDatabase,
+    buf: &mut String,
+    safety: Option<ObjectSafetyViolation>,
+) {
+    let Some(osv) = safety else {
+        buf.push_str("// Object Safety: Yes");
+        return;
+    };
+    buf.push_str("// Object Safety: No\n// - Reason: ");
+    match osv {
+        ObjectSafetyViolation::SizedSelf => {
+            buf.push_str("has a `Self: Sized` bound");
+        }
+        ObjectSafetyViolation::SelfReferential => {
+            buf.push_str("has a bound that references `Self`");
+        }
+        ObjectSafetyViolation::Method(func, mvc) => {
+            let name = hir::Function::from(func).name(db);
+            format_to!(
+                buf,
+                "has a method `{}` that is non dispatchable because of:\n//   - ",
+                name.as_str()
+            );
+            let desc = match mvc {
+                MethodViolationCode::StaticMethod => "missing a receiver",
+                MethodViolationCode::ReferencesSelfInput => "a parameter references `Self`",
+                MethodViolationCode::ReferencesSelfOutput => "the return type references `Self`",
+                MethodViolationCode::ReferencesImplTraitInTrait => {
+                    "the return type contains `impl Trait`"
+                }
+                MethodViolationCode::AsyncFn => "being async",
+                MethodViolationCode::WhereClauseReferencesSelf => {
+                    "a where clause references `Self`"
+                }
+                MethodViolationCode::Generic => "a non-lifetime generic parameter",
+                MethodViolationCode::UndispatchableReceiver => "a non-dispatchable receiver type",
+            };
+            buf.push_str(desc);
+        }
+        ObjectSafetyViolation::AssocConst(const_) => {
+            let name = hir::Const::from(const_).name(db);
+            if let Some(name) = name {
+                format_to!(buf, "has an associated constant `{}`", name.as_str());
+            } else {
+                buf.push_str("has an associated constant");
+            }
+        }
+        ObjectSafetyViolation::GAT(alias) => {
+            let name = hir::TypeAlias::from(alias).name(db);
+            format_to!(buf, "has a generic associated type `{}`", name.as_str());
+        }
+        ObjectSafetyViolation::HasNonSafeSuperTrait(super_trait) => {
+            let name = hir::Trait::from(super_trait).name(db);
+            format_to!(buf, "has a object unsafe supertrait `{}`", name.as_str());
+        }
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
index 9585bdbe4c5..f2f83d538cc 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
@@ -5442,7 +5442,7 @@ const FOO$0: Option<&i32> = Some(2).as_ref();
 fn hover_const_eval_dyn_trait() {
     check(
         r#"
-//- minicore: fmt, coerce_unsized, builtin_impls
+//- minicore: fmt, coerce_unsized, builtin_impls, dispatch_from_dyn
 use core::fmt::Debug;
 
 const FOO$0: &dyn Debug = &2i32;
@@ -7107,6 +7107,7 @@ impl T$0 for () {}
             ```
 
             ```rust
+            // Object Safety: Yes
             trait T {}
             ```
         "#]],
@@ -7126,6 +7127,7 @@ impl T$0 for () {}
             ```
 
             ```rust
+            // Object Safety: Yes
             trait T {}
             ```
         "#]],
@@ -7149,6 +7151,9 @@ impl T$0 for () {}
             ```
 
             ```rust
+            // Object Safety: No
+            // - Reason: has a method `func` that is non dispatchable because of:
+            //   - missing a receiver
             trait T { /* … */ }
             ```
         "#]],
@@ -7172,6 +7177,9 @@ impl T$0 for () {}
             ```
 
             ```rust
+            // Object Safety: No
+            // - Reason: has a method `func` that is non dispatchable because of:
+            //   - missing a receiver
             trait T {
                 fn func();
                 const FLAG: i32;
@@ -7199,6 +7207,9 @@ impl T$0 for () {}
             ```
 
             ```rust
+            // Object Safety: No
+            // - Reason: has a method `func` that is non dispatchable because of:
+            //   - missing a receiver
             trait T {
                 fn func();
                 const FLAG: i32;
@@ -7226,6 +7237,9 @@ impl T$0 for () {}
             ```
 
             ```rust
+            // Object Safety: No
+            // - Reason: has a method `func` that is non dispatchable because of:
+            //   - missing a receiver
             trait T {
                 fn func();
                 const FLAG: i32;
@@ -8465,8 +8479,8 @@ impl Iterator for S {
                                 file_id: FileId(
                                     1,
                                 ),
-                                full_range: 7800..8042,
-                                focus_range: 7865..7871,
+                                full_range: 7801..8043,
+                                focus_range: 7866..7872,
                                 name: "Future",
                                 kind: Trait,
                                 container_name: "future",
@@ -8479,8 +8493,8 @@ impl Iterator for S {
                                 file_id: FileId(
                                     1,
                                 ),
-                                full_range: 8672..9171,
-                                focus_range: 8749..8757,
+                                full_range: 8673..9172,
+                                focus_range: 8750..8758,
                                 name: "Iterator",
                                 kind: Trait,
                                 container_name: "iterator",
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs
index 756198d0c01..31c1a991d53 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs
@@ -288,7 +288,7 @@ mod tests {
         check_with_config(
             InlayHintsConfig { adjustment_hints: AdjustmentHints::Always, ..DISABLED_CONFIG },
             r#"
-//- minicore: coerce_unsized, fn, eq, index
+//- minicore: coerce_unsized, fn, eq, index, dispatch_from_dyn
 fn main() {
     let _: u32         = loop {};
                        //^^^^^^^<never-to-any>
@@ -428,7 +428,7 @@ impl core::ops::IndexMut for Struct {}
                 ..DISABLED_CONFIG
             },
             r#"
-//- minicore: coerce_unsized, fn, eq, index
+//- minicore: coerce_unsized, fn, eq, index, dispatch_from_dyn
 fn main() {
 
     Struct.consume();