about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crates/hir/src/lib.rs35
-rw-r--r--crates/hir/src/semantics.rs26
-rw-r--r--crates/hir/src/source_analyzer.rs72
-rw-r--r--crates/hir_def/src/builtin_attr.rs106
-rw-r--r--crates/hir_def/src/nameres/collector.rs8
-rw-r--r--crates/ide/src/doc_links.rs12
-rw-r--r--crates/ide/src/hover/render.rs23
-rw-r--r--crates/ide/src/hover/tests.rs44
-rw-r--r--crates/ide/src/navigation_target.rs2
-rw-r--r--crates/ide/src/static_index.rs1
-rw-r--r--crates/ide/src/syntax_highlighting.rs1
-rw-r--r--crates/ide/src/syntax_highlighting/highlight.rs48
-rw-r--r--crates/ide/src/syntax_highlighting/inject.rs2
-rw-r--r--crates/ide/src/syntax_highlighting/tags.rs28
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html10
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_strings.html20
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html2
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlighting.html11
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs1
-rw-r--r--crates/ide_completion/src/item.rs2
-rw-r--r--crates/ide_db/src/defs.rs50
-rw-r--r--crates/ide_db/src/lib.rs2
-rw-r--r--crates/ide_db/src/path_transform.rs4
-rw-r--r--crates/ide_db/src/rename.rs6
-rw-r--r--crates/rust-analyzer/src/semantic_tokens.rs1
-rw-r--r--crates/rust-analyzer/src/to_proto.rs9
26 files changed, 350 insertions, 176 deletions
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index e0dc921c9f0..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,
@@ -2024,6 +2025,40 @@ impl Local {
 }
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+pub struct BuiltinAttr(usize);
+
+impl BuiltinAttr {
+    pub(crate) fn by_name(name: &str) -> Option<Self> {
+        // 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
+    }
+
+    pub fn template(&self, _: &dyn HirDatabase) -> AttributeTemplate {
+        hir_def::builtin_attr::INERT_ATTRIBUTES[self.0].template
+    }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+pub struct ToolModule(usize);
+
+impl ToolModule {
+    pub(crate) fn by_name(name: &str) -> Option<Self> {
+        // 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)]
 pub struct Label {
     pub(crate) parent: DefWithBodyId,
     pub(crate) label_id: LabelId,
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 27ba42ce1af..ed1b2f64fd1 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, ToolModule, 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),
+    ToolModule(ToolModule),
 }
 
 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::ToolModule(_)
+            | 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<MacroDef> {
-        self.imp.resolve_path_as_macro(path)
-    }
-
     pub fn resolve_extern_crate(&self, extern_crate: &ast::ExternCrate) -> Option<Crate> {
         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<MacroDef> {
-        self.analyze(path.syntax()).resolve_path_as_macro(self.db, path)
-    }
-
     fn resolve_extern_crate(&self, extern_crate: &ast::ExternCrate) -> Option<Crate> {
         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..762f172c2af 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, ToolModule, Trait, Type, TypeAlias,
+    TypeParam, Variant,
 };
 use base_db::CrateId;
 
@@ -246,24 +247,14 @@ impl SourceAnalyzer {
         }
     }
 
-    pub(crate) fn resolve_path_as_macro(
-        &self,
-        db: &dyn HirDatabase,
-        path: &ast::Path,
-    ) -> Option<MacroDef> {
-        // 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,
         path: &ast::Path,
     ) -> Option<PathResolution> {
-        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())?;
@@ -318,29 +309,62 @@ 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
+        // 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(outer_path) = parent().and_then(ast::Path::cast) {
-            if let Some(qualifier) = outer_path.qualifier() {
-                if path == &qualifier {
+        if let Some(use_tree) = parent().and_then(ast::UseTree::cast) {
+            if let Some(qualifier) = use_tree.path() {
+                if path == &qualifier && use_tree.coloncolon_token().is_some() {
                     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
+
+        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(use_tree) = parent().and_then(ast::UseTree::cast) {
-            if let Some(qualifier) = use_tree.path() {
-                if path == &qualifier && use_tree.coloncolon_token().is_some() {
+        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 builtin @ Some(_) = BuiltinAttr::by_name(&name_ref.text()) {
+                        builtin.map(PathResolution::BuiltinAttr)
+                    } else if let tool @ Some(_) = ToolModule::by_name(&name_ref.text()) {
+                        tool.map(PathResolution::ToolModule)
+                    } else {
+                        None
+                    }
+                }),
+            };
         }
 
-        if parent().map_or(false, |it| ast::Visibility::can_cast(it.kind())) {
+        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,
+            // 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())
+                .and_then(|name_ref| ToolModule::by_name(&name_ref.text()))
+                .map(PathResolution::ToolModule),
+            None => None,
         }
     }
 
diff --git a/crates/hir_def/src/builtin_attr.rs b/crates/hir_def/src/builtin_attr.rs
index 6cd185ceeb2..3f43111fb1d 100644
--- a/crates/hir_def/src/builtin_attr.rs
+++ b/crates/hir_def/src/builtin_attr.rs
@@ -2,35 +2,100 @@
 //!
 //! 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.
+#[derive(Clone, Copy)]
+pub struct AttributeTemplate {
+    pub word: bool,
+    pub list: Option<&'static str>,
+    pub name_value_str: Option<&'static str>,
+}
+
+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()
+        })
+        .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 +223,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 +260,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:
@@ -259,10 +330,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 \
         which contains compiler-rt intrinsics and will never be stable",
@@ -287,7 +354,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 +415,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 +499,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..617f63d3582 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::ToolModule(_)
+        | 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::ToolModule(_) => return None,
     };
 
     Some((def, res, None))
diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs
index dd4a961d31c..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,
@@ -369,11 +369,32 @@ 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 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 5718b9097c4..f1d7d2791d8 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
@@ -4278,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 = "...")\]
+        "##]],
+    );
+}
diff --git a/crates/ide/src/navigation_target.rs b/crates/ide/src/navigation_target.rs
index 7deb6cae38b..49ff3625caa 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::ToolModule(_) => 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.rs b/crates/ide/src/syntax_highlighting.rs
index 8b613fde54d..56e07da0b90 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.
+// 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 b8177b4ab6c..34c4ccd4c76 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<RootDatabase>, 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<RootDatabase>,
     krate: Option<hir::Crate>,
