From e58af219a4b55ab122f46f4abac2f59032c8d690 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 3 Dec 2021 16:32:14 +0100 Subject: [PATCH 1/6] feat: Resolve builtin-attr and tools in ide layer --- crates/hir/src/lib.rs | 20 ++++ crates/hir/src/semantics.rs | 26 ++--- crates/hir/src/source_analyzer.rs | 68 ++++++++---- crates/hir_def/src/builtin_attr.rs | 104 +++++++++++++++--- crates/hir_def/src/nameres/collector.rs | 8 +- crates/ide/src/doc_links.rs | 12 +- crates/ide/src/hover/render.rs | 2 + crates/ide/src/hover/tests.rs | 1 - crates/ide/src/navigation_target.rs | 2 + crates/ide/src/static_index.rs | 1 + .../ide/src/syntax_highlighting/highlight.rs | 2 + crates/ide/src/syntax_highlighting/inject.rs | 2 + crates/ide_db/src/defs.rs | 50 +++------ crates/ide_db/src/path_transform.rs | 4 +- crates/ide_db/src/rename.rs | 6 +- 15 files changed, 209 insertions(+), 99 deletions(-) diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index e0dc921c9f0..c0c2cc78182 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -2023,6 +2023,26 @@ impl Local { } } +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct BuiltinAttr(usize); + +impl BuiltinAttr { + pub(crate) fn by_name(name: &str) -> Option { + // TODO: def maps registered attrs? + hir_def::builtin_attr::find_builtin_attr_idx(name).map(Self) + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct Tool(usize); + +impl Tool { + pub(crate) fn by_name(name: &str) -> Option { + // TODO: def maps registered tools + hir_def::builtin_attr::TOOL_MODULES.iter().position(|&tool| tool == name).map(Self) + } +} + #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct Label { pub(crate) parent: DefWithBodyId, diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 27ba42ce1af..c48bd1b0538 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -25,9 +25,9 @@ use crate::{ db::HirDatabase, semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, source_analyzer::{resolve_hir_path, resolve_hir_path_as_macro, SourceAnalyzer}, - Access, AssocItem, Callable, ConstParam, Crate, Field, Function, HasSource, HirFileId, Impl, - InFile, Label, LifetimeParam, Local, MacroDef, Module, ModuleDef, Name, Path, ScopeDef, Trait, - Type, TypeAlias, TypeParam, VariantDef, + Access, AssocItem, BuiltinAttr, Callable, ConstParam, Crate, Field, Function, HasSource, + HirFileId, Impl, InFile, Label, LifetimeParam, Local, MacroDef, Module, ModuleDef, Name, Path, + ScopeDef, Tool, Trait, Type, TypeAlias, TypeParam, VariantDef, }; #[derive(Debug, Clone, PartialEq, Eq)] @@ -43,6 +43,8 @@ pub enum PathResolution { SelfType(Impl), Macro(MacroDef), AssocItem(AssocItem), + BuiltinAttr(BuiltinAttr), + Tool(Tool), } impl PathResolution { @@ -63,9 +65,11 @@ impl PathResolution { PathResolution::Def(ModuleDef::TypeAlias(alias)) => { Some(TypeNs::TypeAliasId((*alias).into())) } - PathResolution::Local(_) | PathResolution::Macro(_) | PathResolution::ConstParam(_) => { - None - } + PathResolution::BuiltinAttr(_) + | PathResolution::Tool(_) + | PathResolution::Local(_) + | PathResolution::Macro(_) + | PathResolution::ConstParam(_) => None, PathResolution::TypeParam(param) => Some(TypeNs::GenericParam((*param).into())), PathResolution::SelfType(impl_def) => Some(TypeNs::SelfType((*impl_def).into())), PathResolution::AssocItem(AssocItem::Const(_) | AssocItem::Function(_)) => None, @@ -334,10 +338,6 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { self.imp.resolve_path(path) } - pub fn resolve_path_as_macro(&self, path: &ast::Path) -> Option { - self.imp.resolve_path_as_macro(path) - } - pub fn resolve_extern_crate(&self, extern_crate: &ast::ExternCrate) -> Option { self.imp.resolve_extern_crate(extern_crate) } @@ -860,12 +860,6 @@ impl<'db> SemanticsImpl<'db> { self.analyze(path.syntax()).resolve_path(self.db, path) } - // FIXME: This shouldn't exist, but is currently required to always resolve attribute paths in - // the IDE layer due to namespace collisions - fn resolve_path_as_macro(&self, path: &ast::Path) -> Option { - self.analyze(path.syntax()).resolve_path_as_macro(self.db, path) - } - fn resolve_extern_crate(&self, extern_crate: &ast::ExternCrate) -> Option { let krate = self.scope(extern_crate.syntax()).krate()?; krate.dependencies(self.db).into_iter().find_map(|dep| { diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index 23fcd02b57a..8f6c3d995fa 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -29,8 +29,9 @@ use syntax::{ }; use crate::{ - db::HirDatabase, semantics::PathResolution, Adt, BuiltinType, Const, Field, Function, Local, - MacroDef, ModuleDef, Static, Struct, Trait, Type, TypeAlias, TypeParam, Variant, + db::HirDatabase, semantics::PathResolution, Adt, BuiltinAttr, BuiltinType, Const, Field, + Function, Local, MacroDef, ModuleDef, Static, Struct, Tool, Trait, Type, TypeAlias, TypeParam, + Variant, }; use base_db::CrateId; @@ -246,18 +247,6 @@ impl SourceAnalyzer { } } - pub(crate) fn resolve_path_as_macro( - &self, - db: &dyn HirDatabase, - path: &ast::Path, - ) -> Option { - // This must be a normal source file rather than macro file. - let hygiene = Hygiene::new(db.upcast(), self.file_id); - let ctx = body::LowerCtx::with_hygiene(db.upcast(), &hygiene); - let hir_path = Path::from_src(path.clone(), &ctx)?; - resolve_hir_path_as_macro(db, &self.resolver, &hir_path) - } - pub(crate) fn resolve_path( &self, db: &dyn HirDatabase, @@ -318,15 +307,6 @@ impl SourceAnalyzer { let ctx = body::LowerCtx::with_hygiene(db.upcast(), &hygiene); let hir_path = Path::from_src(path.clone(), &ctx)?; - // Case where path is a qualifier of another path, e.g. foo::bar::Baz where we are - // trying to resolve foo::bar. - if let Some(outer_path) = parent().and_then(ast::Path::cast) { - if let Some(qualifier) = outer_path.qualifier() { - if path == &qualifier { - return resolve_hir_path_qualifier(db, &self.resolver, &hir_path); - } - } - } // Case where path is a qualifier of a use tree, e.g. foo::bar::{Baz, Qux} where we are // trying to resolve foo::bar. if let Some(use_tree) = parent().and_then(ast::UseTree::cast) { @@ -337,10 +317,50 @@ impl SourceAnalyzer { } } - if parent().map_or(false, |it| ast::Visibility::can_cast(it.kind())) { + let is_path_of_attr = path + .top_path() + .syntax() + .ancestors() + .nth(2) // Path -> Meta -> Attr + .map_or(false, |it| ast::Attr::can_cast(it.kind())); + + // Case where path is a qualifier of another path, e.g. foo::bar::Baz where we are + // trying to resolve foo::bar. + if let Some(outer_path) = path.parent_path() { + if let Some(qualifier) = outer_path.qualifier() { + if path == &qualifier { + return resolve_hir_path_qualifier(db, &self.resolver, &hir_path); + } + } + } else if is_path_of_attr { + let res = resolve_hir_path_as_macro(db, &self.resolver, &hir_path); + return match res { + Some(_) => res.map(PathResolution::Macro), + None => path.as_single_name_ref().and_then(|name_ref| { + if let Some(builtin) = BuiltinAttr::by_name(&name_ref.text()) { + Some(PathResolution::BuiltinAttr(builtin)) + } else if let Some(tool) = Tool::by_name(&name_ref.text()) { + Some(PathResolution::Tool(tool)) + } else { + None + } + }), + }; + } + + let res = if parent().map_or(false, |it| ast::Visibility::can_cast(it.kind())) { resolve_hir_path_qualifier(db, &self.resolver, &hir_path) } else { resolve_hir_path_(db, &self.resolver, &hir_path, prefer_value_ns) + }; + match res { + Some(_) => res, + None if is_path_of_attr => path + .first_segment() + .and_then(|seg| seg.name_ref()) + .and_then(|name_ref| Tool::by_name(&name_ref.text())) + .map(PathResolution::Tool), + None => None, } } diff --git a/crates/hir_def/src/builtin_attr.rs b/crates/hir_def/src/builtin_attr.rs index 6cd185ceeb2..cd3a8a8605d 100644 --- a/crates/hir_def/src/builtin_attr.rs +++ b/crates/hir_def/src/builtin_attr.rs @@ -2,35 +2,98 @@ //! //! The actual definitions were copied from rustc's `compiler/rustc_feature/src/builtin_attrs.rs`. //! -//! It was last synchronized with upstream commit 835150e70288535bc57bb624792229b9dc94991d. +//! It was last synchronized with upstream commit ae90dcf0207c57c3034f00b07048d63f8b2363c8. //! //! The macros were adjusted to only expand to the attribute name, since that is all we need to do //! name resolution, and `BUILTIN_ATTRIBUTES` is almost entirely unchanged from the original, to //! ease updating. +use once_cell::sync::OnceCell; +use rustc_hash::FxHashMap; + /// Ignored attribute namespaces used by tools. pub const TOOL_MODULES: &[&str] = &["rustfmt", "clippy"]; -type BuiltinAttribute = &'static str; +pub struct BuiltinAttribute { + pub name: &'static str, + pub template: AttributeTemplate, +} + +/// A template that the attribute input must match. +/// Only top-level shape (`#[attr]` vs `#[attr(...)]` vs `#[attr = ...]`) is considered now. +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 { + BUILTIN_LOOKUP_TABLE + .get_or_init(|| { + INERT_ATTRIBUTES.iter().map(|attr| attr.name).enumerate().map(|(a, b)| (b, a)).collect() + }) + .get(name) + .copied() +} + +// impl AttributeTemplate { +// const DEFAULT: AttributeTemplate = +// AttributeTemplate { word: false, list: None, name_value_str: None }; +// } + +/// A convenience macro for constructing attribute templates. +/// E.g., `template!(Word, List: "description")` means that the attribute +/// supports forms `#[attr]` and `#[attr(description)]`. +macro_rules! template { + (Word) => { template!(@ true, None, None) }; + (List: $descr: expr) => { template!(@ false, Some($descr), None) }; + (NameValueStr: $descr: expr) => { template!(@ false, None, Some($descr)) }; + (Word, List: $descr: expr) => { template!(@ true, Some($descr), None) }; + (Word, NameValueStr: $descr: expr) => { template!(@ true, None, Some($descr)) }; + (List: $descr1: expr, NameValueStr: $descr2: expr) => { + template!(@ false, Some($descr1), Some($descr2)) + }; + (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 + } }; +} macro_rules! ungated { ($attr:ident, $typ:expr, $tpl:expr $(,)?) => { - stringify!($attr) + BuiltinAttribute { name: stringify!($attr), template: $tpl } }; } macro_rules! gated { - ($attr:ident $($rest:tt)*) => { - stringify!($attr) + ($attr:ident, $typ:expr, $tpl:expr, $gate:ident, $msg:expr $(,)?) => { + BuiltinAttribute { name: stringify!($attr), template: $tpl } + }; + ($attr:ident, $typ:expr, $tpl:expr, $msg:expr $(,)?) => { + BuiltinAttribute { name: stringify!($attr), template: $tpl } }; } macro_rules! rustc_attr { - (TEST, $attr:ident $($rest:tt)*) => { - stringify!($attr) + (TEST, $attr:ident, $typ:expr, $tpl:expr $(,)?) => { + rustc_attr!( + $attr, + $typ, + $tpl, + concat!( + "the `#[", + stringify!($attr), + "]` attribute is just used for rustc unit tests \ + and will never be stable", + ), + ) }; - ($attr:ident $($rest:tt)*) => { - stringify!($attr) + ($attr:ident, $typ:expr, $tpl:expr, $msg:expr $(,)?) => { + BuiltinAttribute { name: stringify!($attr), template: $tpl } }; } @@ -158,8 +221,8 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ // Plugins: // XXX Modified for use in rust-analyzer - gated!(plugin_registrar), - gated!(plugin), + gated!(plugin_registrar, Normal, template!(Word), experimental!()), + gated!(plugin, CrateLevel, template!(Word), experimental!()), // Testing: gated!(allow_fail, Normal, template!(Word), experimental!(allow_fail)), @@ -195,6 +258,12 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ ), gated!(cmse_nonsecure_entry, AssumedUsed, template!(Word), experimental!(cmse_nonsecure_entry)), + // RFC 2632 + gated!( + default_method_body_is_const, AssumedUsed, template!(Word), const_trait_impl, + "`default_method_body_is_const` is a temporary placeholder for declaring default bodies \ + as `const`, which may be removed or renamed in the future." + ), // ========================================================================== // Internal attributes: Stability, deprecation, and unsafe: @@ -258,10 +327,6 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ ), gated!(panic_runtime, AssumedUsed, template!(Word), experimental!(panic_runtime)), gated!(needs_panic_runtime, AssumedUsed, template!(Word), experimental!(needs_panic_runtime)), - gated!( - unwind, AssumedUsed, template!(List: "allowed|aborts"), unwind_attributes, - experimental!(unwind), - ), gated!( compiler_builtins, AssumedUsed, template!(Word), "the `#[compiler_builtins]` attribute is used to identify the `compiler_builtins` crate \ @@ -287,7 +352,11 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ // Internal attributes, Macro related: // ========================================================================== - rustc_attr!(rustc_builtin_macro, AssumedUsed, template!(Word, NameValueStr: "name"), IMPL_DETAIL), + rustc_attr!( + rustc_builtin_macro, AssumedUsed, + template!(Word, List: "name, /*opt*/ attributes(name1, name2, ...)"), + IMPL_DETAIL, + ), rustc_attr!(rustc_proc_macro_decls, Normal, template!(Word), INTERNAL_UNSTABLE), rustc_attr!( rustc_macro_transparency, AssumedUsed, @@ -344,7 +413,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ lang, Normal, template!(NameValueStr: "name"), lang_items, "language items are subject to change", ), - gated!(rustc_diagnostic_item), // XXX modified in rust-analyzer + gated!(rustc_diagnostic_item, Normal, template!(NameValueStr: "name"), experimental!()), // XXX Modified for use in rust-analyzer gated!( // Used in resolve: prelude_import, AssumedUsed, template!(Word), @@ -428,6 +497,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_attr!(TEST, rustc_dump_program_clauses, AssumedUsed, template!(Word)), rustc_attr!(TEST, rustc_dump_env_program_clauses, AssumedUsed, template!(Word)), rustc_attr!(TEST, rustc_object_lifetime_default, AssumedUsed, template!(Word)), + rustc_attr!(TEST, rustc_dump_vtable, AssumedUsed, template!(Word)), rustc_attr!(TEST, rustc_dummy, Normal, template!(Word /* doesn't matter*/)), gated!( omit_gdb_pretty_printer_section, AssumedUsed, template!(Word), diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index d7a35caf29e..ad4a4aa752b 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs @@ -1798,14 +1798,18 @@ impl ModCollector<'_, '_> { let registered = self.def_collector.registered_tools.iter().map(SmolStr::as_str); let is_tool = builtin_attr::TOOL_MODULES.iter().copied().chain(registered).any(pred); + // FIXME: tool modules can be shadowed by actual modules if is_tool { return true; } if segments.len() == 1 { let registered = self.def_collector.registered_attrs.iter().map(SmolStr::as_str); - let is_inert = - builtin_attr::INERT_ATTRIBUTES.iter().copied().chain(registered).any(pred); + let is_inert = builtin_attr::INERT_ATTRIBUTES + .iter() + .map(|it| it.name) + .chain(registered) + .any(pred); return is_inert; } } diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs index d5dca95fbaf..edcea7aace7 100644 --- a/crates/ide/src/doc_links.rs +++ b/crates/ide/src/doc_links.rs @@ -181,7 +181,9 @@ pub(crate) fn resolve_doc_path_for_def( Definition::TypeAlias(it) => it.resolve_doc_path(db, link, ns), Definition::Macro(it) => it.resolve_doc_path(db, link, ns), Definition::Field(it) => it.resolve_doc_path(db, link, ns), - Definition::BuiltinType(_) + Definition::BuiltinAttr(_) + | Definition::Tool(_) + | Definition::BuiltinType(_) | Definition::SelfType(_) | Definition::Local(_) | Definition::GenericParam(_) @@ -492,9 +494,11 @@ fn filename_and_frag_for_def( // FIXME fragment numbering return Some((adt, file, Some(String::from("impl")))); } - Definition::Local(_) => return None, - Definition::GenericParam(_) => return None, - Definition::Label(_) => return None, + Definition::Local(_) + | Definition::GenericParam(_) + | Definition::Label(_) + | Definition::BuiltinAttr(_) + | Definition::Tool(_) => return None, }; Some((def, res, None)) diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs index dd4a961d31c..e72ed8c2b28 100644 --- a/crates/ide/src/hover/render.rs +++ b/crates/ide/src/hover/render.rs @@ -369,6 +369,8 @@ pub(super) fn definition( } Definition::GenericParam(it) => label_and_docs(db, it), Definition::Label(it) => return Some(Markup::fenced_block(&it.name(db))), + Definition::BuiltinAttr(_) => return None, // FIXME + Definition::Tool(_) => return None, // FIXME }; markup(docs.filter(|_| config.documentation.is_some()).map(Into::into), label, mod_path) diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index 5718b9097c4..4187e9ca361 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -3678,7 +3678,6 @@ fn hover_clippy_lint() { #[test] fn hover_attr_path_qualifier() { - cov_mark::check!(name_ref_classify_attr_path_qualifier); check( r#" //- /foo.rs crate:foo diff --git a/crates/ide/src/navigation_target.rs b/crates/ide/src/navigation_target.rs index 7deb6cae38b..aa865ddc21d 100644 --- a/crates/ide/src/navigation_target.rs +++ b/crates/ide/src/navigation_target.rs @@ -214,6 +214,8 @@ impl TryToNav for Definition { Definition::Trait(it) => it.try_to_nav(db), Definition::TypeAlias(it) => it.try_to_nav(db), Definition::BuiltinType(_) => None, + Definition::Tool(_) => None, + Definition::BuiltinAttr(_) => None, } } } diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs index cf98bc32faf..77d202cc39c 100644 --- a/crates/ide/src/static_index.rs +++ b/crates/ide/src/static_index.rs @@ -299,6 +299,7 @@ pub func() { r#" //- minicore:derive #[rustc_builtin_macro] +//^^^^^^^^^^^^^^^^^^^ pub macro Copy {} //^^^^ #[derive(Copy)] diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs index b8177b4ab6c..52e729ee68c 100644 --- a/crates/ide/src/syntax_highlighting/highlight.rs +++ b/crates/ide/src/syntax_highlighting/highlight.rs @@ -542,6 +542,8 @@ fn highlight_def( h } Definition::Label(_) => Highlight::new(HlTag::Symbol(SymbolKind::Label)), + Definition::BuiltinAttr(_) => Highlight::new(HlTag::Symbol(SymbolKind::Label)), // FIXME + Definition::Tool(_) => Highlight::new(HlTag::Symbol(SymbolKind::Label)), // FIXME }; let famous_defs = FamousDefs(sema, krate); diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs index 686fd5baa72..d4dba508344 100644 --- a/crates/ide/src/syntax_highlighting/inject.rs +++ b/crates/ide/src/syntax_highlighting/inject.rs @@ -263,6 +263,8 @@ fn module_def_to_hl_tag(def: Definition) -> HlTag { hir::GenericParam::ConstParam(_) => SymbolKind::ConstParam, }, Definition::Label(_) => SymbolKind::Label, + Definition::BuiltinAttr(_) => SymbolKind::Label, // FIXME + Definition::Tool(_) => SymbolKind::Label, // FIXME }; HlTag::Symbol(symbol) } diff --git a/crates/ide_db/src/defs.rs b/crates/ide_db/src/defs.rs index 1d7f4392dd0..26cdaa1ac14 100644 --- a/crates/ide_db/src/defs.rs +++ b/crates/ide_db/src/defs.rs @@ -7,9 +7,9 @@ use arrayvec::ArrayVec; use hir::{ - Adt, AsAssocItem, AssocItem, BuiltinType, Const, Field, Function, GenericParam, HasVisibility, - Impl, ItemInNs, Label, Local, MacroDef, Module, ModuleDef, Name, PathResolution, Semantics, - Static, Trait, TypeAlias, Variant, Visibility, + Adt, AsAssocItem, AssocItem, BuiltinAttr, BuiltinType, Const, Field, Function, GenericParam, + HasVisibility, Impl, ItemInNs, Label, Local, MacroDef, Module, ModuleDef, Name, PathResolution, + Semantics, Static, Tool, Trait, TypeAlias, Variant, Visibility, }; use stdx::impl_from; use syntax::{ @@ -37,6 +37,8 @@ pub enum Definition { Local(Local), GenericParam(GenericParam), Label(Label), + BuiltinAttr(BuiltinAttr), + Tool(Tool), } impl Definition { @@ -48,10 +50,9 @@ impl Definition { Some(parent) => parent, None => return Default::default(), }; + // resolve derives if possible if let Some(ident) = ast::Ident::cast(token.clone()) { - let attr = parent - .ancestors() - .find_map(ast::TokenTree::cast) + let attr = ast::TokenTree::cast(parent.clone()) .and_then(|tt| tt.parent_meta()) .and_then(|meta| meta.parent_attr()); if let Some(attr) = attr { @@ -128,7 +129,9 @@ impl Definition { Definition::Local(it) => it.module(db), Definition::GenericParam(it) => it.module(db), Definition::Label(it) => it.module(db), - Definition::BuiltinType(_) => return None, + Definition::BuiltinAttr(_) | Definition::BuiltinType(_) | Definition::Tool(_) => { + return None + } }; Some(module) } @@ -146,7 +149,9 @@ impl Definition { Definition::Variant(it) => it.visibility(db), Definition::BuiltinType(_) => Visibility::Public, Definition::Macro(_) => return None, - Definition::SelfType(_) + Definition::BuiltinAttr(_) + | Definition::Tool(_) + | Definition::SelfType(_) | Definition::Local(_) | Definition::GenericParam(_) | Definition::Label(_) => return None, @@ -171,6 +176,8 @@ impl Definition { Definition::Local(it) => it.name(db)?, Definition::GenericParam(it) => it.name(db), Definition::Label(it) => it.name(db), + Definition::BuiltinAttr(_) => return None, // FIXME + Definition::Tool(_) => return None, // FIXME }; Some(name) } @@ -450,30 +457,7 @@ impl NameRefClass { } } } - let top_path = path.top_path(); - let is_attribute_path = top_path - .syntax() - .ancestors() - .find_map(ast::Attr::cast) - .map(|attr| attr.path().as_ref() == Some(&top_path)); - return match is_attribute_path { - Some(true) if path == top_path => sema - .resolve_path_as_macro(&path) - .filter(|mac| mac.kind() == hir::MacroKind::Attr) - .map(Definition::Macro) - .map(NameRefClass::Definition), - // in case of the path being a qualifier, don't resolve to anything but a module - Some(true) => match sema.resolve_path(&path)? { - PathResolution::Def(ModuleDef::Module(module)) => { - cov_mark::hit!(name_ref_classify_attr_path_qualifier); - Some(NameRefClass::Definition(Definition::Module(module))) - } - _ => None, - }, - // inside attribute, but our path isn't part of the attribute's path(might be in its expression only) - Some(false) => None, - None => sema.resolve_path(&path).map(Into::into).map(NameRefClass::Definition), - }; + return sema.resolve_path(&path).map(Into::into).map(NameRefClass::Definition); } let extern_crate = ast::ExternCrate::cast(parent)?; @@ -566,6 +550,8 @@ impl From for Definition { PathResolution::Macro(def) => Definition::Macro(def), PathResolution::SelfType(impl_def) => Definition::SelfType(impl_def), PathResolution::ConstParam(par) => Definition::GenericParam(par.into()), + PathResolution::BuiltinAttr(attr) => Definition::BuiltinAttr(attr), + PathResolution::Tool(tool) => Definition::Tool(tool), } } } diff --git a/crates/ide_db/src/path_transform.rs b/crates/ide_db/src/path_transform.rs index b8b1307a3c0..a4db8b3848b 100644 --- a/crates/ide_db/src/path_transform.rs +++ b/crates/ide_db/src/path_transform.rs @@ -164,7 +164,9 @@ impl<'a> Ctx<'a> { | hir::PathResolution::ConstParam(_) | hir::PathResolution::SelfType(_) | hir::PathResolution::Macro(_) - | hir::PathResolution::AssocItem(_) => (), + | hir::PathResolution::AssocItem(_) + | hir::PathResolution::BuiltinAttr(_) + | hir::PathResolution::Tool(_) => (), } Some(()) } diff --git a/crates/ide_db/src/rename.rs b/crates/ide_db/src/rename.rs index 678153c6e1d..35bd4d02a63 100644 --- a/crates/ide_db/src/rename.rs +++ b/crates/ide_db/src/rename.rs @@ -115,8 +115,6 @@ impl Definition { Definition::Static(it) => name_range(it, sema), Definition::Trait(it) => name_range(it, sema), Definition::TypeAlias(it) => name_range(it, sema), - Definition::BuiltinType(_) => return None, - Definition::SelfType(_) => return None, Definition::Local(local) => { let src = local.source(sema.db); let name = match &src.value { @@ -146,6 +144,10 @@ impl Definition { let lifetime = src.value.lifetime()?; src.with_value(lifetime.syntax()).original_file_range_opt(sema.db) } + Definition::BuiltinType(_) => return None, + Definition::SelfType(_) => return None, + Definition::BuiltinAttr(_) => return None, + Definition::Tool(_) => return None, }; return res; From d1677f3286d57009361eee85fb19d523c64b937c Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 3 Dec 2021 16:53:30 +0100 Subject: [PATCH 2/6] Remove syntax highlighting hack for builtin attrs --- crates/hir/src/lib.rs | 4 +- crates/ide/src/syntax_highlighting.rs | 1 + .../ide/src/syntax_highlighting/highlight.rs | 50 ++++--------------- crates/ide/src/syntax_highlighting/inject.rs | 4 +- crates/ide/src/syntax_highlighting/tags.rs | 30 +++++------ .../test_data/highlight_doctest.html | 10 ++-- .../test_data/highlight_strings.html | 20 ++++---- .../test_data/highlight_unsafe.html | 2 +- .../test_data/highlighting.html | 10 ++-- crates/ide_completion/src/item.rs | 2 + crates/ide_db/src/lib.rs | 2 + crates/rust-analyzer/src/semantic_tokens.rs | 1 + crates/rust-analyzer/src/to_proto.rs | 9 ++-- 13 files changed, 62 insertions(+), 83 deletions(-) diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index c0c2cc78182..cfa674cafa6 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -2028,7 +2028,7 @@ pub struct BuiltinAttr(usize); impl BuiltinAttr { pub(crate) fn by_name(name: &str) -> Option { - // TODO: def maps registered attrs? + // FIXME: def maps registered attrs? hir_def::builtin_attr::find_builtin_attr_idx(name).map(Self) } } @@ -2038,7 +2038,7 @@ pub struct Tool(usize); impl Tool { pub(crate) fn by_name(name: &str) -> Option { - // TODO: def maps registered tools + // FIXME: def maps registered tools hir_def::builtin_attr::TOOL_MODULES.iter().position(|&tool| tool == name).map(Self) } } diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs index 8b613fde54d..118f8dd9be0 100644 --- a/crates/ide/src/syntax_highlighting.rs +++ b/crates/ide/src/syntax_highlighting.rs @@ -115,6 +115,7 @@ pub struct HlRange { // parameter:: Emitted for non-self function parameters. // property:: Emitted for struct and union fields. // selfKeyword:: Emitted for the self function parameter and self path-specifier. +// tool:: Emitted for tool modules. // typeParameter:: Emitted for type parameters. // unresolvedReference:: Emitted for unresolved references, names that rust-analyzer can't find the definition of. // variable:: Emitted for locals, constants and statics. diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs index 52e729ee68c..340168a90d0 100644 --- a/crates/ide/src/syntax_highlighting/highlight.rs +++ b/crates/ide/src/syntax_highlighting/highlight.rs @@ -208,22 +208,14 @@ fn node( }, // Highlight references like the definitions they resolve to ast::NameRef(name_ref) => { - if node.ancestors().any(|it| it.kind() == ATTR) { - - // FIXME: We highlight paths in attributes slightly differently to work around this module - // currently not knowing about tool attributes and rustc builtin attributes as - // we do not want to resolve those to functions that may be defined in scope. - highlight_name_ref_in_attr(sema, name_ref) - } else { - highlight_name_ref( - sema, - krate, - bindings_shadow_count, - &mut binding_hash, - syntactic_name_ref_highlighting, - name_ref, - ) - } + highlight_name_ref( + sema, + krate, + bindings_shadow_count, + &mut binding_hash, + syntactic_name_ref_highlighting, + name_ref, + ) }, ast::Lifetime(lifetime) => { match NameClass::classify_lifetime(sema, &lifetime) { @@ -243,28 +235,6 @@ fn node( Some((highlight, binding_hash)) } -fn highlight_name_ref_in_attr(sema: &Semantics, name_ref: ast::NameRef) -> Highlight { - match NameRefClass::classify(sema, &name_ref) { - Some(name_class) => match name_class { - NameRefClass::Definition(Definition::Module(_)) - if name_ref - .syntax() - .ancestors() - .find_map(ast::Path::cast) - .map_or(false, |it| it.parent_path().is_some()) => - { - HlTag::Symbol(SymbolKind::Module) - } - NameRefClass::Definition(Definition::Macro(m)) if m.kind() == hir::MacroKind::Attr => { - HlTag::Symbol(SymbolKind::Macro) - } - _ => HlTag::BuiltinAttr, - }, - None => HlTag::BuiltinAttr, - } - .into() -} - fn highlight_name_ref( sema: &Semantics, krate: Option, @@ -542,8 +512,8 @@ fn highlight_def( h } Definition::Label(_) => Highlight::new(HlTag::Symbol(SymbolKind::Label)), - Definition::BuiltinAttr(_) => Highlight::new(HlTag::Symbol(SymbolKind::Label)), // FIXME - Definition::Tool(_) => Highlight::new(HlTag::Symbol(SymbolKind::Label)), // FIXME + Definition::BuiltinAttr(_) => Highlight::new(HlTag::Symbol(SymbolKind::BuiltinAttr)), + Definition::Tool(_) => Highlight::new(HlTag::Symbol(SymbolKind::Tool)), }; let famous_defs = FamousDefs(sema, krate); diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs index d4dba508344..50f74cdec1b 100644 --- a/crates/ide/src/syntax_highlighting/inject.rs +++ b/crates/ide/src/syntax_highlighting/inject.rs @@ -263,8 +263,8 @@ fn module_def_to_hl_tag(def: Definition) -> HlTag { hir::GenericParam::ConstParam(_) => SymbolKind::ConstParam, }, Definition::Label(_) => SymbolKind::Label, - Definition::BuiltinAttr(_) => SymbolKind::Label, // FIXME - Definition::Tool(_) => SymbolKind::Label, // FIXME + Definition::BuiltinAttr(_) => SymbolKind::BuiltinAttr, + Definition::Tool(_) => SymbolKind::Tool, }; HlTag::Symbol(symbol) } diff --git a/crates/ide/src/syntax_highlighting/tags.rs b/crates/ide/src/syntax_highlighting/tags.rs index 92c7fcab76f..a19eee58172 100644 --- a/crates/ide/src/syntax_highlighting/tags.rs +++ b/crates/ide/src/syntax_highlighting/tags.rs @@ -20,7 +20,6 @@ pub enum HlTag { Attribute, BoolLiteral, - BuiltinAttr, BuiltinType, ByteLiteral, CharLiteral, @@ -125,30 +124,31 @@ impl HlTag { fn as_str(self) -> &'static str { match self { HlTag::Symbol(symbol) => match symbol { + SymbolKind::BuiltinAttr => "builtin_attr", SymbolKind::Const => "constant", - SymbolKind::Static => "static", + SymbolKind::ConstParam => "const_param", SymbolKind::Enum => "enum", - SymbolKind::Variant => "enum_variant", - SymbolKind::Struct => "struct", - SymbolKind::Union => "union", SymbolKind::Field => "field", - SymbolKind::Module => "module", - SymbolKind::Trait => "trait", SymbolKind::Function => "function", + SymbolKind::Impl => "self_type", + SymbolKind::Label => "label", + SymbolKind::LifetimeParam => "lifetime", + SymbolKind::Local => "variable", + SymbolKind::Macro => "macro", + SymbolKind::Module => "module", + SymbolKind::SelfParam => "self_keyword", + SymbolKind::Static => "static", + SymbolKind::Struct => "struct", + SymbolKind::Tool => "tool", + SymbolKind::Trait => "trait", SymbolKind::TypeAlias => "type_alias", SymbolKind::TypeParam => "type_param", - SymbolKind::ConstParam => "const_param", - SymbolKind::LifetimeParam => "lifetime", - SymbolKind::Macro => "macro", - SymbolKind::Local => "variable", - SymbolKind::Label => "label", + SymbolKind::Union => "union", SymbolKind::ValueParam => "value_param", - SymbolKind::SelfParam => "self_keyword", - SymbolKind::Impl => "self_type", + SymbolKind::Variant => "enum_variant", }, HlTag::Attribute => "attribute", HlTag::BoolLiteral => "bool_literal", - HlTag::BuiltinAttr => "builtin_attr", HlTag::BuiltinType => "builtin_type", HlTag::ByteLiteral => "byte_literal", HlTag::CharLiteral => "char_literal", diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html index 9c92bd3e742..1e778cc6194 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html @@ -72,7 +72,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd /// # Examples /// /// ``` - /// # #![allow(unused_mut)] + /// # #![allow(unused_mut)] /// let mut foo: Foo = Foo::new(); /// ``` pub const fn new() -> Foo { @@ -143,12 +143,12 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd /// /// ``` /// loop {} -#[cfg_attr(not(feature = "false"), doc = "loop {}")] -#[doc = "loop {}"] +#[cfg_attr(not(feature = "false"), doc = "loop {}")] +#[doc = "loop {}"] /// ``` /// -#[cfg_attr(feature = "alloc", doc = "```rust")] -#[cfg_attr(not(feature = "alloc"), doc = "```ignore")] +#[cfg_attr(feature = "alloc", doc = "```rust")] +#[cfg_attr(not(feature = "alloc"), doc = "```ignore")] /// let _ = example(&alloc::vec![1, 2, 3]); /// ``` pub fn mix_and_match() {} diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html index 893094b24a4..b44fc817c78 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html @@ -45,14 +45,14 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd $crate::io::_print($crate::format_args_nl!($($arg)*)); }) } -#[rustc_builtin_macro] -#[macro_export] +#[rustc_builtin_macro] +#[macro_export] macro_rules! format_args {} -#[rustc_builtin_macro] -#[macro_export] +#[rustc_builtin_macro] +#[macro_export] macro_rules! const_format_args {} -#[rustc_builtin_macro] -#[macro_export] +#[rustc_builtin_macro] +#[macro_export] macro_rules! format_args_nl {} mod panic { @@ -77,12 +77,12 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd } } -#[rustc_builtin_macro(std_panic)] -#[macro_export] +#[rustc_builtin_macro(std_panic)] +#[macro_export] macro_rules! panic {} -#[rustc_builtin_macro] +#[rustc_builtin_macro] macro_rules! assert {} -#[rustc_builtin_macro] +#[rustc_builtin_macro] macro_rules! asm {} macro_rules! toho { diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html index 2b86340efd7..65dfbdf2119 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html @@ -54,7 +54,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd unsafe fn unsafe_method(&self) {} } -#[repr(packed)] +#[repr(packed)] struct Packed { a: u16, } diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html index d8c9827b5e0..da607b51211 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html @@ -43,15 +43,15 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
use inner::{self as inner_mod};
 mod inner {}
 
-#[proc_macros::identity]
+#[proc_macros::identity]
 pub mod ops {
-    #[lang = "fn_once"]
+    #[lang = "fn_once"]
     pub trait FnOnce<Args> {}
 
-    #[lang = "fn_mut"]
+    #[lang = "fn_mut"]
     pub trait FnMut<Args>: FnOnce<Args> {}
 
-    #[lang = "fn"]
+    #[lang = "fn"]
     pub trait Fn<Args>: FnMut<Args> {}
 }
 
@@ -86,7 +86,7 @@ proc_macros::mirror! {
     }
 }
 
-#[derive(Copy)]
+#[derive(Copy)]
 struct FooCopy {
     x: u32,
 }
diff --git a/crates/ide_completion/src/item.rs b/crates/ide_completion/src/item.rs
index ebe227d260a..e80a8edd9cf 100644
--- a/crates/ide_completion/src/item.rs
+++ b/crates/ide_completion/src/item.rs
@@ -232,6 +232,7 @@ impl CompletionItemKind {
     pub(crate) fn tag(&self) -> &'static str {
         match self {
             CompletionItemKind::SymbolKind(kind) => match kind {
+                SymbolKind::BuiltinAttr => "ba",
                 SymbolKind::Const => "ct",
                 SymbolKind::ConstParam => "cp",
                 SymbolKind::Enum => "en",
@@ -246,6 +247,7 @@ impl CompletionItemKind {
                 SymbolKind::SelfParam => "sp",
                 SymbolKind::Static => "sc",
                 SymbolKind::Struct => "st",
+                SymbolKind::Tool => "tl",
                 SymbolKind::Trait => "tt",
                 SymbolKind::TypeAlias => "ta",
                 SymbolKind::TypeParam => "tp",
diff --git a/crates/ide_db/src/lib.rs b/crates/ide_db/src/lib.rs
index 1250008984e..6eaea53f5c1 100644
--- a/crates/ide_db/src/lib.rs
+++ b/crates/ide_db/src/lib.rs
@@ -145,6 +145,7 @@ fn line_index(db: &dyn LineIndexDatabase, file_id: FileId) -> Arc {
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
 pub enum SymbolKind {
+    BuiltinAttr,
     Const,
     ConstParam,
     Enum,
@@ -159,6 +160,7 @@ pub enum SymbolKind {
     SelfParam,
     Static,
     Struct,
+    Tool,
     Trait,
     TypeAlias,
     TypeParam,
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs
index 3117b757285..9d19482bee3 100644
--- a/crates/rust-analyzer/src/semantic_tokens.rs
+++ b/crates/rust-analyzer/src/semantic_tokens.rs
@@ -65,6 +65,7 @@ define_semantic_token_types![
     (SELF_KEYWORD, "selfKeyword"),
     (SEMICOLON, "semicolon"),
     (TYPE_ALIAS, "typeAlias"),
+    (TOOL, "tool"),
     (UNION, "union"),
     (UNRESOLVED_REFERENCE, "unresolvedReference"),
 ];
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 8e77df95982..1dd4dabec4d 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -50,8 +50,8 @@ pub(crate) fn symbol_kind(symbol_kind: SymbolKind) -> lsp_types::SymbolKind {
         SymbolKind::Enum => lsp_types::SymbolKind::ENUM,
         SymbolKind::Variant => lsp_types::SymbolKind::ENUM_MEMBER,
         SymbolKind::Trait => lsp_types::SymbolKind::INTERFACE,
-        SymbolKind::Macro => lsp_types::SymbolKind::FUNCTION,
-        SymbolKind::Module => lsp_types::SymbolKind::MODULE,
+        SymbolKind::Macro | SymbolKind::BuiltinAttr => lsp_types::SymbolKind::FUNCTION,
+        SymbolKind::Module | SymbolKind::Tool => lsp_types::SymbolKind::MODULE,
         SymbolKind::TypeAlias | SymbolKind::TypeParam => lsp_types::SymbolKind::TYPE_PARAMETER,
         SymbolKind::Field => lsp_types::SymbolKind::FIELD,
         SymbolKind::Static => lsp_types::SymbolKind::CONSTANT,
@@ -128,6 +128,8 @@ pub(crate) fn completion_item_kind(
             SymbolKind::Union => lsp_types::CompletionItemKind::STRUCT,
             SymbolKind::ValueParam => lsp_types::CompletionItemKind::VALUE,
             SymbolKind::Variant => lsp_types::CompletionItemKind::ENUM_MEMBER,
+            SymbolKind::BuiltinAttr => lsp_types::CompletionItemKind::FUNCTION,
+            SymbolKind::Tool => lsp_types::CompletionItemKind::MODULE,
         },
     }
 }
@@ -499,10 +501,11 @@ fn semantic_token_type_and_modifiers(
             SymbolKind::TypeAlias => semantic_tokens::TYPE_ALIAS,
             SymbolKind::Trait => lsp_types::SemanticTokenType::INTERFACE,
             SymbolKind::Macro => lsp_types::SemanticTokenType::MACRO,
+            SymbolKind::BuiltinAttr => semantic_tokens::BUILTIN_ATTRIBUTE,
+            SymbolKind::Tool => semantic_tokens::TOOL,
         },
         HlTag::Attribute => semantic_tokens::ATTRIBUTE,
         HlTag::BoolLiteral => semantic_tokens::BOOLEAN,
-        HlTag::BuiltinAttr => semantic_tokens::BUILTIN_ATTRIBUTE,
         HlTag::BuiltinType => semantic_tokens::BUILTIN_TYPE,
         HlTag::ByteLiteral | HlTag::NumericLiteral => lsp_types::SemanticTokenType::NUMBER,
         HlTag::CharLiteral => semantic_tokens::CHAR,

From db559e504966238248fe7775e66c6dc4acd6fe0d Mon Sep 17 00:00:00 2001
From: Lukas Wirth 
Date: Fri, 3 Dec 2021 17:07:06 +0100
Subject: [PATCH 3/6] Simplify

---
 crates/hir/src/source_analyzer.rs                  | 14 +++++++++-----
 .../test_data/highlighting.html                    |  1 +
 crates/ide/src/syntax_highlighting/tests.rs        |  1 +
 3 files changed, 11 insertions(+), 5 deletions(-)

diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index 8f6c3d995fa..e14cd43ede8 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -252,7 +252,9 @@ impl SourceAnalyzer {
         db: &dyn HirDatabase,
         path: &ast::Path,
     ) -> Option {
-        let parent = || path.syntax().parent();
+        let parent = path.syntax().parent();
+        let parent = || parent.clone();
+
         let mut prefer_value_ns = false;
         if let Some(path_expr) = parent().and_then(ast::PathExpr::cast) {
             let expr_id = self.expr_id(db, &path_expr.into())?;
@@ -337,10 +339,10 @@ impl SourceAnalyzer {
             return match res {
                 Some(_) => res.map(PathResolution::Macro),
                 None => path.as_single_name_ref().and_then(|name_ref| {
-                    if let Some(builtin) = BuiltinAttr::by_name(&name_ref.text()) {
-                        Some(PathResolution::BuiltinAttr(builtin))
-                    } else if let Some(tool) = Tool::by_name(&name_ref.text()) {
-                        Some(PathResolution::Tool(tool))
+                    if let builtin @ Some(_) = BuiltinAttr::by_name(&name_ref.text()) {
+                        builtin.map(PathResolution::BuiltinAttr)
+                    } else if let tool @ Some(_) = Tool::by_name(&name_ref.text()) {
+                        tool.map(PathResolution::Tool)
                     } else {
                         None
                     }
@@ -355,6 +357,8 @@ impl SourceAnalyzer {
         };
         match res {
             Some(_) => res,
+            // this labels any path that starts with a tool module as the tool itself, this is technically wrong
+            // but there is no benefit in differentiating these two cases for the time being
             None if is_path_of_attr => path
                 .first_segment()
                 .and_then(|seg| seg.name_ref())
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
index da607b51211..370f58080e6 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
@@ -43,6 +43,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 
use inner::{self as inner_mod};
 mod inner {}
 
+#[allow()]
 #[proc_macros::identity]
 pub mod ops {
     #[lang = "fn_once"]
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index e74f39a8650..63a1417c6aa 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -16,6 +16,7 @@ fn test_highlighting() {
 use inner::{self as inner_mod};
 mod inner {}
 
+#[allow()]
 #[proc_macros::identity]
 pub mod ops {
     #[lang = "fn_once"]

From 70b8331fd51774f764affded4127c2ef25df87a1 Mon Sep 17 00:00:00 2001
From: Lukas Wirth 
Date: Fri, 3 Dec 2021 17:10:56 +0100
Subject: [PATCH 4/6] Basic hover for builtin-attr and tool modules

---
 crates/hir/src/lib.rs          | 10 ++++++++++
 crates/ide/src/hover/render.rs |  5 +++--
 2 files changed, 13 insertions(+), 2 deletions(-)

diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index cfa674cafa6..e64a8182c9c 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -2031,6 +2031,11 @@ impl BuiltinAttr {
         // FIXME: def maps registered attrs?
         hir_def::builtin_attr::find_builtin_attr_idx(name).map(Self)
     }
+
+    pub fn name(&self, _: &dyn HirDatabase) -> &str {
+        // FIXME: Return a `Name` here
+        hir_def::builtin_attr::INERT_ATTRIBUTES[self.0].name
+    }
 }
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
@@ -2041,6 +2046,11 @@ impl Tool {
         // FIXME: def maps registered tools
         hir_def::builtin_attr::TOOL_MODULES.iter().position(|&tool| tool == name).map(Self)
     }
+
+    pub fn name(&self, _: &dyn HirDatabase) -> &str {
+        // FIXME: Return a `Name` here
+        hir_def::builtin_attr::TOOL_MODULES[self.0]
+    }
 }
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs
index e72ed8c2b28..46f5b2e35be 100644
--- a/crates/ide/src/hover/render.rs
+++ b/crates/ide/src/hover/render.rs
@@ -369,8 +369,9 @@ pub(super) fn definition(
         }
         Definition::GenericParam(it) => label_and_docs(db, it),
         Definition::Label(it) => return Some(Markup::fenced_block(&it.name(db))),
-        Definition::BuiltinAttr(_) => return None, // FIXME
-        Definition::Tool(_) => return None,        // FIXME
+        // FIXME: We should be able to show more info about these
+        Definition::BuiltinAttr(it) => return Some(Markup::fenced_block(&it.name(db))),
+        Definition::Tool(it) => return Some(Markup::fenced_block(&it.name(db))),
     };
 
     markup(docs.filter(|_| config.documentation.is_some()).map(Into::into), label, mod_path)

From d174158abce0ab7e9f2d3bc7556ab7070d38aaa4 Mon Sep 17 00:00:00 2001
From: Lukas Wirth 
Date: Fri, 3 Dec 2021 17:15:19 +0100
Subject: [PATCH 5/6] Rename things: Tool -> ToolModule

---
 crates/hir/src/lib.rs                           |  4 ++--
 crates/hir/src/semantics.rs                     |  6 +++---
 crates/hir/src/source_analyzer.rs               | 12 ++++++------
 crates/ide/src/doc_links.rs                     |  4 ++--
 crates/ide/src/hover/render.rs                  |  2 +-
 crates/ide/src/navigation_target.rs             |  2 +-
 crates/ide/src/syntax_highlighting.rs           |  2 +-
 crates/ide/src/syntax_highlighting/highlight.rs |  2 +-
 crates/ide/src/syntax_highlighting/inject.rs    |  2 +-
 crates/ide/src/syntax_highlighting/tags.rs      |  2 +-
 crates/ide_completion/src/item.rs               |  2 +-
 crates/ide_db/src/defs.rs                       | 12 ++++++------
 crates/ide_db/src/lib.rs                        |  2 +-
 crates/ide_db/src/path_transform.rs             |  2 +-
 crates/ide_db/src/rename.rs                     |  2 +-
 crates/rust-analyzer/src/semantic_tokens.rs     |  2 +-
 crates/rust-analyzer/src/to_proto.rs            |  6 +++---
 17 files changed, 33 insertions(+), 33 deletions(-)

diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index e64a8182c9c..c7f94ff9aa6 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -2039,9 +2039,9 @@ impl BuiltinAttr {
 }
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-pub struct Tool(usize);
+pub struct ToolModule(usize);
 
-impl Tool {
+impl ToolModule {
     pub(crate) fn by_name(name: &str) -> Option {
         // FIXME: def maps registered tools
         hir_def::builtin_attr::TOOL_MODULES.iter().position(|&tool| tool == name).map(Self)
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index c48bd1b0538..ed1b2f64fd1 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -27,7 +27,7 @@ use crate::{
     source_analyzer::{resolve_hir_path, resolve_hir_path_as_macro, SourceAnalyzer},
     Access, AssocItem, BuiltinAttr, Callable, ConstParam, Crate, Field, Function, HasSource,
     HirFileId, Impl, InFile, Label, LifetimeParam, Local, MacroDef, Module, ModuleDef, Name, Path,
-    ScopeDef, Tool, Trait, Type, TypeAlias, TypeParam, VariantDef,
+    ScopeDef, ToolModule, Trait, Type, TypeAlias, TypeParam, VariantDef,
 };
 
 #[derive(Debug, Clone, PartialEq, Eq)]
@@ -44,7 +44,7 @@ pub enum PathResolution {
     Macro(MacroDef),
     AssocItem(AssocItem),
     BuiltinAttr(BuiltinAttr),
-    Tool(Tool),
+    ToolModule(ToolModule),
 }
 
 impl PathResolution {
@@ -66,7 +66,7 @@ impl PathResolution {
                 Some(TypeNs::TypeAliasId((*alias).into()))
             }
             PathResolution::BuiltinAttr(_)
-            | PathResolution::Tool(_)
+            | PathResolution::ToolModule(_)
             | PathResolution::Local(_)
             | PathResolution::Macro(_)
             | PathResolution::ConstParam(_) => None,
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index e14cd43ede8..762f172c2af 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -30,8 +30,8 @@ use syntax::{
 
 use crate::{
     db::HirDatabase, semantics::PathResolution, Adt, BuiltinAttr, BuiltinType, Const, Field,
-    Function, Local, MacroDef, ModuleDef, Static, Struct, Tool, Trait, Type, TypeAlias, TypeParam,
-    Variant,
+    Function, Local, MacroDef, ModuleDef, Static, Struct, ToolModule, Trait, Type, TypeAlias,
+    TypeParam, Variant,
 };
 use base_db::CrateId;
 
@@ -341,8 +341,8 @@ impl SourceAnalyzer {
                 None => path.as_single_name_ref().and_then(|name_ref| {
                     if let builtin @ Some(_) = BuiltinAttr::by_name(&name_ref.text()) {
                         builtin.map(PathResolution::BuiltinAttr)
-                    } else if let tool @ Some(_) = Tool::by_name(&name_ref.text()) {
-                        tool.map(PathResolution::Tool)
+                    } else if let tool @ Some(_) = ToolModule::by_name(&name_ref.text()) {
+                        tool.map(PathResolution::ToolModule)
                     } else {
                         None
                     }
@@ -362,8 +362,8 @@ impl SourceAnalyzer {
             None if is_path_of_attr => path
                 .first_segment()
                 .and_then(|seg| seg.name_ref())
-                .and_then(|name_ref| Tool::by_name(&name_ref.text()))
-                .map(PathResolution::Tool),
+                .and_then(|name_ref| ToolModule::by_name(&name_ref.text()))
+                .map(PathResolution::ToolModule),
             None => None,
         }
     }
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs
index edcea7aace7..617f63d3582 100644
--- a/crates/ide/src/doc_links.rs
+++ b/crates/ide/src/doc_links.rs
@@ -182,7 +182,7 @@ pub(crate) fn resolve_doc_path_for_def(
         Definition::Macro(it) => it.resolve_doc_path(db, link, ns),
         Definition::Field(it) => it.resolve_doc_path(db, link, ns),
         Definition::BuiltinAttr(_)
-        | Definition::Tool(_)
+        | Definition::ToolModule(_)
         | Definition::BuiltinType(_)
         | Definition::SelfType(_)
         | Definition::Local(_)
@@ -498,7 +498,7 @@ fn filename_and_frag_for_def(
         | Definition::GenericParam(_)
         | Definition::Label(_)
         | Definition::BuiltinAttr(_)
-        | Definition::Tool(_) => return None,
+        | Definition::ToolModule(_) => return None,
     };
 
     Some((def, res, None))
diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs
index 46f5b2e35be..393bb253b61 100644
--- a/crates/ide/src/hover/render.rs
+++ b/crates/ide/src/hover/render.rs
@@ -371,7 +371,7 @@ pub(super) fn definition(
         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::Tool(it) => return Some(Markup::fenced_block(&it.name(db))),
+        Definition::ToolModule(it) => return Some(Markup::fenced_block(&it.name(db))),
     };
 
     markup(docs.filter(|_| config.documentation.is_some()).map(Into::into), label, mod_path)
diff --git a/crates/ide/src/navigation_target.rs b/crates/ide/src/navigation_target.rs
index aa865ddc21d..49ff3625caa 100644
--- a/crates/ide/src/navigation_target.rs
+++ b/crates/ide/src/navigation_target.rs
@@ -214,7 +214,7 @@ impl TryToNav for Definition {
             Definition::Trait(it) => it.try_to_nav(db),
             Definition::TypeAlias(it) => it.try_to_nav(db),
             Definition::BuiltinType(_) => None,
-            Definition::Tool(_) => None,
+            Definition::ToolModule(_) => None,
             Definition::BuiltinAttr(_) => None,
         }
     }
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index 118f8dd9be0..56e07da0b90 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -115,7 +115,7 @@ pub struct HlRange {
 // parameter:: Emitted for non-self function parameters.
 // property:: Emitted for struct and union fields.
 // selfKeyword:: Emitted for the self function parameter and self path-specifier.
-// tool:: Emitted for tool modules.
+// toolModule:: Emitted for tool modules.
 // typeParameter:: Emitted for type parameters.
 // unresolvedReference:: Emitted for unresolved references, names that rust-analyzer can't find the definition of.
 // variable:: Emitted for locals, constants and statics.
diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs
index 340168a90d0..34c4ccd4c76 100644
--- a/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/crates/ide/src/syntax_highlighting/highlight.rs
@@ -513,7 +513,7 @@ fn highlight_def(
         }
         Definition::Label(_) => Highlight::new(HlTag::Symbol(SymbolKind::Label)),
         Definition::BuiltinAttr(_) => Highlight::new(HlTag::Symbol(SymbolKind::BuiltinAttr)),
-        Definition::Tool(_) => Highlight::new(HlTag::Symbol(SymbolKind::Tool)),
+        Definition::ToolModule(_) => Highlight::new(HlTag::Symbol(SymbolKind::ToolModule)),
     };
 
     let famous_defs = FamousDefs(sema, krate);
diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs
index 50f74cdec1b..a3b05ee2633 100644
--- a/crates/ide/src/syntax_highlighting/inject.rs
+++ b/crates/ide/src/syntax_highlighting/inject.rs
@@ -264,7 +264,7 @@ fn module_def_to_hl_tag(def: Definition) -> HlTag {
         },
         Definition::Label(_) => SymbolKind::Label,
         Definition::BuiltinAttr(_) => SymbolKind::BuiltinAttr,
-        Definition::Tool(_) => SymbolKind::Tool,
+        Definition::ToolModule(_) => SymbolKind::ToolModule,
     };
     HlTag::Symbol(symbol)
 }
diff --git a/crates/ide/src/syntax_highlighting/tags.rs b/crates/ide/src/syntax_highlighting/tags.rs
index a19eee58172..3e0a78392a3 100644
--- a/crates/ide/src/syntax_highlighting/tags.rs
+++ b/crates/ide/src/syntax_highlighting/tags.rs
@@ -139,7 +139,7 @@ impl HlTag {
                 SymbolKind::SelfParam => "self_keyword",
                 SymbolKind::Static => "static",
                 SymbolKind::Struct => "struct",
-                SymbolKind::Tool => "tool",
+                SymbolKind::ToolModule => "tool_module",
                 SymbolKind::Trait => "trait",
                 SymbolKind::TypeAlias => "type_alias",
                 SymbolKind::TypeParam => "type_param",
diff --git a/crates/ide_completion/src/item.rs b/crates/ide_completion/src/item.rs
index e80a8edd9cf..b3f404d9053 100644
--- a/crates/ide_completion/src/item.rs
+++ b/crates/ide_completion/src/item.rs
@@ -247,7 +247,7 @@ impl CompletionItemKind {
                 SymbolKind::SelfParam => "sp",
                 SymbolKind::Static => "sc",
                 SymbolKind::Struct => "st",
-                SymbolKind::Tool => "tl",
+                SymbolKind::ToolModule => "tm",
                 SymbolKind::Trait => "tt",
                 SymbolKind::TypeAlias => "ta",
                 SymbolKind::TypeParam => "tp",
diff --git a/crates/ide_db/src/defs.rs b/crates/ide_db/src/defs.rs
index 26cdaa1ac14..ef2f0f940f5 100644
--- a/crates/ide_db/src/defs.rs
+++ b/crates/ide_db/src/defs.rs
@@ -9,7 +9,7 @@ use arrayvec::ArrayVec;
 use hir::{
     Adt, AsAssocItem, AssocItem, BuiltinAttr, BuiltinType, Const, Field, Function, GenericParam,
     HasVisibility, Impl, ItemInNs, Label, Local, MacroDef, Module, ModuleDef, Name, PathResolution,
-    Semantics, Static, Tool, Trait, TypeAlias, Variant, Visibility,
+    Semantics, Static, ToolModule, Trait, TypeAlias, Variant, Visibility,
 };
 use stdx::impl_from;
 use syntax::{
@@ -38,7 +38,7 @@ pub enum Definition {
     GenericParam(GenericParam),
     Label(Label),
     BuiltinAttr(BuiltinAttr),
-    Tool(Tool),
+    ToolModule(ToolModule),
 }
 
 impl Definition {
@@ -129,7 +129,7 @@ impl Definition {
             Definition::Local(it) => it.module(db),
             Definition::GenericParam(it) => it.module(db),
             Definition::Label(it) => it.module(db),
-            Definition::BuiltinAttr(_) | Definition::BuiltinType(_) | Definition::Tool(_) => {
+            Definition::BuiltinAttr(_) | Definition::BuiltinType(_) | Definition::ToolModule(_) => {
                 return None
             }
         };
@@ -150,7 +150,7 @@ impl Definition {
             Definition::BuiltinType(_) => Visibility::Public,
             Definition::Macro(_) => return None,
             Definition::BuiltinAttr(_)
-            | Definition::Tool(_)
+            | Definition::ToolModule(_)
             | Definition::SelfType(_)
             | Definition::Local(_)
             | Definition::GenericParam(_)
@@ -177,7 +177,7 @@ impl Definition {
             Definition::GenericParam(it) => it.name(db),
             Definition::Label(it) => it.name(db),
             Definition::BuiltinAttr(_) => return None, // FIXME
-            Definition::Tool(_) => return None,        // FIXME
+            Definition::ToolModule(_) => return None,  // FIXME
         };
         Some(name)
     }
@@ -551,7 +551,7 @@ impl From for Definition {
             PathResolution::SelfType(impl_def) => Definition::SelfType(impl_def),
             PathResolution::ConstParam(par) => Definition::GenericParam(par.into()),
             PathResolution::BuiltinAttr(attr) => Definition::BuiltinAttr(attr),
-            PathResolution::Tool(tool) => Definition::Tool(tool),
+            PathResolution::ToolModule(tool) => Definition::ToolModule(tool),
         }
     }
 }
diff --git a/crates/ide_db/src/lib.rs b/crates/ide_db/src/lib.rs
index 6eaea53f5c1..67f23536bd8 100644
--- a/crates/ide_db/src/lib.rs
+++ b/crates/ide_db/src/lib.rs
@@ -160,7 +160,7 @@ pub enum SymbolKind {
     SelfParam,
     Static,
     Struct,
-    Tool,
+    ToolModule,
     Trait,
     TypeAlias,
     TypeParam,
diff --git a/crates/ide_db/src/path_transform.rs b/crates/ide_db/src/path_transform.rs
index a4db8b3848b..1b4f793a507 100644
--- a/crates/ide_db/src/path_transform.rs
+++ b/crates/ide_db/src/path_transform.rs
@@ -166,7 +166,7 @@ impl<'a> Ctx<'a> {
             | hir::PathResolution::Macro(_)
             | hir::PathResolution::AssocItem(_)
             | hir::PathResolution::BuiltinAttr(_)
-            | hir::PathResolution::Tool(_) => (),
+            | hir::PathResolution::ToolModule(_) => (),
         }
         Some(())
     }
diff --git a/crates/ide_db/src/rename.rs b/crates/ide_db/src/rename.rs
index 35bd4d02a63..60160f9553e 100644
--- a/crates/ide_db/src/rename.rs
+++ b/crates/ide_db/src/rename.rs
@@ -147,7 +147,7 @@ impl Definition {
             Definition::BuiltinType(_) => return None,
             Definition::SelfType(_) => return None,
             Definition::BuiltinAttr(_) => return None,
-            Definition::Tool(_) => return None,
+            Definition::ToolModule(_) => return None,
         };
         return res;
 
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs
index 9d19482bee3..8e1ccfb397b 100644
--- a/crates/rust-analyzer/src/semantic_tokens.rs
+++ b/crates/rust-analyzer/src/semantic_tokens.rs
@@ -65,7 +65,7 @@ define_semantic_token_types![
     (SELF_KEYWORD, "selfKeyword"),
     (SEMICOLON, "semicolon"),
     (TYPE_ALIAS, "typeAlias"),
-    (TOOL, "tool"),
+    (TOOL_MODULE, "toolModule"),
     (UNION, "union"),
     (UNRESOLVED_REFERENCE, "unresolvedReference"),
 ];
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 1dd4dabec4d..579ad22baca 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -51,7 +51,7 @@ pub(crate) fn symbol_kind(symbol_kind: SymbolKind) -> lsp_types::SymbolKind {
         SymbolKind::Variant => lsp_types::SymbolKind::ENUM_MEMBER,
         SymbolKind::Trait => lsp_types::SymbolKind::INTERFACE,
         SymbolKind::Macro | SymbolKind::BuiltinAttr => lsp_types::SymbolKind::FUNCTION,
-        SymbolKind::Module | SymbolKind::Tool => lsp_types::SymbolKind::MODULE,
+        SymbolKind::Module | SymbolKind::ToolModule => lsp_types::SymbolKind::MODULE,
         SymbolKind::TypeAlias | SymbolKind::TypeParam => lsp_types::SymbolKind::TYPE_PARAMETER,
         SymbolKind::Field => lsp_types::SymbolKind::FIELD,
         SymbolKind::Static => lsp_types::SymbolKind::CONSTANT,
@@ -129,7 +129,7 @@ pub(crate) fn completion_item_kind(
             SymbolKind::ValueParam => lsp_types::CompletionItemKind::VALUE,
             SymbolKind::Variant => lsp_types::CompletionItemKind::ENUM_MEMBER,
             SymbolKind::BuiltinAttr => lsp_types::CompletionItemKind::FUNCTION,
-            SymbolKind::Tool => lsp_types::CompletionItemKind::MODULE,
+            SymbolKind::ToolModule => lsp_types::CompletionItemKind::MODULE,
         },
     }
 }
@@ -502,7 +502,7 @@ fn semantic_token_type_and_modifiers(
             SymbolKind::Trait => lsp_types::SemanticTokenType::INTERFACE,
             SymbolKind::Macro => lsp_types::SemanticTokenType::MACRO,
             SymbolKind::BuiltinAttr => semantic_tokens::BUILTIN_ATTRIBUTE,
-            SymbolKind::Tool => semantic_tokens::TOOL,
+            SymbolKind::ToolModule => semantic_tokens::TOOL_MODULE,
         },
         HlTag::Attribute => semantic_tokens::ATTRIBUTE,
         HlTag::BoolLiteral => semantic_tokens::BOOLEAN,

From 8da850b6d5cdb9896ff936170ccc6a6891ca067d Mon Sep 17 00:00:00 2001
From: Lukas Wirth 
Date: Fri, 3 Dec 2021 20:28:15 +0100
Subject: [PATCH 6/6] 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 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> = 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 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 {
+    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 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 = "...")\]
+        "##]],
+    );
+}