feat: Implement object safety

This commit is contained in:
Shoyu Vanilla 2024-08-07 03:16:24 +09:00
parent 06a40a61b0
commit 6520a43ca3
19 changed files with 1194 additions and 66 deletions

View file

@ -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());
}
}
}

View file

@ -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",

View file

@ -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();