@@ -542,6 +512,8 @@ fn highlight_def(
             h
         }
         Definition::Label(_) => Highlight::new(HlTag::Symbol(SymbolKind::Label)),
+        Definition::BuiltinAttr(_) => Highlight::new(HlTag::Symbol(SymbolKind::BuiltinAttr)),
+        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 686fd5baa72..a3b05ee2633 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::BuiltinAttr,
+        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 92c7fcab76f..3e0a78392a3 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::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::ToolModule => "tool_module",
                 SymbolKind::Trait => "trait",
-                SymbolKind::Function => "function",
                 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
     <span class="comment documentation">/// # Examples</span>
     <span class="comment documentation">///</span>
     <span class="comment documentation">/// ```</span>
-    <span class="comment documentation">/// #</span><span class="none injected"> </span><span class="attribute attribute injected">#</span><span class="attribute attribute injected">!</span><span class="attribute attribute injected">[</span><span class="builtin_attr attribute injected">allow</span><span class="parenthesis attribute injected">(</span><span class="none attribute injected">unused_mut</span><span class="parenthesis attribute injected">)</span><span class="attribute attribute injected">]</span>
+    <span class="comment documentation">/// #</span><span class="none injected"> </span><span class="attribute attribute injected">#</span><span class="attribute attribute injected">!</span><span class="attribute attribute injected">[</span><span class="builtin_attr attribute injected library">allow</span><span class="parenthesis attribute injected">(</span><span class="none attribute injected">unused_mut</span><span class="parenthesis attribute injected">)</span><span class="attribute attribute injected">]</span>
     <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="keyword injected">mut</span><span class="none injected"> </span><span class="variable declaration injected mutable">foo</span><span class="colon injected">:</span><span class="none injected"> </span><span class="struct injected">Foo</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span>
     <span class="comment documentation">/// ```</span>
     <span class="keyword">pub</span> <span class="keyword">const</span> <span class="keyword">fn</span> <span class="function associated declaration public static">new</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="struct">Foo</span> <span class="brace">{</span>
@@ -143,12 +143,12 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 <span class="comment documentation">///</span>
 <span class="comment documentation">/// ```</span>
 <span class="comment documentation">/// </span><span class="keyword control injected">loop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="brace injected">}</span>
-<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute">cfg_attr</span><span class="parenthesis attribute">(</span><span class="none attribute">not</span><span class="parenthesis attribute">(</span><span class="none attribute">feature</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"false"</span><span class="parenthesis attribute">)</span><span class="comma attribute">,</span><span class="attribute attribute"> </span><span class="none attribute">doc</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"</span><span class="keyword control injected">loop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="brace injected">}</span><span class="string_literal attribute">"</span><span class="parenthesis attribute">)</span><span class="attribute attribute">]</span>
-<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute">doc</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"</span><span class="keyword control injected">loop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="brace injected">}</span><span class="string_literal attribute">"</span><span class="attribute attribute">]</span>
+<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute library">cfg_attr</span><span class="parenthesis attribute">(</span><span class="none attribute">not</span><span class="parenthesis attribute">(</span><span class="none attribute">feature</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"false"</span><span class="parenthesis attribute">)</span><span class="comma attribute">,</span><span class="attribute attribute"> </span><span class="none attribute">doc</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"</span><span class="keyword control injected">loop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="brace injected">}</span><span class="string_literal attribute">"</span><span class="parenthesis attribute">)</span><span class="attribute attribute">]</span>
+<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute library">doc</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"</span><span class="keyword control injected">loop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="brace injected">}</span><span class="string_literal attribute">"</span><span class="attribute attribute">]</span>
 <span class="comment documentation">/// ```</span>
 <span class="comment documentation">///</span>
