about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLukas Wirth <lukastw97@gmail.com>2021-12-03 20:28:15 +0100
committerLukas Wirth <lukastw97@gmail.com>2021-12-03 20:28:15 +0100
commit8da850b6d5cdb9896ff936170ccc6a6891ca067d (patch)
treea6b2153162b073ff599ff92230aba2caae22a410
parentd174158abce0ab7e9f2d3bc7556ab7070d38aaa4 (diff)
downloadrust-8da850b6d5cdb9896ff936170ccc6a6891ca067d.tar.gz
rust-8da850b6d5cdb9896ff936170ccc6a6891ca067d.zip
Improve hover message for inert attributes
-rw-r--r--crates/hir/src/lib.rs5
-rw-r--r--crates/hir_def/src/builtin_attr.rs12
-rw-r--r--crates/ide/src/hover/render.rs22
-rw-r--r--crates/ide/src/hover/tests.rs43
4 files changed, 75 insertions, 7 deletions
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index c7f94ff9aa6..4d758c7df76 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 cd3a8a8605d..3f43111fb1d 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<FxHashMap<&'static str, usize>> = OnceCell::new();
-
 pub fn find_builtin_attr_idx(name: &str) -> Option<usize> {
+    static BUILTIN_LOOKUP_TABLE: OnceCell<FxHashMap<&'static str, usize>> = 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 393bb253b61..c493e3e2fb2 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<Markup> {
+    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<D>(db: &RootDatabase, def: D) -> (String, Option<hir::Documentation>)
 where
     D: HasAttrs + HirDisplay,
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index 4187e9ca361..f1d7d2791d8 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 = "...")\]
+        "##]],
+    );
+}