Repository: rust-lang/rust-analyzer
Pull request #16808 (2024-03-11 12:22:02)

feat: Add proc macro semantic token type
Closes  #11529


Patch:
diff --git a/crates/hir-def/src/body.rs b/crates/hir-def/src/body.rs
index c9f1add275..37d37fd331 100644
--- a/crates/hir-def/src/body.rs
+++ b/crates/hir-def/src/body.rs
@@ -10,6 +10,7 @@ use std::ops::Index;
 
 use base_db::CrateId;
 use cfg::{CfgExpr, CfgOptions};
+use either::Either;
 use hir_expand::{name::Name, HirFileId, InFile};
 use la_arena::{Arena, ArenaMap};
 use rustc_hash::FxHashMap;
@@ -44,8 +45,7 @@ pub struct Body {
     ///
     /// If this `Body` is for the body of a constant, this will just be
     /// empty.
-    pub params: Box<[PatId]>,
-    pub self_param: Option<BindingId>,
+    pub params: Vec<PatId>,
     /// The `ExprId` of the actual body expression.
     pub body_expr: ExprId,
     /// Block expressions in this body that may contain inner items.
@@ -55,7 +55,7 @@ pub struct Body {
 pub type ExprPtr = AstPtr<ast::Expr>;
 pub type ExprSource = InFile<ExprPtr>;
 
-pub type PatPtr = AstPtr<ast::Pat>;
+pub type PatPtr = AstPtr<Either<ast::Pat, ast::SelfParam>>;
 pub type PatSource = InFile<PatPtr>;
 
 pub type LabelPtr = AstPtr<ast::Label>;
@@ -63,7 +63,6 @@ pub type LabelSource = InFile<LabelPtr>;
 
 pub type FieldPtr = AstPtr<ast::RecordExprField>;
 pub type FieldSource = InFile<FieldPtr>;
-
 pub type PatFieldPtr = AstPtr<ast::RecordPatField>;
 pub type PatFieldSource = InFile<PatFieldPtr>;
 
@@ -89,8 +88,6 @@ pub struct BodySourceMap {
     label_map: FxHashMap<LabelSource, LabelId>,
     label_map_back: ArenaMap<LabelId, LabelSource>,
 
-    self_param: Option<InFile<AstPtr<ast::SelfParam>>>,
-
     /// We don't create explicit nodes for record fields (`S { record_field: 92 }`).
     /// Instead, we use id of expression (`92`) to identify the field.
     field_map_back: FxHashMap<ExprId, FieldSource>,
@@ -218,11 +215,10 @@ impl Body {
     fn shrink_to_fit(&mut self) {
         let Self {
             body_expr: _,
-            params: _,
-            self_param: _,
             block_scopes,
             exprs,
             labels,
+            params,
             pats,
             bindings,
             binding_owners,
@@ -230,6 +226,7 @@ impl Body {
         block_scopes.shrink_to_fit();
         exprs.shrink_to_fit();
         labels.shrink_to_fit();
+        params.shrink_to_fit();
         pats.shrink_to_fit();
         bindings.shrink_to_fit();
         binding_owners.shrink_to_fit();
@@ -300,7 +297,6 @@ impl Default for Body {
             params: Default::default(),
             block_scopes: Default::default(),
             binding_owners: Default::default(),
-            self_param: Default::default(),
         }
     }
 }
@@ -358,12 +354,14 @@ impl BodySourceMap {
         self.pat_map_back.get(pat).cloned().ok_or(SyntheticSyntax)
     }
 
-    pub fn self_param_syntax(&self) -> Option<InFile<AstPtr<ast::SelfParam>>> {
-        self.self_param
+    pub fn node_pat(&self, node: InFile<&ast::Pat>) -> Option<PatId> {
+        let src = node.map(|it| AstPtr::new(it).wrap_left());
+        self.pat_map.get(&src).cloned()
     }
 
-    pub fn node_pat(&self, node: InFile<&ast::Pat>) -> Option<PatId> {
-        self.pat_map.get(&node.map(AstPtr::new)).cloned()
+    pub fn node_self_param(&self, node: InFile<&ast::SelfParam>) -> Option<PatId> {
+        let src = node.map(|it| AstPtr::new(it).wrap_right());
+        self.pat_map.get(&src).cloned()
     }
 
     pub fn label_syntax(&self, label: LabelId) -> LabelSource {
@@ -403,7 +401,6 @@ impl BodySourceMap {
 
     fn shrink_to_fit(&mut self) {
         let Self {
-            self_param: _,
             expr_map,
             expr_map_back,
             pat_map,
diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs
index 340e95dbc2..6669127789 100644
--- a/crates/hir-def/src/body/lower.rs
+++ b/crates/hir-def/src/body/lower.rs
@@ -4,6 +4,7 @@
 use std::mem;
 
 use base_db::CrateId;
+use either::Either;
 use hir_expand::{
     name::{name, AsName, Name},
     ExpandError, InFile,
@@ -28,6 +29,7 @@ use crate::{
     db::DefDatabase,
     expander::Expander,
     hir::{
+        dummy_expr_id,
         format_args::{
             self, FormatAlignment, FormatArgs, FormatArgsPiece, FormatArgument, FormatArgumentKind,
             FormatArgumentsCollector, FormatCount, FormatDebugHex, FormatOptions,
@@ -64,7 +66,16 @@ pub(super) fn lower(
         def_map: expander.module.def_map(db),
         source_map: BodySourceMap::default(),
         ast_id_map: db.ast_id_map(expander.current_file_id()),
-        body: Body::default(),
+        body: Body {
+            exprs: Default::default(),
+            pats: Default::default(),
+            bindings: Default::default(),
+            binding_owners: Default::default(),
+            labels: Default::default(),
+            params: Vec::new(),
+            body_expr: dummy_expr_id(),
+            block_scopes: Vec::new(),
+        },
         expander,
         current_try_block_label: None,
         is_lowering_assignee_expr: false,
@@ -180,35 +191,35 @@ impl ExprCollector<'_> {
         is_async_fn: bool,
     ) -> (Body, BodySourceMap) {
         if let Some((param_list, mut attr_enabled)) = param_list {
-            let mut params = vec![];
             if let Some(self_param) =
                 param_list.self_param().filter(|_| attr_enabled.next().unwrap_or(false))
             {
                 let is_mutable =
                     self_param.mut_token().is_some() && self_param.amp_token().is_none();
+                let ptr = AstPtr::new(&Either::Right(self_param));
                 let binding_id: la_arena::Idx<Binding> =
                     self.alloc_binding(name![self], BindingAnnotation::new(is_mutable, false));
-                self.body.self_param = Some(binding_id);
-                self.source_map.self_param = Some(self.expander.in_file(AstPtr::new(&self_param)));
+                let param_pat = self.alloc_pat(Pat::Bind { id: binding_id, subpat: None }, ptr);
+                self.add_definition_to_binding(binding_id, param_pat);
+                self.body.params.push(param_pat);
             }
 
             for (param, _) in param_list.params().zip(attr_enabled).filter(|(_, enabled)| *enabled)
             {
                 let param_pat = self.collect_pat_top(param.pat());
-                params.push(param_pat);
+                self.body.params.push(param_pat);
             }
-            self.body.params = params.into_boxed_slice();
         };
         self.body.body_expr = self.with_label_rib(RibKind::Closure, |this| {
             if is_async_fn {
                 match body {
                     Some(e) => {
-                        let syntax_ptr = AstPtr::new(&e);
                         let expr = this.collect_expr(e);
-                        this.alloc_expr_desugared_with_ptr(
-                            Expr::Async { id: None, statements: Box::new([]), tail: Some(expr) },
-                            syntax_ptr,
-                        )
+                        this.alloc_expr_desugared(Expr::Async {
+                            id: None,
+                            statements: Box::new([]),
+                            tail: Some(expr),
+                        })
                     }
                     None => this.missing_expr(),
                 }
@@ -394,7 +405,7 @@ impl ExprCollector<'_> {
             }
             ast::Expr::ParenExpr(e) => {
                 let inner = self.collect_expr_opt(e.expr());
-                // make the paren expr point to the inner expression as well for IDE resolution
+                // make the paren expr point to the inner expression as well
                 let src = self.expander.in_file(syntax_ptr);
                 self.source_map.expr_map.insert(src, inner);
                 inner
@@ -696,7 +707,6 @@ impl ExprCollector<'_> {
             .alloc_label_desugared(Label { name: Name::generate_new_name(self.body.labels.len()) });
         let old_label = self.current_try_block_label.replace(label);
 
-        let ptr = AstPtr::new(&e).upcast();
         let (btail, expr_id) = self.with_labeled_rib(label, |this| {
             let mut btail = None;
             let block = this.collect_block_(e, |id, statements, tail| {
@@ -706,21 +716,23 @@ impl ExprCollector<'_> {
             (btail, block)
         });
 
-        let callee = self.alloc_expr_desugared_with_ptr(Expr::Path(try_from_output), ptr);
+        let callee = self.alloc_expr_desugared(Expr::Path(try_from_output));
         let next_tail = match btail {
-            Some(tail) => self.alloc_expr_desugared_with_ptr(
-                Expr::Call { callee, args: Box::new([tail]), is_assignee_expr: false },
-                ptr,
-            ),
+            Some(tail) => self.alloc_expr_desugared(Expr::Call {
+                callee,
+                args: Box::new([tail]),
+                is_assignee_expr: false,
+            }),
             None => {
-                let unit = self.alloc_expr_desugared_with_ptr(
-                    Expr::Tuple { exprs: Box::new([]), is_assignee_expr: false },
-                    ptr,
-                );
-                self.alloc_expr_desugared_with_ptr(
-                    Expr::Call { callee, args: Box::new([unit]), is_assignee_expr: false },
-                    ptr,
-                )
+                let unit = self.alloc_expr_desugared(Expr::Tuple {
+                    exprs: Box::new([]),
+                    is_assignee_expr: false,
+                });
+                self.alloc_expr_desugared(Expr::Call {
+                    callee,
+                    args: Box::new([unit]),
+                    is_assignee_expr: false,
+                })
             }
         };
         let Expr::Block { tail, .. } = &mut self.body.exprs[expr_id] else {
@@ -1055,12 +1067,16 @@ impl ExprCollector<'_> {
                 None => None,
             },
         );
-        expansion.inspect(|&tail| {
-            // Make the macro-call point to its expanded expression so we can query
-            // semantics on syntax pointers to the macro
-            let src = self.expander.in_file(syntax_ptr);
-            self.source_map.expr_map.insert(src, tail);
-        })
+        match expansion {
+            Some(tail) => {
+                // Make the macro-call point to its expanded expression so we can query
+                // semantics on syntax pointers to the macro
+                let src = self.expander.in_file(syntax_ptr);
+                self.source_map.expr_map.insert(src, tail);
+                Some(tail)
+            }
+            None => None,
+        }
     }
 
     fn collect_stmt(&mut self, statements: &mut Vec<Statement>, s: ast::Stmt) {
@@ -1245,7 +1261,7 @@ impl ExprCollector<'_> {
                     (Some(id), Pat::Bind { id, subpat })
                 };
 
-                let ptr = AstPtr::new(&pat);
+                let ptr = AstPtr::new(&Either::Left(pat));
                 let pat = self.alloc_pat(pattern, ptr);
                 if let Some(binding_id) = binding {
                     self.add_definition_to_binding(binding_id, pat);
@@ -1343,10 +1359,9 @@ impl ExprCollector<'_> {
                     suffix: suffix.into_iter().map(|p| self.collect_pat(p, binding_list)).collect(),
                 }
             }
+            #[rustfmt::skip] // https://github.com/rust-lang/rustfmt/issues/5676
             ast::Pat::LiteralPat(lit) => 'b: {
-                let Some((hir_lit, ast_lit)) = pat_literal_to_hir(lit) else {
-                    break 'b Pat::Missing;
-                };
+                let Some((hir_lit, ast_lit)) = pat_literal_to_hir(lit) else { break 'b Pat::Missing };
                 let expr = Expr::Literal(hir_lit);
                 let expr_ptr = AstPtr::new(&ast::Expr::Literal(ast_lit));
                 let expr_id = self.alloc_expr(expr, expr_ptr);
@@ -1382,7 +1397,7 @@ impl ExprCollector<'_> {
             ast::Pat::MacroPat(mac) => match mac.macro_call() {
                 Some(call) => {
                     let macro_ptr = AstPtr::new(&call);
-                    let src = self.expander.in_file(AstPtr::new(&pat));
+                    let src = self.expander.in_file(AstPtr::new(&Either::Left(pat)));
                     let pat =
                         self.collect_macro_call(call, macro_ptr, true, |this, expanded_pat| {
                             this.collect_pat_opt(expanded_pat, binding_list)
@@ -1411,7 +1426,7 @@ impl ExprCollector<'_> {
                 Pat::Range { start, end }
             }
         };
-        let ptr = AstPtr::new(&pat);
+        let ptr = AstPtr::new(&Either::Left(pat));
         self.alloc_pat(pattern, ptr)
     }
 
@@ -1972,19 +1987,10 @@ impl ExprCollector<'_> {
         self.source_map.expr_map.insert(src, id);
         id
     }
-    // FIXME: desugared exprs don't have ptr, that's wrong and should be fixed.
-    // Migrate to alloc_expr_desugared_with_ptr and then rename back
+    // FIXME: desugared exprs don't have ptr, that's wrong and should be fixed somehow.
     fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId {
         self.body.exprs.alloc(expr)
     }
-    fn alloc_expr_desugared_with_ptr(&mut self, expr: Expr, ptr: ExprPtr) -> ExprId {
-        let src = self.expander.in_file(ptr);
-        let id = self.body.exprs.alloc(expr);
-        self.source_map.expr_map_back.insert(id, src);
-        // We intentionally don't fill this as it could overwrite a non-desugared entry
-        // self.source_map.expr_map.insert(src, id);
-        id
-    }
     fn missing_expr(&mut self) -> ExprId {
         self.alloc_expr_desugared(Expr::Missing)
     }
diff --git a/crates/hir-def/src/body/pretty.rs b/crates/hir-def/src/body/pretty.rs
index cbb5ca887f..b2aab55a6a 100644
--- a/crates/hir-def/src/body/pretty.rs
+++ b/crates/hir-def/src/body/pretty.rs
@@ -48,16 +48,7 @@ pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBo
     let mut p = Printer { db, body, buf: header, indent_level: 0, needs_indent: false };
     if let DefWithBodyId::FunctionId(it) = owner {
         p.buf.push('(');
-        let params = &db.function_data(it).params;
-        let mut params = params.iter();
-        if let Some(self_param) = body.self_param {
-            p.print_binding(self_param);
-            p.buf.push(':');
-            if let Some(ty) = params.next() {
-                p.print_type_ref(ty);
-            }
-        }
-        body.params.iter().zip(params).for_each(|(&param, ty)| {
+        body.params.iter().zip(db.function_data(it).params.iter()).for_each(|(&param, ty)| {
             p.print_pat(param);
             p.buf.push(':');
             p.print_type_ref(ty);
diff --git a/crates/hir-def/src/body/scope.rs b/crates/hir-def/src/body/scope.rs
index 0020e4eac3..69b82ae871 100644
--- a/crates/hir-def/src/body/scope.rs
+++ b/crates/hir-def/src/body/scope.rs
@@ -96,9 +96,6 @@ impl ExprScopes {
             scope_by_expr: ArenaMap::with_capacity(body.exprs.len()),
         };
         let mut root = scopes.root_scope();
-        if let Some(self_param) = body.self_param {
-            scopes.add_bindings(body, root, self_param);
-        }
         scopes.add_params_bindings(body, root, &body.params);
         compute_expr_scopes(body.body_expr, body, &mut scopes, &mut root);
         scopes
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index 34ba17f145..9cea414e1a 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -22,7 +22,7 @@ mod pat;
 mod path;
 pub(crate) mod unify;
 
-use std::{convert::identity, iter, ops::Index};
+use std::{convert::identity, ops::Index};
 
 use chalk_ir::{
     cast::Cast, fold::TypeFoldable, interner::HasInterner, DebruijnIndex, Mutability, Safety,
@@ -777,15 +777,7 @@ impl<'a> InferenceContext<'a> {
 
             param_tys.push(va_list_ty)
         }
-        let mut param_tys = param_tys.into_iter().chain(iter::repeat(self.table.new_type_var()));
-        if let Some(self_param) = self.body.self_param {
-            if let Some(ty) = param_tys.next() {
-                let ty = self.insert_type_vars(ty);
-                let ty = self.normalize_associated_types_in(ty);
-                self.write_binding_ty(self_param, ty);
-            }
-        }
-        for (ty, pat) in param_tys.zip(&*self.body.params) {
+        for (ty, pat) in param_tys.into_iter().zip(self.body.params.iter()) {
             let ty = self.insert_type_vars(ty);
             let ty = self.normalize_associated_types_in(ty);
 
diff --git a/crates/hir-ty/src/layout.rs b/crates/hir-ty/src/layout.rs
index 9655981cc9..dea292711d 100644
--- a/crates/hir-ty/src/layout.rs
+++ b/crates/hir-ty/src/layout.rs
@@ -371,8 +371,8 @@ pub fn layout_of_ty_query(
         TyKind::Never => cx.layout_of_never_type(),
         TyKind::Dyn(_) | TyKind::Foreign(_) => {
             let mut unit = layout_of_unit(&cx, dl)?;
-            match &mut unit.abi {
-                Abi::Aggregate { sized } => *sized = false,
+            match unit.abi {
+                Abi::Aggregate { ref mut sized } => *sized = false,
                 _ => return Err(LayoutError::Unknown),
             }
             unit
diff --git a/crates/hir-ty/src/mir.rs b/crates/hir-ty/src/mir.rs
index d513355037..cfaef2a392 100644
--- a/crates/hir-ty/src/mir.rs
+++ b/crates/hir-ty/src/mir.rs
@@ -1165,7 +1165,6 @@ impl MirBody {
 pub enum MirSpan {
     ExprId(ExprId),
     PatId(PatId),
-    SelfParam,
     Unknown,
 }
 
diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs
index fd98141af6..2428678d72 100644
--- a/crates/hir-ty/src/mir/eval.rs
+++ b/crates/hir-ty/src/mir/eval.rs
@@ -376,10 +376,6 @@ impl MirEvalError {
                         Ok(s) => s.map(|it| it.syntax_node_ptr()),
                         Err(_) => continue,
                     },
-                    MirSpan::SelfParam => match source_map.self_param_syntax() {
-                        Some(s) => s.map(|it| it.syntax_node_ptr()),
-                        None => continue,
-                    },
                     MirSpan::Unknown => continue,
                 };
                 let file_id = span.file_id.original_file(db.upcast());
diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs
index 7e582c03ef..d0f739e6ac 100644
--- a/crates/hir-ty/src/mir/lower.rs
+++ b/crates/hir-ty/src/mir/lower.rs
@@ -1810,20 +1810,9 @@ impl<'ctx> MirLowerCtx<'ctx> {
     fn lower_params_and_bindings(
         &mut self,
         params: impl Iterator<Item = (PatId, Ty)> + Clone,
-        self_binding: Option<(BindingId, Ty)>,
         pick_binding: impl Fn(BindingId) -> bool,
     ) -> Result<BasicBlockId> {
         let base_param_count = self.result.param_locals.len();
-        let self_binding = match self_binding {
-            Some((self_binding, ty)) => {
-                let local_id = self.result.locals.alloc(Local { ty });
-                self.drop_scopes.last_mut().unwrap().locals.push(local_id);
-                self.result.binding_locals.insert(self_binding, local_id);
-                self.result.param_locals.push(local_id);
-                Some(self_binding)
-            }
-            None => None,
-        };
         self.result.param_locals.extend(params.clone().map(|(it, ty)| {
             let local_id = self.result.locals.alloc(Local { ty });
             self.drop_scopes.last_mut().unwrap().locals.push(local_id);
@@ -1849,23 +1838,9 @@ impl<'ctx> MirLowerCtx<'ctx> {
             }
         }
         let mut current = self.result.start_block;
-        if let Some(self_binding) = self_binding {
-            let local = self.result.param_locals.clone()[base_param_count];
-            if local != self.binding_local(self_binding)? {
-                let r = self.match_self_param(self_binding, current, local)?;
-                if let Some(b) = r.1 {
-                    self.set_terminator(b, TerminatorKind::Unreachable, MirSpan::SelfParam);
-                }
-                current = r.0;
-            }
-        }
-        let local_params = self
-            .result
-            .param_locals
-            .clone()
-            .into_iter()
-            .skip(base_param_count + self_binding.is_some() as usize);
-        for ((param, _), local) in params.zip(local_params) {
+        for ((param, _), local) in
+            params.zip(self.result.param_locals.clone().into_iter().skip(base_param_count))
+        {
             if let Pat::Bind { id, .. } = self.body[param] {
                 if local == self.binding_local(id)? {
                     continue;
@@ -2044,7 +2019,6 @@ pub fn mir_body_for_closure_query(
     };
     let current = ctx.lower_params_and_bindings(
         args.iter().zip(sig.params().iter()).map(|(it, y)| (*it, y.clone())),
-        None,
         |_| true,
     )?;
     if let Some(current) = ctx.lower_expr_to_place(*root, return_slot().into(), current)? {
@@ -2175,16 +2149,16 @@ pub fn lower_to_mir(
                 let substs = TyBuilder::placeholder_subst(db, fid);
                 let callable_sig =
                     db.callable_item_signature(fid.into()).substitute(Interner, &substs);
-                let mut params = callable_sig.params().iter();
-                let self_param = body.self_param.and_then(|id| Some((id, params.next()?.clone())));
                 break 'b ctx.lower_params_and_bindings(
-                    body.params.iter().zip(params).map(|(it, y)| (*it, y.clone())),
-                    self_param,
+                    body.params
+                        .iter()
+                        .zip(callable_sig.params().iter())
+                        .map(|(it, y)| (*it, y.clone())),
                     binding_picker,
                 )?;
             }
         }
-        ctx.lower_params_and_bindings([].into_iter(), None, binding_picker)?
+        ctx.lower_params_and_bindings([].into_iter(), binding_picker)?
     };
     if let Some(current) = ctx.lower_expr_to_place(root_expr, return_slot().into(), current)? {
         let current = ctx.pop_drop_scope_assert_finished(current, root_expr.into())?;
diff --git a/crates/hir-ty/src/mir/lower/pattern_matching.rs b/crates/hir-ty/src/mir/lower/pattern_matching.rs
index 7596906794..90cbd13a6c 100644
--- a/crates/hir-ty/src/mir/lower/pattern_matching.rs
+++ b/crates/hir-ty/src/mir/lower/pattern_matching.rs
@@ -11,7 +11,7 @@ use crate::{
             Substitution, SwitchTargets, TerminatorKind, TupleFieldId, TupleId, TyBuilder, TyKind,
             ValueNs, VariantData, VariantId,
         },
-        LocalId, MutBorrowKind,
+        MutBorrowKind,
     },
     BindingMode,
 };
@@ -82,22 +82,6 @@ impl MirLowerCtx<'_> {
         Ok((current, current_else))
     }
 
-    pub(super) fn match_self_param(
-        &mut self,
-        id: BindingId,
-        current: BasicBlockId,
-        local: LocalId,
-    ) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
-        self.pattern_match_binding(
-            id,
-            BindingMode::Move,
-            local.into(),
-            MirSpan::SelfParam,
-            current,
-            None,
-        )
-    }
-
     fn pattern_match_inner(
         &mut self,
         mut current: BasicBlockId,
@@ -299,9 +283,9 @@ impl MirLowerCtx<'_> {
                     (current, current_else) =
                         self.pattern_match_inner(current, current_else, next_place, pat, mode)?;
                 }
-                if let &Some(slice) = slice {
+                if let Some(slice) = slice {
                     if mode == MatchingMode::Bind {
-                        if let Pat::Bind { id, subpat: _ } = self.body[slice] {
+                        if let Pat::Bind { id, subpat: _ } = self.body[*slice] {
                             let next_place = cond_place.project(
                                 ProjectionElem::Subslice {
                                     from: prefix.len() as u64,
@@ -309,12 +293,11 @@ impl MirLowerCtx<'_> {
                                 },
                                 &mut self.result.projection_store,
                             );
-                            let mode = self.infer.binding_modes[slice];
                             (current, current_else) = self.pattern_match_binding(
                                 id,
-                                mode,
+                                *slice,
                                 next_place,
-                                (slice).into(),
+                                (*slice).into(),
                                 current,
                                 current_else,
                             )?;
@@ -415,10 +398,9 @@ impl MirLowerCtx<'_> {
                         self.pattern_match_inner(current, current_else, cond_place, *subpat, mode)?
                 }
                 if mode == MatchingMode::Bind {
-                    let mode = self.infer.binding_modes[pattern];
                     self.pattern_match_binding(
                         *id,
-                        mode,
+                        pattern,
                         cond_place,
                         pattern.into(),
                         current,
@@ -455,13 +437,14 @@ impl MirLowerCtx<'_> {
     fn pattern_match_binding(
         &mut self,
         id: BindingId,
-        mode: BindingMode,
+        pat: PatId,
         cond_place: Place,
         span: MirSpan,
         current: BasicBlockId,
         current_else: Option<BasicBlockId>,
     ) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
         let target_place = self.binding_local(id)?;
+        let mode = self.infer.binding_modes[pat];
         self.push_storage_live(id, current)?;
         self.push_assignment(
             current,
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index 4518422d27..fa9fe4953e 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -204,7 +204,7 @@ pub struct NoSuchField {
 
 #[derive(Debug)]
 pub struct PrivateAssocItem {
-    pub expr_or_pat: InFile<AstPtr<Either<ast::Expr, ast::Pat>>>,
+    pub expr_or_pat: InFile<AstPtr<Either<ast::Expr, Either<ast::Pat, ast::SelfParam>>>>,
     pub item: AssocItem,
 }
 
@@ -240,7 +240,7 @@ pub struct UnresolvedMethodCall {
 
 #[derive(Debug)]
 pub struct UnresolvedAssocItem {
-    pub expr_or_pat: InFile<AstPtr<Either<ast::Expr, ast::Pat>>>,
+    pub expr_or_pat: InFile<AstPtr<Either<ast::Expr, Either<ast::Pat, ast::SelfParam>>>>,
 }
 
 #[derive(Debug)]
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index f18853030a..41bc7b5fa4 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -56,8 +56,8 @@ use hir_def::{
     AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId, CrateRootModuleId, DefWithBodyId,
     EnumId, EnumVariantId, ExternCrateId, FunctionId, GenericDefId, GenericParamId, HasModule,
     ImplId, InTypeConstId, ItemContainerId, LifetimeParamId, LocalFieldId, Lookup, MacroExpander,
-    MacroId, ModuleId, StaticId, StructId, TraitAliasId, TraitId, TupleId, TypeAliasId,
-    TypeOrConstParamId, TypeParamId, UnionId,
+    ModuleId, StaticId, StructId, TraitAliasId, TraitId, TupleId, TypeAliasId, TypeOrConstParamId,
+    TypeParamId, UnionId,
 };
 use hir_expand::{attrs::collect_attrs, name::name, proc_macro::ProcMacroKind, MacroCallKind};
 use hir_ty::{
@@ -122,7 +122,7 @@ pub use {
         visibility::Visibility,
         // FIXME: This is here since some queries take it as input that are used
         // outside of hir.
-        {AdtId, ModuleDefId},
+        {AdtId, MacroId, ModuleDefId},
     },
     hir_expand::{
         attrs::{Attr, AttrId},
@@ -1725,10 +1725,6 @@ impl DefWithBody {
                             Ok(s) => s.map(|it| it.into()),
                             Err(_) => continue,
                         },
-                        mir::MirSpan::SelfParam => match source_map.self_param_syntax() {
-                            Some(s) => s.map(|it| it.into()),
-                            None => continue,
-                        },
                         mir::MirSpan::Unknown => continue,
                     };
                     acc.push(
@@ -1780,11 +1776,6 @@ impl DefWithBody {
                                         Ok(s) => s.map(|it| it.into()),
                                         Err(_) => continue,
                                     },
-                                    mir::MirSpan::SelfParam => match source_map.self_param_syntax()
-                                    {
-                                        Some(s) => s.map(|it| it.into()),
-                                        None => continue,
-                                    },
                                     mir::MirSpan::Unknown => continue,
                                 };
                                 acc.push(NeedMut { local, span }.into());
@@ -2136,11 +2127,8 @@ impl Param {
     pub fn as_local(&self, db: &dyn HirDatabase) -> Option<Local> {
         let parent = DefWithBodyId::FunctionId(self.func.into());
         let body = db.body(parent);
-        if let Some(self_param) = body.self_param.filter(|_| self.idx == 0) {
-            Some(Local { parent, binding_id: self_param })
-        } else if let Pat::Bind { id, .. } =
-            &body[body.params[self.idx - body.self_param.is_some() as usize]]
-        {
+        let pat_id = body.params[self.idx];
+        if let Pat::Bind { id, .. } = &body[pat_id] {
             Some(Local { parent, binding_id: *id })
         } else {
             None
@@ -2155,7 +2143,7 @@ impl Param {
         let InFile { file_id, value } = self.func.source(db)?;
         let params = value.param_list()?;
         if params.self_param().is_some() {
-            params.params().nth(self.idx.checked_sub(params.self_param().is_some() as usize)?)
+            params.params().nth(self.idx.checked_sub(1)?)
         } else {
             params.params().nth(self.idx)
         }
@@ -3146,59 +3134,35 @@ impl Local {
     /// All definitions for this local. Example: `let (a$0, _) | (_, a$0) = it;`
     pub fn sources(self, db: &dyn HirDatabase) -> Vec<LocalSource> {
         let (body, source_map) = db.body_with_source_map(self.parent);
-        match body.self_param.zip(source_map.self_param_syntax()) {
-            Some((param, source)) if param == self.binding_id => {
-                let root = source.file_syntax(db.upcast());
-                vec![LocalSource {
-                    local: self,
-                    source: source.map(|ast| Either::Right(ast.to_node(&root))),
-                }]
-            }
-            _ => body[self.binding_id]
-                .definitions
-                .iter()
-                .map(|&definition| {
-                    let src = source_map.pat_syntax(definition).unwrap(); // Hmm...
-                    let root = src.file_syntax(db.upcast());
-                    LocalSource {
-                        local: self,
-                        source: src.map(|ast| match ast.to_node(&root) {
-                            ast::Pat::IdentPat(it) => Either::Left(it),
-                            _ => unreachable!("local with non ident-pattern"),
-                        }),
-                    }
-                })
-                .collect(),
-        }
+        self.sources_(db, &body, &source_map).collect()
     }
 
     /// The leftmost definition for this local. Example: `let (a$0, _) | (_, a) = it;`
     pub fn primary_source(self, db: &dyn HirDatabase) -> LocalSource {
         let (body, source_map) = db.body_with_source_map(self.parent);
-        match body.self_param.zip(source_map.self_param_syntax()) {
-            Some((param, source)) if param == self.binding_id => {
-                let root = source.file_syntax(db.upcast());
-                LocalSource {
-                    local: self,
-                    source: source.map(|ast| Either::Right(ast.to_node(&root))),
-                }
-            }
-            _ => body[self.binding_id]
-                .definitions
-                .first()
-                .map(|&definition| {
-                    let src = source_map.pat_syntax(definition).unwrap(); // Hmm...
-                    let root = src.file_syntax(db.upcast());
-                    LocalSource {
-                        local: self,
-                        source: src.map(|ast| match ast.to_node(&root) {
-                            ast::Pat::IdentPat(it) => Either::Left(it),
-                            _ => unreachable!("local with non ident-pattern"),
-                        }),
-                    }
+        let src = self.sources_(db, &body, &source_map).next().unwrap();
+        src
+    }
+
+    fn sources_<'a>(
+        self,
+        db: &'a dyn HirDatabase,
+        body: &'a hir_def::body::Body,
+        source_map: &'a hir_def::body::BodySourceMap,
+    ) -> impl Iterator<Item = LocalSource> + 'a {
+        body[self.binding_id]
+            .definitions
+            .iter()
+            .map(|&definition| {
+                let src = source_map.pat_syntax(definition).unwrap(); // Hmm...
+                let root = src.file_syntax(db.upcast());
+                src.map(|ast| match ast.to_node(&root) {
+                    Either::Left(ast::Pat::IdentPat(it)) => Either::Left(it),
+                    Either::Left(_) => unreachable!("local with non ident-pattern"),
+                    Either::Right(it) => Either::Right(it),
                 })
-                .unwrap(),
-        }
+            })
+            .map(move |source| LocalSource { local: self, source })
     }
 }
 
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 99907ea15b..02a5d2afac 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -1236,6 +1236,11 @@ impl<'db> SemanticsImpl<'db> {
         sa.resolve_macro_call(self.db, macro_call)
     }
 
+    pub fn is_proc_macro_call(&self, macro_call: &ast::MacroCall) -> bool {
+        self.resolve_macro_call(macro_call)
+            .map_or(false, |m| matches!(m.id, MacroId::ProcMacroId(..)))
+    }
+
     pub fn is_unsafe_macro_call(&self, macro_call: &ast::MacroCall) -> bool {
         let sa = match self.analyze(macro_call.syntax()) {
             Some(it) => it,
diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs
index d4d6f0b243..4733ea5a35 100644
--- a/crates/hir/src/semantics/source_to_def.rs
+++ b/crates/hir/src/semantics/source_to_def.rs
@@ -101,7 +101,7 @@ use hir_def::{
 use hir_expand::{attrs::AttrId, name::AsName, HirFileId, HirFileIdExt, MacroCallId};
 use rustc_hash::FxHashMap;
 use smallvec::SmallVec;
-use stdx::impl_from;
+use stdx::{impl_from, never};
 use syntax::{
     ast::{self, HasName},
     AstNode, SyntaxNode,
@@ -253,8 +253,14 @@ impl SourceToDefCtx<'_, '_> {
         src: InFile<ast::SelfParam>,
     ) -> Option<(DefWithBodyId, BindingId)> {
         let container = self.find_pat_or_label_container(src.syntax())?;
-        let body = self.db.body(container);
-        Some((container, body.self_param?))
+        let (body, source_map) = self.db.body_with_source_map(container);
+        let pat_id = source_map.node_self_param(src.as_ref())?;
+        if let crate::Pat::Bind { id, .. } = body[pat_id] {
+            Some((container, id))
+        } else {
+            never!();
+            None
+        }
     }
     pub(super) fn label_to_def(
         &mut self,
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index dc96a1b03d..f87e0a3897 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -219,10 +219,11 @@ impl SourceAnalyzer {
     pub(crate) fn type_of_self(
         &self,
         db: &dyn HirDatabase,
-        _param: &ast::SelfParam,
+        param: &ast::SelfParam,
     ) -> Option<Type> {
-        let binding = self.body()?.self_param?;
-        let ty = self.infer.as_ref()?[binding].clone();
+        let src = InFile { file_id: self.file_id, value: param };
+        let pat_id = self.body_source_map()?.node_self_param(src)?;
+        let ty = self.infer.as_ref()?[pat_id].clone();
         Some(Type::new_with_resolver(db, &self.resolver, ty))
     }
 
diff --git a/crates/ide-completion/src/item.rs b/crates/ide-completion/src/item.rs
index 4bab288685..357060817c 100644
--- a/crates/ide-completion/src/item.rs
+++ b/crates/ide-completion/src/item.rs
@@ -369,6 +369,7 @@ impl CompletionItemKind {
                 SymbolKind::LifetimeParam => "lt",
                 SymbolKind::Local => "lc",
                 SymbolKind::Macro => "ma",
+                SymbolKind::ProcMacro => "pm",
                 SymbolKind::Module => "md",
                 SymbolKind::SelfParam => "sp",
                 SymbolKind::SelfType => "sy",
diff --git a/crates/ide-db/src/lib.rs b/crates/ide-db/src/lib.rs
index be08b37bac..22afd424d2 100644
--- a/crates/ide-db/src/lib.rs
+++ b/crates/ide-db/src/lib.rs
@@ -348,6 +348,7 @@ pub enum SymbolKind {
     LifetimeParam,
     Local,
     Macro,
+    ProcMacro,
     Module,
     SelfParam,
     SelfType,
@@ -366,9 +367,8 @@ pub enum SymbolKind {
 impl From<hir::MacroKind> for SymbolKind {
     fn from(it: hir::MacroKind) -> Self {
         match it {
-            hir::MacroKind::Declarative | hir::MacroKind::BuiltIn | hir::MacroKind::ProcMacro => {
-                SymbolKind::Macro
-            }
+            hir::MacroKind::Declarative | hir::MacroKind::BuiltIn => SymbolKind::Macro,
+            hir::MacroKind::ProcMacro => SymbolKind::ProcMacro,
             hir::MacroKind::Derive => SymbolKind::Derive,
             hir::MacroKind::Attr => SymbolKind::Attribute,
         }
@@ -381,6 +381,7 @@ impl From<hir::ModuleDefId> for SymbolKind {
             hir::ModuleDefId::ConstId(..) => SymbolKind::Const,
             hir::ModuleDefId::EnumVariantId(..) => SymbolKind::Variant,
             hir::ModuleDefId::FunctionId(..) => SymbolKind::Function,
+            hir::ModuleDefId::MacroId(hir::MacroId::ProcMacroId(..)) => SymbolKind::ProcMacro,
             hir::ModuleDefId::MacroId(..) => SymbolKind::Macro,
             hir::ModuleDefId::ModuleId(..) => SymbolKind::Module,
             hir::ModuleDefId::StaticId(..) => SymbolKind::Static,
diff --git a/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/crates/ide-diagnostics/src/handlers/mutability_errors.rs
index 34a0038295..91f1058d65 100644
--- a/crates/ide-diagnostics/src/handlers/mutability_errors.rs
+++ b/crates/ide-diagnostics/src/handlers/mutability_errors.rs
@@ -413,7 +413,7 @@ fn main() {
 fn main() {
     return;
     let mut x = 2;
-      //^^^^^ 💡 warn: unused variable
+      //^^^^^ warn: unused variable
     &mut x;
 }
 "#,
@@ -423,7 +423,7 @@ fn main() {
 fn main() {
     loop {}
     let mut x = 2;
-      //^^^^^ 💡 warn: unused variable
+      //^^^^^ warn: unused variable
     &mut x;
 }
 "#,
@@ -444,7 +444,7 @@ fn main(b: bool) {
         g();
     }
     let mut x = 2;
-      //^^^^^ 💡 warn: unused variable
+      //^^^^^ warn: unused variable
     &mut x;
 }
 "#,
@@ -459,7 +459,7 @@ fn main(b: bool) {
         return;
     }
     let mut x = 2;
-      //^^^^^ 💡 warn: unused variable
+      //^^^^^ warn: unused variable
     &mut x;
 }
 "#,
@@ -789,7 +789,7 @@ fn f() {
                //^^ 💡 error: cannot mutate immutable variable `x`
     _ = (x, y);
     let x = Foo;
-      //^ 💡 warn: unused variable
+      //^ warn: unused variable
     let x = Foo;
     let y: &mut (i32, u8) = &mut x;
                           //^^^^^^ 💡 error: cannot mutate immutable variable `x`
diff --git a/crates/ide-diagnostics/src/handlers/unused_variables.rs b/crates/ide-diagnostics/src/handlers/unused_variables.rs
index a9e1d07d7c..28ccf474b4 100644
--- a/crates/ide-diagnostics/src/handlers/unused_variables.rs
+++ b/crates/ide-diagnostics/src/handlers/unused_variables.rs
@@ -1,11 +1,3 @@
-use ide_db::{
-    assists::{Assist, AssistId, AssistKind},
-    base_db::FileRange,
-    label::Label,
-    source_change::SourceChange,
-};
-use text_edit::TextEdit;
-
 use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
 
 // Diagnostic: unused-variables
@@ -16,38 +8,18 @@ pub(crate) fn unused_variables(
     d: &hir::UnusedVariable,
 ) -> Diagnostic {
     let ast = d.local.primary_source(ctx.sema.db).syntax_ptr();
-    let diagnostic_range = ctx.sema.diagnostics_display_range(ast);
-    let var_name = d.local.primary_source(ctx.sema.db).syntax().to_string();
     Diagnostic::new_with_syntax_node_ptr(
         ctx,
         DiagnosticCode::RustcLint("unused_variables"),
         "unused variable",
         ast,
     )
-    .with_fixes(fixes(&var_name, diagnostic_range, ast.file_id.is_macro()))
     .experimental()
 }
 
-fn fixes(var_name: &String, diagnostic_range: FileRange, is_in_marco: bool) -> Option<Vec<Assist>> {
-    if is_in_marco {
-        return None;
-    }
-    Some(vec![Assist {
-        id: AssistId("unscore_unused_variable_name", AssistKind::QuickFix),
-        label: Label::new(format!("Rename unused {} to _{}", var_name, var_name)),
-        group: None,
-        target: diagnostic_range.range,
-        source_change: Some(SourceChange::from_text_edit(
-            diagnostic_range.file_id,
-            TextEdit::replace(diagnostic_range.range, format!("_{}", var_name)),
-        )),
-        trigger_signature_help: false,
-    }])
-}
-
 #[cfg(test)]
 mod tests {
-    use crate::tests::{check_diagnostics, check_fix, check_no_fix};
+    use crate::tests::check_diagnostics;
 
     #[test]
     fn unused_variables_simple() {
@@ -57,23 +29,23 @@ mod tests {
 struct Foo { f1: i32, f2: i64 }
 
 fn f(kkk: i32) {}
-   //^^^ 💡 warn: unused variable
+   //^^^ warn: unused variable
 fn main() {
     let a = 2;
-      //^ 💡 warn: unused variable
+      //^ warn: unused variable
     let b = 5;
     // note: `unused variable` implies `unused mut`, so we should not emit both at the same time.
     let mut c = f(b);
-      //^^^^^ 💡 warn: unused variable
+      //^^^^^ warn: unused variable
     let (d, e) = (3, 5);
-       //^ 💡 warn: unused variable
+       //^ warn: unused variable
     let _ = e;
     let f1 = 2;
     let f2 = 5;
     let f = Foo { f1, f2 };
     match f {
         Foo { f1, f2 } => {
-            //^^ 💡 warn: unused variable
+            //^^ warn: unused variable
             _ = f2;
         }
     }
@@ -81,7 +53,7 @@ fn main() {
     if g {}
     let h: fn() -> i32 = || 2;
     let i = h();
-      //^ 💡 warn: unused variable
+      //^ warn: unused variable
 }
 "#,
         );
@@ -95,11 +67,11 @@ struct S {
 }
 impl S {
     fn owned_self(self, u: i32) {}
-                      //^ 💡 warn: unused variable
+                      //^ warn: unused variable
     fn ref_self(&self, u: i32) {}
-                     //^ 💡 warn: unused variable
+                     //^ warn: unused variable
     fn ref_mut_self(&mut self, u: i32) {}
-                             //^ 💡 warn: unused variable
+                             //^ warn: unused variable
     fn owned_mut_self(mut self) {}
                     //^^^^^^^^ 💡 warn: variable does not need to be mutable
 
@@ -131,78 +103,7 @@ fn main() {
 #[deny(unused)]
 fn main2() {
     let x = 2;
-      //^ 💡 error: unused variable
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn fix_unused_variable() {
-        check_fix(
-            r#"
-fn main() {
-    let x$0 = 2;
-}
-"#,
-            r#"
-fn main() {
-    let _x = 2;
-}
-"#,
-        );
-
-        check_fix(
-            r#"
-fn main() {
-    let ($0d, _e) = (3, 5);
-}
-"#,
-            r#"
-fn main() {
-    let (_d, _e) = (3, 5);
-}
-"#,
-        );
-
-        check_fix(
-            r#"
-struct Foo { f1: i32, f2: i64 }
-fn main() {
-    let f = Foo { f1: 0, f2: 0 };
-    match f {
-        Foo { f1$0, f2 } => {
-            _ = f2;
-        }
-    }
-}
-"#,
-            r#"
-struct Foo { f1: i32, f2: i64 }
-fn main() {
-    let f = Foo { f1: 0, f2: 0 };
-    match f {
-        Foo { _f1, f2 } => {
-            _ = f2;
-        }
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn no_fix_for_marco() {
-        check_no_fix(
-            r#"
-macro_rules! my_macro {
-    () => {
-        let x = 3;
-    };
-}
-
-fn main() {
-    $0my_macro!();
+      //^ error: unused variable
 }
 "#,
         );
diff --git a/crates/ide/src/inlay_hints/implicit_drop.rs b/crates/ide/src/inlay_hints/implicit_drop.rs
index 5ba4e514e1..8d9ad5bda1 100644
--- a/crates/ide/src/inlay_hints/implicit_drop.rs
+++ b/crates/ide/src/inlay_hints/implicit_drop.rs
@@ -74,10 +74,6 @@ pub(super) fn hints(
                     Ok(s) => s.value.text_range(),
                     Err(_) => continue,
                 },
-                MirSpan::SelfParam => match source_map.self_param_syntax() {
-                    Some(s) => s.value.text_range(),
-                    None => continue,
-                },
                 MirSpan::Unknown => continue,
             };
             let binding = &hir.bindings[*binding];
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index dcdc6118a3..fef2aba3c6 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -1710,7 +1710,7 @@ use proc_macros::mirror;
 mirror$0! {}
 "#,
             expect![[r#"
-                mirror Macro FileId(1) 1..77 22..28
+                mirror ProcMacro FileId(1) 1..77 22..28
 
                 FileId(0) 26..32
             "#]],
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index d2bd3bab14..6aaf8f8e77 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -248,6 +248,7 @@ fn traverse(
     // an attribute nested in a macro call will not emit `inside_attribute`
     let mut inside_attribute = false;
     let mut inside_macro_call = false;
+    let mut inside_proc_macro_call = false;
 
     // Walk all nodes, keeping track of whether we are inside a macro or not.
     // If in macro, expand it first and highlight the expanded code.
@@ -298,8 +299,9 @@ fn traverse(
                         ast::Item::Fn(_) | ast::Item::Const(_) | ast::Item::Static(_) => {
                             bindings_shadow_count.clear()
                         }
-                        ast::Item::MacroCall(_) => {
+                        ast::Item::MacroCall(ref macro_call) => {
                             inside_macro_call = true;
+                            inside_proc_macro_call = sema.is_proc_macro_call(macro_call);
                         }
                         _ => (),
                     }
@@ -344,6 +346,7 @@ fn traverse(
                     }
                     Some(ast::Item::MacroCall(_)) => {
                         inside_macro_call = false;
+                        inside_proc_macro_call = false;
                     }
                     _ => (),
                 }
@@ -519,6 +522,9 @@ fn traverse(
                 highlight |= HlMod::Attribute
             }
             if inside_macro_call && tt_level > 0 {
+                if inside_proc_macro_call {
+                    highlight |= HlMod::ProcMacro
+                }
                 highlight |= HlMod::Macro
             }
 
diff --git a/crates/ide/src/syntax_highlighting/html.rs b/crates/ide/src/syntax_highlighting/html.rs
index bbc6b55a64..a6dca0541e 100644
--- a/crates/ide/src/syntax_highlighting/html.rs
+++ b/crates/ide/src/syntax_highlighting/html.rs
@@ -98,6 +98,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .numeric_literal    { color: #BFEBBF; }
 .bool_literal       { color: #BFE6EB; }
 .macro              { color: #94BFF3; }
+.proc_macro         { color: #94BFF3; text-decoration: underline; }
 .derive             { color: #94BFF3; font-style: italic; }
 .module             { color: #AFD8AF; }
 .value_param        { color: #DCDCCC; }
diff --git a/crates/ide/src/syntax_highlighting/tags.rs b/crates/ide/src/syntax_highlighting/tags.rs
index 6d4cdd0efe..5163a0de41 100644
--- a/crates/ide/src/syntax_highlighting/tags.rs
+++ b/crates/ide/src/syntax_highlighting/tags.rs
@@ -75,6 +75,7 @@ pub enum HlMod {
     Library,
     /// Used to differentiate individual elements within macro calls.
     Macro,
+    ProcMacro,
     /// Mutable binding.
     Mutable,
     /// Used for public items.
@@ -146,6 +147,7 @@ impl HlTag {
                 SymbolKind::LifetimeParam => "lifetime",
                 SymbolKind::Local => "variable",
                 SymbolKind::Macro => "macro",
+                SymbolKind::ProcMacro => "proc_macro",
                 SymbolKind::Module => "module",
                 SymbolKind::SelfParam => "self_keyword",
                 SymbolKind::SelfType => "self_type_keyword",
@@ -219,6 +221,7 @@ impl HlMod {
         HlMod::IntraDocLink,
         HlMod::Library,
         HlMod::Macro,
+        HlMod::ProcMacro,
         HlMod::Mutable,
         HlMod::Public,
         HlMod::Reference,
@@ -243,6 +246,7 @@ impl HlMod {
             HlMod::IntraDocLink => "intra_doc_link",
             HlMod::Library => "library",
             HlMod::Macro => "macro",
+            HlMod::ProcMacro => "proc_macro",
             HlMod::Mutable => "mutable",
             HlMod::Public => "public",
             HlMod::Reference => "reference",
diff --git a/crates/rust-analyzer/src/lsp/semantic_tokens.rs b/crates/rust-analyzer/src/lsp/semantic_tokens.rs
index dd7dcf5277..c5081c4bea 100644
--- a/crates/rust-analyzer/src/lsp/semantic_tokens.rs
+++ b/crates/rust-analyzer/src/lsp/semantic_tokens.rs
@@ -85,6 +85,7 @@ define_semantic_token_types![
         (LIFETIME, "lifetime"),
         (LOGICAL, "logical") => OPERATOR,
         (MACRO_BANG, "macroBang") => MACRO,
+        (PROC_MACRO, "procMacro") => MACRO,
         (PARENTHESIS, "parenthesis"),
         (PUNCTUATION, "punctuation"),
         (SELF_KEYWORD, "selfKeyword") => KEYWORD,
@@ -143,6 +144,7 @@ define_semantic_token_modifiers![
         (INTRA_DOC_LINK, "intraDocLink"),
         (LIBRARY, "library"),
         (MACRO_MODIFIER, "macro"),
+        (PROC_MACRO_MODIFIER, "proc_macro"),
         (MUTABLE, "mutable"),
         (PUBLIC, "public"),
         (REFERENCE, "reference"),
diff --git a/crates/rust-analyzer/src/lsp/to_proto.rs b/crates/rust-analyzer/src/lsp/to_proto.rs
index 2b0528c9bd..0423b2f4da 100644
--- a/crates/rust-analyzer/src/lsp/to_proto.rs
+++ b/crates/rust-analyzer/src/lsp/to_proto.rs
@@ -57,6 +57,7 @@ pub(crate) fn symbol_kind(symbol_kind: SymbolKind) -> lsp_types::SymbolKind {
         SymbolKind::Variant => lsp_types::SymbolKind::ENUM_MEMBER,
         SymbolKind::Trait | SymbolKind::TraitAlias => lsp_types::SymbolKind::INTERFACE,
         SymbolKind::Macro
+        | SymbolKind::ProcMacro
         | SymbolKind::BuiltinAttr
         | SymbolKind::Attribute
         | SymbolKind::Derive
@@ -139,6 +140,7 @@ pub(crate) fn completion_item_kind(
             SymbolKind::LifetimeParam => lsp_types::CompletionItemKind::TYPE_PARAMETER,
             SymbolKind::Local => lsp_types::CompletionItemKind::VARIABLE,
             SymbolKind::Macro => lsp_types::CompletionItemKind::FUNCTION,
+            SymbolKind::ProcMacro => lsp_types::CompletionItemKind::FUNCTION,
             SymbolKind::Module => lsp_types::CompletionItemKind::MODULE,
             SymbolKind::SelfParam => lsp_types::CompletionItemKind::VALUE,
             SymbolKind::SelfType => lsp_types::CompletionItemKind::TYPE_PARAMETER,
@@ -675,6 +677,7 @@ fn semantic_token_type_and_modifiers(
             SymbolKind::Trait => semantic_tokens::INTERFACE,
             SymbolKind::TraitAlias => semantic_tokens::INTERFACE,
             SymbolKind::Macro => semantic_tokens::MACRO,
+            SymbolKind::ProcMacro => semantic_tokens::PROC_MACRO,
             SymbolKind::BuiltinAttr => semantic_tokens::BUILTIN_ATTRIBUTE,
             SymbolKind::ToolModule => semantic_tokens::TOOL_MODULE,
         },
@@ -728,6 +731,7 @@ fn semantic_token_type_and_modifiers(
             HlMod::IntraDocLink => semantic_tokens::INTRA_DOC_LINK,
             HlMod::Library => semantic_tokens::LIBRARY,
             HlMod::Macro => semantic_tokens::MACRO_MODIFIER,
+            HlMod::ProcMacro => semantic_tokens::PROC_MACRO_MODIFIER,
             HlMod::Mutable => semantic_tokens::MUTABLE,
             HlMod::Public => semantic_tokens::PUBLIC,
             HlMod::Reference => semantic_tokens::REFERENCE,
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index e106a85c6e..bca6db19dc 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -488,22 +488,20 @@ impl GlobalState {
 
     fn update_diagnostics(&mut self) {
         let db = self.analysis_host.raw_database();
-        let subscriptions = {
-            let vfs = &self.vfs.read().0;
-            self.mem_docs
-                .iter()
-                .map(|path| vfs.file_id(path).unwrap())
-                .filter(|&file_id| {
-                    let source_root = db.file_source_root(file_id);
-                    // Only publish diagnostics for files in the workspace, not from crates.io deps
-                    // or the sysroot.
-                    // While theoretically these should never have errors, we have quite a few false
-                    // positives particularly in the stdlib, and those diagnostics would stay around
-                    // forever if we emitted them here.
-                    !db.source_root(source_root).is_library
-                })
-                .collect::<Vec<_>>()
-        };
+        let subscriptions = self
+            .mem_docs
+            .iter()
+            .map(|path| self.vfs.read().0.file_id(path).unwrap())
+            .filter(|&file_id| {
+                let source_root = db.file_source_root(file_id);
+                // Only publish diagnostics for files in the workspace, not from crates.io deps
+                // or the sysroot.
+                // While theoretically these should never have errors, we have quite a few false
+                // positives particularly in the stdlib, and those diagnostics would stay around
+                // forever if we emitted them here.
+                !db.source_root(source_root).is_library
+            })
+            .collect::<Vec<_>>();
         tracing::trace!("updating notifications for {:?}", subscriptions);
 
         // Diagnostics are triggered by the user typing


Test patch:
diff --git a/crates/hir-ty/src/tests.rs b/crates/hir-ty/src/tests.rs
index 1cae8eaeac..5e159236f4 100644
--- a/crates/hir-ty/src/tests.rs
+++ b/crates/hir-ty/src/tests.rs
@@ -293,29 +293,20 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
         let mut types: Vec<(InFile<SyntaxNode>, &Ty)> = Vec::new();
         let mut mismatches: Vec<(InFile<SyntaxNode>, &TypeMismatch)> = Vec::new();
 
-        if let Some(self_param) = body.self_param {
-            let ty = &inference_result.type_of_binding[self_param];
-            if let Some(syntax_ptr) = body_source_map.self_param_syntax() {
-                let root = db.parse_or_expand(syntax_ptr.file_id);
-                let node = syntax_ptr.map(|ptr| ptr.to_node(&root).syntax().clone());
-                types.push((node.clone(), ty));
-            }
-        }
-
         for (pat, mut ty) in inference_result.type_of_pat.iter() {
             if let Pat::Bind { id, .. } = body.pats[pat] {
                 ty = &inference_result.type_of_binding[id];
             }
-            let node = match body_source_map.pat_syntax(pat) {
+            let syntax_ptr = match body_source_map.pat_syntax(pat) {
                 Ok(sp) => {
                     let root = db.parse_or_expand(sp.file_id);
                     sp.map(|ptr| ptr.to_node(&root).syntax().clone())
                 }
                 Err(SyntheticSyntax) => continue,
             };
-            types.push((node.clone(), ty));
+            types.push((syntax_ptr.clone(), ty));
             if let Some(mismatch) = inference_result.type_mismatch_for_pat(pat) {
-                mismatches.push((node, mismatch));
+                mismatches.push((syntax_ptr, mismatch));
             }
         }
 
diff --git a/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs
index 917e9f4408..ffd6a6051b 100644
--- a/crates/hir-ty/src/tests/simple.rs
+++ b/crates/hir-ty/src/tests/simple.rs
@@ -2121,7 +2121,6 @@ async fn main() {
         "#,
         expect![[r#"
             16..193 '{     ...2 }; }': ()
-            16..193 '{     ...2 }; }': impl Future<Output = ()>
             26..27 'x': i32
             30..43 'unsafe { 92 }': i32
             39..41 '92': i32
@@ -2132,8 +2131,6 @@ async fn main() {
             73..75 '()': ()
             95..96 'z': ControlFlow<(), ()>
             130..140 'try { () }': ControlFlow<(), ()>
-            130..140 'try { () }': fn from_output<ControlFlow<(), ()>>(<ControlFlow<(), ()> as Try>::Output) -> ControlFlow<(), ()>
-            130..140 'try { () }': ControlFlow<(), ()>
             136..138 '()': ()
             150..151 'w': i32
             154..166 'const { 92 }': i32
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html
index 4dcbfe4eb6..6994cb3d5c 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html
@@ -29,6 +29,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .numeric_literal    { color: #BFEBBF; }
 .bool_literal       { color: #BFE6EB; }
 .macro              { color: #94BFF3; }
+.proc_macro         { color: #94BFF3; text-decoration: underline; }
 .derive             { color: #94BFF3; font-style: italic; }
 .module             { color: #AFD8AF; }
 .value_param        { color: #DCDCCC; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html b/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html
index bf5505caf3..dc2d103b58 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html
@@ -29,6 +29,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .numeric_literal    { color: #BFEBBF; }
 .bool_literal       { color: #BFE6EB; }
 .macro              { color: #94BFF3; }
+.proc_macro         { color: #94BFF3; text-decoration: underline; }
 .derive             { color: #94BFF3; font-style: italic; }
 .module             { color: #AFD8AF; }
 .value_param        { color: #DCDCCC; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html b/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html
index 977d18c6b7..093cc2358a 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html
@@ -29,6 +29,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .numeric_literal    { color: #BFEBBF; }
 .bool_literal       { color: #BFE6EB; }
 .macro              { color: #94BFF3; }
+.proc_macro         { color: #94BFF3; text-decoration: underline; }
 .derive             { color: #94BFF3; font-style: italic; }
 .module             { color: #AFD8AF; }
 .value_param        { color: #DCDCCC; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html b/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html
index 0d1b3c1f18..154b823fff 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html
@@ -29,6 +29,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .numeric_literal    { color: #BFEBBF; }
 .bool_literal       { color: #BFE6EB; }
 .macro              { color: #94BFF3; }
+.proc_macro         { color: #94BFF3; text-decoration: underline; }
 .derive             { color: #94BFF3; font-style: italic; }
 .module             { color: #AFD8AF; }
 .value_param        { color: #DCDCCC; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html b/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html
index dd1528ed03..58613bf151 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html
@@ -29,6 +29,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .numeric_literal    { color: #BFEBBF; }
 .bool_literal       { color: #BFE6EB; }
 .macro              { color: #94BFF3; }
+.proc_macro         { color: #94BFF3; text-decoration: underline; }
 .derive             { color: #94BFF3; font-style: italic; }
 .module             { color: #AFD8AF; }
 .value_param        { color: #DCDCCC; }
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 d5f92aa5d4..34274932af 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
@@ -29,6 +29,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .numeric_literal    { color: #BFEBBF; }
 .bool_literal       { color: #BFE6EB; }
 .macro              { color: #94BFF3; }
+.proc_macro         { color: #94BFF3; text-decoration: underline; }
 .derive             { color: #94BFF3; font-style: italic; }
 .module             { color: #AFD8AF; }
 .value_param        { color: #DCDCCC; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html b/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
index 88a008796b..729e4791f5 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
@@ -29,6 +29,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .numeric_literal    { color: #BFEBBF; }
 .bool_literal       { color: #BFE6EB; }
 .macro              { color: #94BFF3; }
+.proc_macro         { color: #94BFF3; text-decoration: underline; }
 .derive             { color: #94BFF3; font-style: italic; }
 .module             { color: #AFD8AF; }
 .value_param        { color: #DCDCCC; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_general.html b/crates/ide/src/syntax_highlighting/test_data/highlight_general.html
index bdeb09d2f8..066fcfb1df 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_general.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_general.html
@@ -29,6 +29,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .numeric_literal    { color: #BFEBBF; }
 .bool_literal       { color: #BFE6EB; }
 .macro              { color: #94BFF3; }
+.proc_macro         { color: #94BFF3; text-decoration: underline; }
 .derive             { color: #94BFF3; font-style: italic; }
 .module             { color: #AFD8AF; }
 .value_param        { color: #DCDCCC; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
index f9c33b8a60..58a147dd80 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
@@ -29,6 +29,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .numeric_literal    { color: #BFEBBF; }
 .bool_literal       { color: #BFE6EB; }
 .macro              { color: #94BFF3; }
+.proc_macro         { color: #94BFF3; text-decoration: underline; }
 .derive             { color: #94BFF3; font-style: italic; }
 .module             { color: #AFD8AF; }
 .value_param        { color: #DCDCCC; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html
index 2043752bc7..22ae5c82a4 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html
@@ -29,6 +29,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .numeric_literal    { color: #BFEBBF; }
 .bool_literal       { color: #BFE6EB; }
 .macro              { color: #94BFF3; }
+.proc_macro         { color: #94BFF3; text-decoration: underline; }
 .derive             { color: #94BFF3; font-style: italic; }
 .module             { color: #AFD8AF; }
 .value_param        { color: #DCDCCC; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html b/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html
index ec39998de2..af77999659 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html
@@ -29,6 +29,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .numeric_literal    { color: #BFEBBF; }
 .bool_literal       { color: #BFE6EB; }
 .macro              { color: #94BFF3; }
+.proc_macro         { color: #94BFF3; text-decoration: underline; }
 .derive             { color: #94BFF3; font-style: italic; }
 .module             { color: #AFD8AF; }
 .value_param        { color: #DCDCCC; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html b/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html
index 4063cf9f75..bcfe542410 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html
@@ -29,6 +29,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .numeric_literal    { color: #BFEBBF; }
 .bool_literal       { color: #BFE6EB; }
 .macro              { color: #94BFF3; }
+.proc_macro         { color: #94BFF3; text-decoration: underline; }
 .derive             { color: #94BFF3; font-style: italic; }
 .module             { color: #AFD8AF; }
 .value_param        { color: #DCDCCC; }
@@ -45,12 +46,12 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 </style>
 <pre><code><span class="keyword">use</span> <span class="module crate_root library">proc_macros</span><span class="operator">::</span><span class="brace">{</span><span class="function library">mirror</span><span class="comma">,</span> <span class="function library">identity</span><span class="comma">,</span> <span class="derive library">DeriveIdentity</span><span class="brace">}</span><span class="semicolon">;</span>
 
-<span class="macro library">mirror</span><span class="macro_bang">!</span> <span class="brace macro">{</span>
-    <span class="brace macro">{</span>
-        <span class="comma macro">,</span><span class="builtin_type macro">i32</span> <span class="colon macro">:</span><span class="field declaration macro public">x</span> <span class="keyword macro">pub</span>
-        <span class="comma macro">,</span><span class="builtin_type macro">i32</span> <span class="colon macro">:</span><span class="field declaration macro public">y</span> <span class="keyword macro">pub</span>
-    <span class="brace macro">}</span> <span class="struct declaration macro">Foo</span> <span class="keyword macro">struct</span>
-<span class="brace macro">}</span>
+<span class="proc_macro library">mirror</span><span class="macro_bang">!</span> <span class="brace macro proc_macro">{</span>
+    <span class="brace macro proc_macro">{</span>
+        <span class="comma macro proc_macro">,</span><span class="builtin_type macro proc_macro">i32</span> <span class="colon macro proc_macro">:</span><span class="field declaration macro proc_macro public">x</span> <span class="keyword macro proc_macro">pub</span>
+        <span class="comma macro proc_macro">,</span><span class="builtin_type macro proc_macro">i32</span> <span class="colon macro proc_macro">:</span><span class="field declaration macro proc_macro public">y</span> <span class="keyword macro proc_macro">pub</span>
+    <span class="brace macro proc_macro">}</span> <span class="struct declaration macro proc_macro">Foo</span> <span class="keyword macro proc_macro">struct</span>
+<span class="brace macro proc_macro">}</span>
 <span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">def_fn</span> <span class="brace">{</span>
     <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="brace">}</span>
 <span class="brace">}</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html b/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html
index 4dcf8e5f01..ef8a48ca1c 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html
@@ -29,6 +29,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .numeric_literal    { color: #BFEBBF; }
 .bool_literal       { color: #BFE6EB; }
 .macro              { color: #94BFF3; }
+.proc_macro         { color: #94BFF3; text-decoration: underline; }
 .derive             { color: #94BFF3; font-style: italic; }
 .module             { color: #AFD8AF; }
 .value_param        { color: #DCDCCC; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_outline.html b/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_outline.html
index 084bbf2f74..a2ded15fd1 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_outline.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_outline.html
@@ -29,6 +29,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .numeric_literal    { color: #BFEBBF; }
 .bool_literal       { color: #BFE6EB; }
 .macro              { color: #94BFF3; }
+.proc_macro         { color: #94BFF3; text-decoration: underline; }
 .derive             { color: #94BFF3; font-style: italic; }
 .module             { color: #AFD8AF; }
 .value_param        { color: #DCDCCC; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_operators.html b/crates/ide/src/syntax_highlighting/test_data/highlight_operators.html
index 1af4bcfbd9..d123ee0497 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_operators.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_operators.html
@@ -29,6 +29,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .numeric_literal    { color: #BFEBBF; }
 .bool_literal       { color: #BFE6EB; }
 .macro              { color: #94BFF3; }
+.proc_macro         { color: #94BFF3; text-decoration: underline; }
 .derive             { color: #94BFF3; font-style: italic; }
 .module             { color: #AFD8AF; }
 .value_param        { color: #DCDCCC; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html b/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html
index 7ee7b338c1..4429e5d933 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html
@@ -29,6 +29,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .numeric_literal    { color: #BFEBBF; }
 .bool_literal       { color: #BFE6EB; }
 .macro              { color: #94BFF3; }
+.proc_macro         { color: #94BFF3; text-decoration: underline; }
 .derive             { color: #94BFF3; font-style: italic; }
 .module             { color: #AFD8AF; }
 .value_param        { color: #DCDCCC; }
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 84a823363f..b6458fa7ca 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
@@ -29,6 +29,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .numeric_literal    { color: #BFEBBF; }
 .bool_literal       { color: #BFE6EB; }
 .macro              { color: #94BFF3; }
+.proc_macro         { color: #94BFF3; text-decoration: underline; }
 .derive             { color: #94BFF3; font-style: italic; }
 .module             { color: #AFD8AF; }
 .value_param        { color: #DCDCCC; }
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 c72ea54e94..49b588baa5 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
@@ -29,6 +29,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .numeric_literal    { color: #BFEBBF; }
 .bool_literal       { color: #BFE6EB; }
 .macro              { color: #94BFF3; }
+.proc_macro         { color: #94BFF3; text-decoration: underline; }
 .derive             { color: #94BFF3; font-style: italic; }
 .module             { color: #AFD8AF; }
 .value_param        { color: #DCDCCC; }