-<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute">cfg_attr</span><span class="parenthesis attribute">(</span><span class="none attribute">feature</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"alloc"</span><span class="comma attribute">,</span><span class="attribute attribute"> </span><span class="none attribute">doc</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"```rust"</span><span class="parenthesis attribute">)</span><span class="attribute attribute">]</span>
-<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute">cfg_attr</span><span class="parenthesis attribute">(</span><span class="none attribute">not</span><span class="parenthesis attribute">(</span><span class="none attribute">feature</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"alloc"</span><span class="parenthesis attribute">)</span><span class="comma attribute">,</span><span class="attribute attribute"> </span><span class="none attribute">doc</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"```ignore"</span><span class="parenthesis attribute">)</span><span class="attribute attribute">]</span>
+<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute library">cfg_attr</span><span class="parenthesis attribute">(</span><span class="none attribute">feature</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"alloc"</span><span class="comma attribute">,</span><span class="attribute attribute"> </span><span class="none attribute">doc</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"```rust"</span><span class="parenthesis attribute">)</span><span class="attribute attribute">]</span>
+<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute library">cfg_attr</span><span class="parenthesis attribute">(</span><span class="none attribute">not</span><span class="parenthesis attribute">(</span><span class="none attribute">feature</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"alloc"</span><span class="parenthesis attribute">)</span><span class="comma attribute">,</span><span class="attribute attribute"> </span><span class="none attribute">doc</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"```ignore"</span><span class="parenthesis attribute">)</span><span class="attribute attribute">]</span>
 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="function injected">example</span><span class="parenthesis injected">(</span><span class="operator injected">&</span><span class="none injected">alloc::</span><span class="macro injected">vec!</span><span class="bracket injected">[</span><span class="numeric_literal injected">1</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">2</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">3</span><span class="bracket injected">]</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span>
 <span class="comment documentation">/// ```</span>
 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration public">mix_and_match</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
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
         <span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>io<span class="colon">:</span><span class="colon">:</span>_print<span class="parenthesis">(</span><span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>format_args_nl<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span>
     <span class="brace">}</span><span class="parenthesis">)</span>
 <span class="brace">}</span>
