From 8da850b6d5cdb9896ff936170ccc6a6891ca067d Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 3 Dec 2021 20:28:15 +0100 Subject: [PATCH] Improve hover message for inert attributes --- crates/hir/src/lib.rs | 5 ++++ crates/hir_def/src/builtin_attr.rs | 12 +++++---- crates/ide/src/hover/render.rs | 22 +++++++++++++-- crates/ide/src/hover/tests.rs | 43 ++++++++++++++++++++++++++++++ 4 files changed, 75 insertions(+), 7 deletions(-) diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index c7f94ff9aa63..4d758c7df761 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -103,6 +103,7 @@ pub use { hir_def::{ adt::StructKind, attr::{Attr, Attrs, AttrsWithOwner, Documentation}, + builtin_attr::AttributeTemplate, find_path::PrefixKind, import_map, item_scope::ItemScope, @@ -2036,6 +2037,10 @@ impl BuiltinAttr { // FIXME: Return a `Name` here hir_def::builtin_attr::INERT_ATTRIBUTES[self.0].name } + + pub fn template(&self, _: &dyn HirDatabase) -> AttributeTemplate { + hir_def::builtin_attr::INERT_ATTRIBUTES[self.0].template + } } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] diff --git a/crates/hir_def/src/builtin_attr.rs b/crates/hir_def/src/builtin_attr.rs index cd3a8a8605d6..3f43111fb1d5 100644 --- a/crates/hir_def/src/builtin_attr.rs +++ b/crates/hir_def/src/builtin_attr.rs @@ -21,15 +21,15 @@ pub struct BuiltinAttribute { /// A template that the attribute input must match. /// Only top-level shape (`#[attr]` vs `#[attr(...)]` vs `#[attr = ...]`) is considered now. +#[derive(Clone, Copy)] pub struct AttributeTemplate { pub word: bool, pub list: Option<&'static str>, pub name_value_str: Option<&'static str>, } -static BUILTIN_LOOKUP_TABLE: OnceCell> = OnceCell::new(); - pub fn find_builtin_attr_idx(name: &str) -> Option { + static BUILTIN_LOOKUP_TABLE: OnceCell> = OnceCell::new(); BUILTIN_LOOKUP_TABLE .get_or_init(|| { INERT_ATTRIBUTES.iter().map(|attr| attr.name).enumerate().map(|(a, b)| (b, a)).collect() @@ -58,9 +58,11 @@ macro_rules! template { (Word, List: $descr1: expr, NameValueStr: $descr2: expr) => { template!(@ true, Some($descr1), Some($descr2)) }; - (@ $word: expr, $list: expr, $name_value_str: expr) => { AttributeTemplate { - word: $word, list: $list, name_value_str: $name_value_str - } }; + (@ $word: expr, $list: expr, $name_value_str: expr) => { + AttributeTemplate { + word: $word, list: $list, name_value_str: $name_value_str + } + }; } macro_rules! ungated { diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs index 393bb253b61a..c493e3e2fb25 100644 --- a/crates/ide/src/hover/render.rs +++ b/crates/ide/src/hover/render.rs @@ -2,7 +2,7 @@ use std::fmt::Display; use either::Either; -use hir::{AsAssocItem, HasAttrs, HasSource, HirDisplay, Semantics, TypeInfo}; +use hir::{AsAssocItem, AttributeTemplate, HasAttrs, HasSource, HirDisplay, Semantics, TypeInfo}; use ide_db::{ base_db::SourceDatabase, defs::Definition, @@ -370,13 +370,31 @@ pub(super) fn definition( Definition::GenericParam(it) => label_and_docs(db, it), Definition::Label(it) => return Some(Markup::fenced_block(&it.name(db))), // FIXME: We should be able to show more info about these - Definition::BuiltinAttr(it) => return Some(Markup::fenced_block(&it.name(db))), + Definition::BuiltinAttr(it) => return render_builtin_attr(db, it), Definition::ToolModule(it) => return Some(Markup::fenced_block(&it.name(db))), }; markup(docs.filter(|_| config.documentation.is_some()).map(Into::into), label, mod_path) } +fn render_builtin_attr(db: &RootDatabase, attr: hir::BuiltinAttr) -> Option { + let name = attr.name(db); + let desc = format!("#[{}]", name); + + let AttributeTemplate { word, list, name_value_str } = attr.template(db); + let mut docs = "Valid forms are:".to_owned(); + if word { + format_to!(docs, "\n - #\\[{}]", name); + } + if let Some(list) = list { + format_to!(docs, "\n - #\\[{}({})]", name, list); + } + if let Some(name_value_str) = name_value_str { + format_to!(docs, "\n - #\\[{} = {}]", name, name_value_str); + } + markup(Some(docs.replace('*', "\\*")), desc, None) +} + fn label_and_docs(db: &RootDatabase, def: D) -> (String, Option) where D: HasAttrs + HirDisplay, diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index 4187e9ca361b..f1d7d2791d8e 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -4277,3 +4277,46 @@ pub struct Foo; "#]], ); } + +#[test] +fn hover_inert_attr() { + check( + r#" +#[doc$0 = ""] +pub struct Foo; +"#, + expect![[r##" + *doc* + + ```rust + #[doc] + ``` + + --- + + Valid forms are: + + * \#\[doc(hidden|inline|...)\] + * \#\[doc = string\] + "##]], + ); + check( + r#" +#[allow$0()] +pub struct Foo; +"#, + expect![[r##" + *allow* + + ```rust + #[allow] + ``` + + --- + + Valid forms are: + + * \#\[allow(lint1, lint2, ..., /\*opt\*/ reason = "...")\] + "##]], + ); +}