Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide/src/hover/render.rs')
-rw-r--r--crates/ide/src/hover/render.rs90
1 files changed, 82 insertions, 8 deletions
diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs
index 3e41b42be4..83adf6548a 100644
--- a/crates/ide/src/hover/render.rs
+++ b/crates/ide/src/hover/render.rs
@@ -3,8 +3,9 @@ use std::{mem, ops::Not};
use either::Either;
use hir::{
- Adt, AsAssocItem, AsExternAssocItem, CaptureKind, HasCrate, HasSource, HirDisplay, Layout,
- LayoutError, Name, Semantics, Trait, Type, TypeInfo,
+ db::ExpandDatabase, Adt, AsAssocItem, AsExternAssocItem, CaptureKind, HasCrate, HasSource,
+ HirDisplay, Layout, LayoutError, MethodViolationCode, Name, ObjectSafetyViolation, Semantics,
+ Trait, Type, TypeInfo,
};
use ide_db::{
base_db::SourceDatabase,
@@ -12,7 +13,7 @@ use ide_db::{
documentation::HasDocs,
famous_defs::FamousDefs,
generated::lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES},
- syntax_helpers::insert_whitespace_into_node,
+ syntax_helpers::prettify_macro_expansion,
RootDatabase,
};
use itertools::Itertools;
@@ -328,7 +329,7 @@ pub(super) fn try_for_lint(attr: &ast::Attr, token: &SyntaxToken) -> Option<Hove
}
let (is_clippy, lints) = match &*path {
"feature" => (false, FEATURES),
- "allow" | "deny" | "forbid" | "warn" => {
+ "allow" | "deny" | "expect" | "forbid" | "warn" => {
let is_clippy = algo::non_trivia_sibling(token.clone().into(), Direction::Prev)
.filter(|t| t.kind() == T![:])
.and_then(|t| algo::non_trivia_sibling(t, Direction::Prev))
@@ -475,8 +476,9 @@ pub(super) fn definition(
Err(_) => {
let source = it.source(db)?;
let mut body = source.value.body()?.syntax().clone();
- if source.file_id.is_macro() {
- body = insert_whitespace_into_node::insert_ws_into(body);
+ if let Some(macro_file) = source.file_id.macro_file() {
+ let span_map = db.expansion_span_map(macro_file);
+ body = prettify_macro_expansion(db, body, &span_map, it.krate(db).into());
}
Some(body.to_string())
}
@@ -485,8 +487,9 @@ pub(super) fn definition(
Definition::Static(it) => {
let source = it.source(db)?;
let mut body = source.value.body()?.syntax().clone();
- if source.file_id.is_macro() {
- body = insert_whitespace_into_node::insert_ws_into(body);
+ if let Some(macro_file) = source.file_id.macro_file() {
+ let span_map = db.expansion_span_map(macro_file);
+ body = prettify_macro_expansion(db, body, &span_map, it.krate(db).into());
}
Some(body.to_string())
}
@@ -526,6 +529,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 +546,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 +979,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());
+ }
+ }
+}