-<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute">rustc_builtin_macro</span><span class="attribute attribute">]</span>
-<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute">macro_export</span><span class="attribute attribute">]</span>
+<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute library">rustc_builtin_macro</span><span class="attribute attribute">]</span>
+<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute library">macro_export</span><span class="attribute attribute">]</span>
 <span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">format_args</span> <span class="brace">{</span><span class="brace">}</span>
-<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute">rustc_builtin_macro</span><span class="attribute attribute">]</span>
-<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute">macro_export</span><span class="attribute attribute">]</span>
+<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute library">rustc_builtin_macro</span><span class="attribute attribute">]</span>
+<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute library">macro_export</span><span class="attribute attribute">]</span>
 <span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">const_format_args</span> <span class="brace">{</span><span class="brace">}</span>
-<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute">rustc_builtin_macro</span><span class="attribute attribute">]</span>
-<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute">macro_export</span><span class="attribute attribute">]</span>
+<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute library">rustc_builtin_macro</span><span class="attribute attribute">]</span>
+<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute library">macro_export</span><span class="attribute attribute">]</span>
 <span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">format_args_nl</span> <span class="brace">{</span><span class="brace">}</span>
 
 <span class="keyword">mod</span> <span class="module declaration">panic</span> <span class="brace">{</span>
@@ -77,12 +77,12 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="brace">}</span>
 <span class="brace">}</span>
 
-<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute">rustc_builtin_macro</span><span class="parenthesis attribute">(</span><span class="none attribute">std_panic</span><span class="parenthesis attribute">)</span><span class="attribute attribute">]</span>
-<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute">macro_export</span><span class="attribute attribute">]</span>
+<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute library">rustc_builtin_macro</span><span class="parenthesis attribute">(</span><span class="none attribute">std_panic</span><span class="parenthesis attribute">)</span><span class="attribute attribute">]</span>
+<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute library">macro_export</span><span class="attribute attribute">]</span>
 <span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">panic</span> <span class="brace">{</span><span class="brace">}</span>
-<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute">rustc_builtin_macro</span><span class="attribute attribute">]</span>
+<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute library">rustc_builtin_macro</span><span class="attribute attribute">]</span>
 <span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">assert</span> <span class="brace">{</span><span class="brace">}</span>
-<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute">rustc_builtin_macro</span><span class="attribute attribute">]</span>
+<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute library">rustc_builtin_macro</span><span class="attribute attribute">]</span>
 <span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">asm</span> <span class="brace">{</span><span class="brace">}</span>
 
 <span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">toho</span> <span class="brace">{</span>
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
     <span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function associated declaration reference unsafe">unsafe_method</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration reference">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
 <span class="brace">}</span>
 
-<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute">repr</span><span class="parenthesis attribute">(</span><span class="none attribute">packed</span><span class="parenthesis attribute">)</span><span class="attribute attribute">]</span>
+<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute library">repr</span><span class="parenthesis attribute">(</span><span class="none attribute">packed</span><span class="parenthesis attribute">)</span><span class="attribute attribute">]</span>
 <span class="keyword">struct</span> <span class="struct declaration">Packed</span> <span class="brace">{</span>
     <span class="field declaration">a</span><span class="colon">:</span> <span class="builtin_type">u16</span><span class="comma">,</span>
 <span class="brace">}</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
index d8c9827b5e0..370f58080e6 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
@@ -43,15 +43,16 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 <pre><code><span class="keyword">use</span> <span class="module">inner</span><span class="operator">::</span><span class="brace">{</span><span class="self_keyword">self</span> <span class="keyword">as</span> <span class="module declaration">inner_mod</span><span class="brace">}</span><span class="semicolon">;</span>
 <span class="keyword">mod</span> <span class="module declaration">inner</span> <span class="brace">{</span><span class="brace">}</span>
 
-<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="module attribute">proc_macros</span><span class="operator attribute">::</span><span class="macro attribute">identity</span><span class="attribute attribute">]</span>
+<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute library">allow</span><span class="parenthesis attribute">(</span><span class="parenthesis attribute">)</span><span class="attribute attribute">]</span>
+<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="module attribute crate_root library">proc_macros</span><span class="operator attribute">::</span><span class="macro attribute library">identity</span><span class="attribute attribute">]</span>
 <span class="keyword">pub</span> <span class="keyword">mod</span> <span class="module declaration public">ops</span> <span class="brace">{</span>
-    <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute">lang</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"fn_once"</span><span class="attribute attribute">]</span>
+    <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute library">lang</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"fn_once"</span><span class="attribute attribute">]</span>
     <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration public">FnOnce</span><span class="angle">&lt;</span><span class="type_param declaration">Args</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="brace">}</span>
 
-    <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute">lang</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"fn_mut"</span><span class="attribute attribute">]</span>
+    <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute library">lang</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"fn_mut"</span><span class="attribute attribute">]</span>
     <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration public">FnMut</span><span class="angle">&lt;</span><span class="type_param declaration">Args</span><span class="angle">&gt;</span><span class="colon">:</span> <span class="trait public">FnOnce</span><span class="angle">&lt;</span><span class="type_param">Args</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="brace">}</span>
 
-    <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute">lang</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"fn"</span><span class="attribute attribute">]</span>
+    <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute library">lang</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"fn"</span><span class="attribute attribute">]</span>
     <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration public">Fn</span><span class="angle">&lt;</span><span class="type_param declaration">Args</span><span class="angle">&gt;</span><span class="colon">:</span> <span class="trait public">FnMut</span><span class="angle">&lt;</span><span class="type_param">Args</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="brace">}</span>
 <span class="brace">}</span>
 
@@ -86,7 +87,7 @@ proc_macros::<span class="macro">mirror!</span> <span class="brace">{</span>
     <span class="brace">}</span>
 <span class="brace">}</span>
 
-<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="macro attribute">derive</span><span class="parenthesis attribute">(</span><span class="macro attribute default_library library">Copy</span><span class="parenthesis attribute">)</span><span class="attribute attribute">]</span>
+<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="macro attribute default_library library">derive</span><span class="parenthesis attribute">(</span><span class="macro attribute default_library library">Copy</span><span class="parenthesis attribute">)</span><span class="attribute attribute">]</span>
 <span class="keyword">struct</span> <span class="struct declaration">FooCopy</span> <span class="brace">{</span>
     <span class="field declaration">x</span><span class="colon">:</span> <span class="builtin_type">u32</span><span class="comma">,</span>
 <span class="brace">}</span>
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"]
diff --git a/crates/ide_completion/src/item.rs b/crates/ide_completion/src/item.rs
index ebe227d260a..b3f404d9053 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::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 1d7f4392dd0..ef2f0f940f5 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, ToolModule, 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),
+    ToolModule(ToolModule),
 }
 
 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::ToolModule(_) => {
+                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::ToolModule(_)
+            | 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::ToolModule(_) => 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<PathResolution> 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::ToolModule(tool) => Definition::ToolModule(tool),
         }
     }
 }
diff --git a/crates/ide_db/src/lib.rs b/crates/ide_db/src/lib.rs
index 1250008984e..67f23536bd8 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<LineIndex> {
 
 #[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,
+    ToolModule,
     Trait,
     TypeAlias,
     TypeParam,
diff --git a/crates/ide_db/src/path_transform.rs b/crates/ide_db/src/path_transform.rs
index b8b1307a3c0..1b4f793a507 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::ToolModule(_) => (),
         }
         Some(())
     }
diff --git a/crates/ide_db/src/rename.rs b/crates/ide_db/src/rename.rs
index 678153c6e1d..60160f9553e 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::ToolModule(_) => return None,
         };
         return res;
 
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs
index 3117b757285..8e1ccfb397b 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_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 8e77df95982..579ad22baca 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::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,
@@ -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::ToolModule => 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::ToolModule => semantic_tokens::TOOL_MODULE,
         },
         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,