Repository: rust-analyzer/rust-analyzer (rust)

Issue:
Semantic highlighting: `$type` in `macro_rules!` is not a keyword
```rust
macro_rules! foo {
    ($type:ty) => {}
}
```

`type` is currently marked as a keyword, excluding the dollar sign. As it's only part of the `macro_rules!` matcher, this shouldn't be the case.

Pull request:
Skip macro matcher fragment name semantic highlighting
Implements a small state-machine for macro_rules! highlighting to separate out the matcher part of its rules. This skips semantically highlighting names of metavariables in the matcher and expander. This might even allow for more fun macro highlighting things in the future.

Fixes #4380.

Patch:
diff --git a/Cargo.toml b/Cargo.toml
index 87d33f06c8..218581d9d0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -19,6 +19,18 @@ miniz_oxide.opt-level = 3
 incremental = true
 debug = 0 # Set this to 1 or 2 to get more useful backtraces in debugger.
 
+# Ideally, we would use `build-override` here, but some crates are also
+# needed at run-time and we end up compiling them twice.
+[profile.release.package]
+chalk-derive.opt-level = 0
+proc-macro2.opt-level = 0
+quote.opt-level = 0
+salsa-macros.opt-level = 0
+serde_derive.opt-level = 0
+syn.opt-level = 0
+tracing-attributes.opt-level = 0
+xtask.opt-level = 0
+
 [patch.'crates-io']
 # rowan = { path = "../rowan" }
 
diff --git a/crates/assists/src/handlers/fix_visibility.rs b/crates/assists/src/handlers/fix_visibility.rs
index d505e9444a..7cd76ea065 100644
--- a/crates/assists/src/handlers/fix_visibility.rs
+++ b/crates/assists/src/handlers/fix_visibility.rs
@@ -324,14 +324,14 @@ pub struct Foo { pub bar: () }
 
     #[test]
     fn fix_visibility_of_enum_variant_field() {
-        // Enum variants, as well as their fields, always get the enum's visibility. In fact, rustc
-        // rejects any visibility specifiers on them, so this assist should never fire on them.
-        check_assist_not_applicable(
+        check_assist(
             fix_visibility,
             r"mod foo { pub enum Foo { Bar { bar: () } } }
               fn main() { foo::Foo::Bar { <|>bar: () }; } ",
+            r"mod foo { pub enum Foo { Bar { $0pub(crate) bar: () } } }
+              fn main() { foo::Foo::Bar { bar: () }; } ",
         );
-        check_assist_not_applicable(
+        check_assist(
             fix_visibility,
             r"
 //- /lib.rs
@@ -339,6 +339,8 @@ mod foo;
 fn main() { foo::Foo::Bar { <|>bar: () }; }
 //- /foo.rs
 pub enum Foo { Bar { bar: () } }
+",
+            r"pub enum Foo { Bar { $0pub(crate) bar: () } }
 ",
         );
         check_assist_not_applicable(
diff --git a/crates/hir_def/src/adt.rs b/crates/hir_def/src/adt.rs
index 6539959c3c..d69ff2fc79 100644
--- a/crates/hir_def/src/adt.rs
+++ b/crates/hir_def/src/adt.rs
@@ -14,7 +14,7 @@ use tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree};
 use crate::{
     body::{CfgExpander, LowerCtx},
     db::DefDatabase,
-    item_tree::{AttrOwner, Field, Fields, ItemTree, ModItem, RawVisibilityId},
+    item_tree::{AttrOwner, Field, Fields, ItemTree, ModItem},
     src::HasChildSource,
     src::HasSource,
     trace::Trace,
@@ -91,7 +91,7 @@ impl StructData {
         let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone();
 
         let strukt = &item_tree[loc.id.value];
-        let variant_data = lower_fields(&item_tree, &cfg_options, &strukt.fields, None);
+        let variant_data = lower_fields(&item_tree, &cfg_options, &strukt.fields);
         Arc::new(StructData {
             name: strukt.name.clone(),
             variant_data: Arc::new(variant_data),
@@ -105,7 +105,7 @@ impl StructData {
         let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone();
 
         let union = &item_tree[loc.id.value];
-        let variant_data = lower_fields(&item_tree, &cfg_options, &union.fields, None);
+        let variant_data = lower_fields(&item_tree, &cfg_options, &union.fields);
 
         Arc::new(StructData {
             name: union.name.clone(),
@@ -126,8 +126,7 @@ impl EnumData {
         for var_id in enum_.variants.clone() {
             if item_tree.attrs(var_id.into()).is_cfg_enabled(&cfg_options) {
                 let var = &item_tree[var_id];
-                let var_data =
-                    lower_fields(&item_tree, &cfg_options, &var.fields, Some(enum_.visibility));
+                let var_data = lower_fields(&item_tree, &cfg_options, &var.fields);
 
                 variants.alloc(EnumVariantData {
                     name: var.name.clone(),
@@ -297,18 +296,13 @@ fn lower_struct(
     }
 }
 
-fn lower_fields(
-    item_tree: &ItemTree,
-    cfg_options: &CfgOptions,
-    fields: &Fields,
-    override_visibility: Option<RawVisibilityId>,
-) -> VariantData {
+fn lower_fields(item_tree: &ItemTree, cfg_options: &CfgOptions, fields: &Fields) -> VariantData {
     match fields {
         Fields::Record(flds) => {
             let mut arena = Arena::new();
             for field_id in flds.clone() {
                 if item_tree.attrs(field_id.into()).is_cfg_enabled(cfg_options) {
-                    arena.alloc(lower_field(item_tree, &item_tree[field_id], override_visibility));
+                    arena.alloc(lower_field(item_tree, &item_tree[field_id]));
                 }
             }
             VariantData::Record(arena)
@@ -317,7 +311,7 @@ fn lower_fields(
             let mut arena = Arena::new();
             for field_id in flds.clone() {
                 if item_tree.attrs(field_id.into()).is_cfg_enabled(cfg_options) {
-                    arena.alloc(lower_field(item_tree, &item_tree[field_id], override_visibility));
+                    arena.alloc(lower_field(item_tree, &item_tree[field_id]));
                 }
             }
             VariantData::Tuple(arena)
@@ -326,14 +320,10 @@ fn lower_fields(
     }
 }
 
-fn lower_field(
-    item_tree: &ItemTree,
-    field: &Field,
-    override_visibility: Option<RawVisibilityId>,
-) -> FieldData {
+fn lower_field(item_tree: &ItemTree, field: &Field) -> FieldData {
     FieldData {
         name: field.name.clone(),
         type_ref: field.type_ref.clone(),
-        visibility: item_tree[override_visibility.unwrap_or(field.visibility)].clone(),
+        visibility: item_tree[field.visibility].clone(),
     }
 }
diff --git a/crates/ide/src/completion/complete_postfix/format_like.rs b/crates/ide/src/completion/complete_postfix/format_like.rs
index 50d1e5c815..81c33bf3af 100644
--- a/crates/ide/src/completion/complete_postfix/format_like.rs
+++ b/crates/ide/src/completion/complete_postfix/format_like.rs
@@ -25,7 +25,6 @@ static KINDS: &[(&str, &str)] = &[
     ("fmt", "format!"),
     ("panic", "panic!"),
     ("println", "println!"),
-    ("eprintln", "eprintln!"),
     ("logd", "log::debug!"),
     ("logt", "log::trace!"),
     ("logi", "log::info!"),
@@ -260,7 +259,6 @@ mod tests {
     fn test_into_suggestion() {
         let test_vector = &[
             ("println!", "{}", r#"println!("{}", $1)"#),
-            ("eprintln!", "{}", r#"eprintln!("{}", $1)"#),
             (
                 "log::info!",
                 "{} {expr} {} {2 + 2}",
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index d54c06b14f..57f3581b6b 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -77,9 +77,7 @@ pub use crate::{
     hover::{HoverAction, HoverConfig, HoverGotoTypeData, HoverResult},
     inlay_hints::{InlayHint, InlayHintsConfig, InlayKind},
     markup::Markup,
-    references::{
-        Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult, RenameError,
-    },
+    references::{Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult},
     runnables::{Runnable, RunnableKind, TestId},
     syntax_highlighting::{
         Highlight, HighlightModifier, HighlightModifiers, HighlightTag, HighlightedRange,
@@ -492,7 +490,7 @@ impl Analysis {
         &self,
         position: FilePosition,
         new_name: &str,
-    ) -> Cancelable<Result<RangeInfo<SourceChange>, RenameError>> {
+    ) -> Cancelable<Option<RangeInfo<SourceChange>>> {
         self.with_db(|db| references::rename(db, position, new_name))
     }
 
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index f65a05ea33..571dd5452c 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -26,7 +26,6 @@ use syntax::{
 use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeInfo};
 
 pub(crate) use self::rename::rename;
-pub use self::rename::RenameError;
 
 pub use ide_db::search::{Reference, ReferenceAccess, ReferenceKind};
 
@@ -733,30 +732,6 @@ fn f(e: En) {
         );
     }
 
-    #[test]
-    fn test_find_all_refs_enum_var_privacy() {
-        check(
-            r#"
-mod m {
-    pub enum En {
-        Variant {
-            field<|>: u8,
-        }
-    }
-}
-
-fn f() -> m::En {
-    m::En::Variant { field: 0 }
-}
-"#,
-            expect![[r#"
-                field RECORD_FIELD FileId(0) 56..65 56..61 Other
-
-                FileId(0) 125..130 Other Read
-            "#]],
-        );
-    }
-
     fn check(ra_fixture: &str, expect: Expect) {
         check_with_scope(ra_fixture, None, expect)
     }
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs
index f3b5cfc8c1..8cbe1ae5a2 100644
--- a/crates/ide/src/references/rename.rs
+++ b/crates/ide/src/references/rename.rs
@@ -6,16 +6,11 @@ use ide_db::{
     defs::{classify_name, classify_name_ref, Definition, NameClass, NameRefClass},
     RootDatabase,
 };
-
-use std::{
-    convert::TryInto,
-    error::Error,
-    fmt::{self, Display},
-};
+use std::convert::TryInto;
 use syntax::{
     algo::find_node_at_offset,
     ast::{self, NameOwner},
-    lex_single_syntax_kind, match_ast, AstNode, SyntaxKind, SyntaxNode, SyntaxToken,
+    lex_single_valid_syntax_kind, match_ast, AstNode, SyntaxKind, SyntaxNode, SyntaxToken,
 };
 use test_utils::mark;
 use text_edit::TextEdit;
@@ -25,37 +20,17 @@ use crate::{
     SourceChange, SourceFileEdit, TextRange, TextSize,
 };
 
-#[derive(Debug)]
-pub struct RenameError(pub(crate) String);
-
-impl fmt::Display for RenameError {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        Display::fmt(&self.0, f)
-    }
-}
-
-impl Error for RenameError {}
-
 pub(crate) fn rename(
     db: &RootDatabase,
     position: FilePosition,
     new_name: &str,
-) -> Result<RangeInfo<SourceChange>, RenameError> {
+) -> Option<RangeInfo<SourceChange>> {
     let sema = Semantics::new(db);
 
-    match lex_single_syntax_kind(new_name) {
-        Some(res) => match res {
-            (SyntaxKind::IDENT, _) => (),
-            (SyntaxKind::UNDERSCORE, _) => (),
-            (SyntaxKind::SELF_KW, _) => return rename_to_self(&sema, position),
-            (_, Some(syntax_error)) => {
-                return Err(RenameError(format!("Invalid name `{}`: {}", new_name, syntax_error)))
-            }
-            (_, None) => {
-                return Err(RenameError(format!("Invalid name `{}`: not an identifier", new_name)))
-            }
-        },
-        None => return Err(RenameError(format!("Invalid name `{}`: not an identifier", new_name))),
+    match lex_single_valid_syntax_kind(new_name)? {
+        SyntaxKind::IDENT | SyntaxKind::UNDERSCORE => (),
+        SyntaxKind::SELF_KW => return rename_to_self(&sema, position),
+        _ => return None,
     }
 
     let source_file = sema.parse(position.file_id);
@@ -128,7 +103,7 @@ fn rename_mod(
     position: FilePosition,
     module: Module,
     new_name: &str,
-) -> Result<RangeInfo<SourceChange>, RenameError> {
+) -> Option<RangeInfo<SourceChange>> {
     let mut source_file_edits = Vec::new();
     let mut file_system_edits = Vec::new();
 
@@ -150,7 +125,7 @@ fn rename_mod(
 
     if let Some(src) = module.declaration_source(sema.db) {
         let file_id = src.file_id.original_file(sema.db);
-        let name = src.value.name().unwrap();
+        let name = src.value.name()?;
         let edit = SourceFileEdit {
             file_id,
             edit: TextEdit::replace(name.syntax().text_range(), new_name.into()),
@@ -158,40 +133,35 @@ fn rename_mod(
         source_file_edits.push(edit);
     }
 
-    let RangeInfo { range, info: refs } = find_all_refs(sema, position, None)
-        .ok_or_else(|| RenameError("No references found at position".to_string()))?;
+    let RangeInfo { range, info: refs } = find_all_refs(sema, position, None)?;
     let ref_edits = refs
         .references
         .into_iter()
         .map(|reference| source_edit_from_reference(reference, new_name));
     source_file_edits.extend(ref_edits);
 
-    Ok(RangeInfo::new(range, SourceChange::from_edits(source_file_edits, file_system_edits)))
+    Some(RangeInfo::new(range, SourceChange::from_edits(source_file_edits, file_system_edits)))
 }
 
 fn rename_to_self(
     sema: &Semantics<RootDatabase>,
     position: FilePosition,
-) -> Result<RangeInfo<SourceChange>, RenameError> {
+) -> Option<RangeInfo<SourceChange>> {
     let source_file = sema.parse(position.file_id);
     let syn = source_file.syntax();
 
-    let fn_def = find_node_at_offset::<ast::Fn>(syn, position.offset)
-        .ok_or_else(|| RenameError("No surrounding method declaration found".to_string()))?;
-    let params =
-        fn_def.param_list().ok_or_else(|| RenameError("Method has no parameters".to_string()))?;
+    let fn_def = find_node_at_offset::<ast::Fn>(syn, position.offset)?;
+    let params = fn_def.param_list()?;
     if params.self_param().is_some() {
-        return Err(RenameError("Method already has a self parameter".to_string()));
+        return None; // method already has self param
     }
-    let first_param =
-        params.params().next().ok_or_else(|| RenameError("Method has no parameters".into()))?;
+    let first_param = params.params().next()?;
     let mutable = match first_param.ty() {
         Some(ast::Type::RefType(rt)) => rt.mut_token().is_some(),
-        _ => return Err(RenameError("Not renaming other types".to_string())),
+        _ => return None, // not renaming other types
     };
 
-    let RangeInfo { range, info: refs } = find_all_refs(sema, position, None)
-        .ok_or_else(|| RenameError("No reference found at position".to_string()))?;
+    let RangeInfo { range, info: refs } = find_all_refs(sema, position, None)?;
 
     let param_range = first_param.syntax().text_range();
     let (param_ref, usages): (Vec<Reference>, Vec<Reference>) = refs
@@ -199,7 +169,7 @@ fn rename_to_self(
         .partition(|reference| param_range.intersect(reference.file_range.range).is_some());
 
     if param_ref.is_empty() {
-        return Err(RenameError("Parameter to rename not found".to_string()));
+        return None;
     }
 
     let mut edits = usages
@@ -215,7 +185,7 @@ fn rename_to_self(
         ),
     });
 
-    Ok(RangeInfo::new(range, SourceChange::from(edits)))
+    Some(RangeInfo::new(range, SourceChange::from(edits)))
 }
 
 fn text_edit_from_self_param(
@@ -246,13 +216,12 @@ fn rename_self_to_param(
     position: FilePosition,
     self_token: SyntaxToken,
     new_name: &str,
-) -> Result<RangeInfo<SourceChange>, RenameError> {
+) -> Option<RangeInfo<SourceChange>> {
     let source_file = sema.parse(position.file_id);
     let syn = source_file.syntax();
 
     let text = sema.db.file_text(position.file_id);
-    let fn_def = find_node_at_offset::<ast::Fn>(syn, position.offset)
-        .ok_or_else(|| RenameError("No surrounding method declaration found".to_string()))?;
+    let fn_def = find_node_at_offset::<ast::Fn>(syn, position.offset)?;
     let search_range = fn_def.syntax().text_range();
 
     let mut edits: Vec<SourceFileEdit> = vec![];
@@ -266,8 +235,7 @@ fn rename_self_to_param(
             syn.token_at_offset(offset).find(|t| t.kind() == SyntaxKind::SELF_KW)
         {
             let edit = if let Some(ref self_param) = ast::SelfParam::cast(usage.parent()) {
-                text_edit_from_self_param(syn, self_param, new_name)
-                    .ok_or_else(|| RenameError("No target type found".to_string()))?
+                text_edit_from_self_param(syn, self_param, new_name)?
             } else {
                 TextEdit::replace(usage.text_range(), String::from(new_name))
             };
@@ -278,18 +246,15 @@ fn rename_self_to_param(
     let range = ast::SelfParam::cast(self_token.parent())
         .map_or(self_token.text_range(), |p| p.syntax().text_range());
 
-    Ok(RangeInfo::new(range, SourceChange::from(edits)))
+    Some(RangeInfo::new(range, SourceChange::from(edits)))
 }
 
 fn rename_reference(
     sema: &Semantics<RootDatabase>,
     position: FilePosition,
     new_name: &str,
-) -> Result<RangeInfo<SourceChange>, RenameError> {
-    let RangeInfo { range, info: refs } = match find_all_refs(sema, position, None) {
-        Some(range_info) => range_info,
-        None => return Err(RenameError("No references found at position".to_string())),
-    };
+) -> Option<RangeInfo<SourceChange>> {
+    let RangeInfo { range, info: refs } = find_all_refs(sema, position, None)?;
 
     let edit = refs
         .into_iter()
@@ -297,10 +262,10 @@ fn rename_reference(
         .collect::<Vec<_>>();
 
     if edit.is_empty() {
-        return Err(RenameError("No references found at position".to_string()));
+        return None;
     }
 
-    Ok(RangeInfo::new(range, SourceChange::from(edit)))
+    Some(RangeInfo::new(range, SourceChange::from(edit)))
 }
 
 #[cfg(test)]
@@ -315,45 +280,25 @@ mod tests {
     fn check(new_name: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
         let ra_fixture_after = &trim_indent(ra_fixture_after);
         let (analysis, position) = fixture::position(ra_fixture_before);
-        let rename_result = analysis
-            .rename(position, new_name)
-            .unwrap_or_else(|err| panic!("Rename to '{}' was cancelled: {}", new_name, err));
-        match rename_result {
-            Ok(source_change) => {
-                let mut text_edit_builder = TextEdit::builder();
-                let mut file_id: Option<FileId> = None;
-                for edit in source_change.info.source_file_edits {
-                    file_id = Some(edit.file_id);
-                    for indel in edit.edit.into_iter() {
-                        text_edit_builder.replace(indel.delete, indel.insert);
-                    }
-                }
-                let mut result = analysis.file_text(file_id.unwrap()).unwrap().to_string();
-                text_edit_builder.finish().apply(&mut result);
-                assert_eq_text!(ra_fixture_after, &*result);
-            }
-            Err(err) => {
-                if ra_fixture_after.starts_with("error:") {
-                    let error_message = ra_fixture_after
-                        .chars()
-                        .into_iter()
-                        .skip("error:".len())
-                        .collect::<String>();
-                    assert_eq!(error_message.trim(), err.to_string());
-                    return;
-                } else {
-                    panic!("Rename to '{}' failed unexpectedly: {}", new_name, err)
+        let source_change = analysis.rename(position, new_name).unwrap();
+        let mut text_edit_builder = TextEdit::builder();
+        let mut file_id: Option<FileId> = None;
+        if let Some(change) = source_change {
+            for edit in change.info.source_file_edits {
+                file_id = Some(edit.file_id);
+                for indel in edit.edit.into_iter() {
+                    text_edit_builder.replace(indel.delete, indel.insert);
                 }
             }
-        };
+        }
+        let mut result = analysis.file_text(file_id.unwrap()).unwrap().to_string();
+        text_edit_builder.finish().apply(&mut result);
+        assert_eq_text!(ra_fixture_after, &*result);
     }
 
     fn check_expect(new_name: &str, ra_fixture: &str, expect: Expect) {
         let (analysis, position) = fixture::position(ra_fixture);
-        let source_change = analysis
-            .rename(position, new_name)
-            .unwrap()
-            .expect("Expect returned RangeInfo to be Some, but was None");
+        let source_change = analysis.rename(position, new_name).unwrap().unwrap();
         expect.assert_debug_eq(&source_change)
     }
 
@@ -368,30 +313,11 @@ mod tests {
     }
 
     #[test]
-    fn test_rename_to_invalid_identifier1() {
-        check(
-            "invalid!",
-            r#"fn main() { let i<|> = 1; }"#,
-            "error: Invalid name `invalid!`: not an identifier",
-        );
-    }
-
-    #[test]
-    fn test_rename_to_invalid_identifier2() {
-        check(
-            "multiple tokens",
-            r#"fn main() { let i<|> = 1; }"#,
-            "error: Invalid name `multiple tokens`: not an identifier",
-        );
-    }
-
-    #[test]
-    fn test_rename_to_invalid_identifier3() {
-        check(
-            "let",
-            r#"fn main() { let i<|> = 1; }"#,
-            "error: Invalid name `let`: not an identifier",
-        );
+    fn test_rename_to_invalid_identifier() {
+        let (analysis, position) = fixture::position(r#"fn main() { let i<|> = 1; }"#);
+        let new_name = "invalid!";
+        let source_change = analysis.rename(position, new_name).unwrap();
+        assert!(source_change.is_none());
     }
 
     #[test]
@@ -423,15 +349,6 @@ fn main() {
         );
     }
 
-    #[test]
-    fn test_rename_unresolved_reference() {
-        check(
-            "new_name",
-            r#"fn main() { let _ = unresolved_ref<|>; }"#,
-            "error: No references found at position",
-        );
-    }
-
     #[test]
     fn test_rename_for_macro_args() {
         check(
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index d9fc25d88b..6aafd6fd50 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -68,7 +68,7 @@ pub(crate) fn highlight(
     // When we leave a node, the we use it to flatten the highlighted ranges.
     let mut stack = HighlightedRangeStack::new();
 
-    let mut current_macro_call: Option<ast::MacroCall> = None;
+    let mut current_macro_call: Option<(ast::MacroCall, Option<MacroMatcherParseState>)> = None;
     let mut format_string: Option<SyntaxElement> = None;
 
     // Walk all nodes, keeping track of whether we are inside a macro or not.
@@ -92,7 +92,6 @@ pub(crate) fn highlight(
         // Track "inside macro" state
         match event.clone().map(|it| it.into_node().and_then(ast::MacroCall::cast)) {
             WalkEvent::Enter(Some(mc)) => {
-                current_macro_call = Some(mc.clone());
                 if let Some(range) = macro_call_range(&mc) {
                     stack.add(HighlightedRange {
                         range,
@@ -100,7 +99,9 @@ pub(crate) fn highlight(
                         binding_hash: None,
                     });
                 }
+                let mut is_macro_rules = None;
                 if let Some(name) = mc.is_macro_rules() {
+                    is_macro_rules = Some(MacroMatcherParseState::new());
                     if let Some((highlight, binding_hash)) = highlight_element(
                         &sema,
                         &mut bindings_shadow_count,
@@ -114,10 +115,11 @@ pub(crate) fn highlight(
                         });
                     }
                 }
+                current_macro_call = Some((mc.clone(), is_macro_rules));
                 continue;
             }
             WalkEvent::Leave(Some(mc)) => {
-                assert!(current_macro_call == Some(mc));
+                assert!(current_macro_call.map(|it| it.0) == Some(mc));
                 current_macro_call = None;
                 format_string = None;
             }
@@ -146,6 +148,20 @@ pub(crate) fn highlight(
             WalkEvent::Leave(_) => continue,
         };
 
+        // check if in matcher part of a macro_rules rule
+        if let Some((_, Some(ref mut state))) = current_macro_call {
+            if let Some(tok) = element.as_token() {
+                if matches!(
+                    update_macro_rules_state(tok, state),
+                    RuleState::Matcher | RuleState::Expander
+                ) {
+                    if skip_metavariables(element.clone()) {
+                        continue;
+                    }
+                }
+            }
+        }
+
         let range = element.text_range();
 
         let element_to_highlight = if current_macro_call.is_some() && element.kind() != COMMENT {
@@ -918,3 +934,99 @@ fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics<RootDatabas
         _ => default.into(),
     }
 }
+
+struct MacroMatcherParseState {
+    /// Opening and corresponding closing bracket of the matcher or expander of the current rule
+    paren_ty: Option<(SyntaxKind, SyntaxKind)>,
+    paren_level: usize,
+    rule_state: RuleState,
+    /// Whether we are inside the outer `{` `}` macro block that holds the rules
+    in_invoc_body: bool,
+}
+
+impl MacroMatcherParseState {
+    fn new() -> Self {
+        MacroMatcherParseState {
+            paren_ty: None,
+            paren_level: 0,
+            in_invoc_body: false,
+            rule_state: RuleState::None,
+        }
+    }
+}
+
+#[derive(Copy, Clone, PartialEq)]
+enum RuleState {
+    Matcher,
+    Expander,
+    Between,
+    None,
+}
+
+impl RuleState {
+    fn transition(&mut self) {
+        *self = match self {
+            RuleState::Matcher => RuleState::Between,
+            RuleState::Expander => RuleState::None,
+            RuleState::Between => RuleState::Expander,
+            RuleState::None => RuleState::Matcher,
+        };
+    }
+}
+
+fn update_macro_rules_state(tok: &SyntaxToken, state: &mut MacroMatcherParseState) -> RuleState {
+    if !state.in_invoc_body {
+        if tok.kind() == T!['{'] {
+            state.in_invoc_body = true;
+        }
+        return state.rule_state;
+    }
+
+    match state.paren_ty {
+        Some((open, close)) => {
+            if tok.kind() == open {
+                state.paren_level += 1;
+            } else if tok.kind() == close {
+                state.paren_level -= 1;
+                if state.paren_level == 0 {
+                    let res = state.rule_state;
+                    state.rule_state.transition();
+                    state.paren_ty = None;
+                    return res;
+                }
+            }
+        }
+        None => {
+            match tok.kind() {
+                T!['('] => {
+                    state.paren_ty = Some((T!['('], T![')']));
+                }
+                T!['{'] => {
+                    state.paren_ty = Some((T!['{'], T!['}']));
+                }
+                T!['['] => {
+                    state.paren_ty = Some((T!['['], T![']']));
+                }
+                _ => (),
+            }
+            if state.paren_ty.is_some() {
+                state.paren_level = 1;
+                state.rule_state.transition();
+            }
+        }
+    }
+    state.rule_state
+}
+
+fn skip_metavariables(element: SyntaxElement) -> bool {
+    let tok = match element.as_token() {
+        Some(tok) => tok,
+        None => return false,
+    };
+    let is_fragment = || tok.prev_token().map(|tok| tok.kind()) == Some(T![$]);
+    match tok.kind() {
+        IDENT if is_fragment() => true,
+        kind if kind.is_keyword() && is_fragment() => true,
+        _ => false,
+    }
+}
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 4e3340b0dd..468655f9c3 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -646,9 +646,14 @@ pub(crate) fn handle_prepare_rename(
     let _p = profile::span("handle_prepare_rename");
     let position = from_proto::file_position(&snap, params)?;
 
-    let change = snap.analysis.rename(position, "dummy")??;
+    let optional_change = snap.analysis.rename(position, "dummy")?;
+    let range = match optional_change {
+        None => return Ok(None),
+        Some(it) => it.range,
+    };
+
     let line_index = snap.analysis.file_line_index(position.file_id)?;
-    let range = to_proto::range(&line_index, change.range);
+    let range = to_proto::range(&line_index, range);
     Ok(Some(PrepareRenameResponse::Range(range)))
 }
 
@@ -667,8 +672,12 @@ pub(crate) fn handle_rename(
         .into());
     }
 
-    let change = snap.analysis.rename(position, &*params.new_name)??;
-    let workspace_edit = to_proto::workspace_edit(&snap, change.info)?;
+    let optional_change = snap.analysis.rename(position, &*params.new_name)?;
+    let source_change = match optional_change {
+        None => return Ok(None),
+        Some(it) => it.info,
+    };
+    let workspace_edit = to_proto::workspace_edit(&snap, source_change)?;
     Ok(Some(workspace_edit))
 }
 


Test patch:
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 1b681b2c6b..43f1b32fd7 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
@@ -37,7 +37,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 </style>
 <pre><code><span class="macro">macro_rules!</span> <span class="macro declaration">println</span> <span class="punctuation">{</span>
     <span class="punctuation">(</span><span class="punctuation">$</span><span class="punctuation">(</span><span class="punctuation">$</span>arg<span class="punctuation">:</span>tt<span class="punctuation">)</span><span class="punctuation">*</span><span class="punctuation">)</span> <span class="operator">=</span><span class="punctuation">&gt;</span> <span class="punctuation">(</span><span class="punctuation">{</span>
-        <span class="punctuation">$</span><span class="keyword">crate</span><span class="punctuation">:</span><span class="punctuation">:</span>io<span class="punctuation">:</span><span class="punctuation">:</span>_print<span class="punctuation">(</span><span class="punctuation">$</span><span class="keyword">crate</span><span class="punctuation">:</span><span class="punctuation">:</span>format_args_nl<span class="punctuation">!</span><span class="punctuation">(</span><span class="punctuation">$</span><span class="punctuation">(</span><span class="punctuation">$</span>arg<span class="punctuation">)</span><span class="punctuation">*</span><span class="punctuation">)</span><span class="punctuation">)</span><span class="punctuation">;</span>
+        <span class="punctuation">$</span>crate<span class="punctuation">:</span><span class="punctuation">:</span>io<span class="punctuation">:</span><span class="punctuation">:</span>_print<span class="punctuation">(</span><span class="punctuation">$</span>crate<span class="punctuation">:</span><span class="punctuation">:</span>format_args_nl<span class="punctuation">!</span><span class="punctuation">(</span><span class="punctuation">$</span><span class="punctuation">(</span><span class="punctuation">$</span>arg<span class="punctuation">)</span><span class="punctuation">*</span><span class="punctuation">)</span><span class="punctuation">)</span><span class="punctuation">;</span>
     <span class="punctuation">}</span><span class="punctuation">)</span>
 <span class="punctuation">}</span>
 #[rustc_builtin_macro]
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
index 1d8a3c4046..4f47e1be9d 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
@@ -115,6 +115,10 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="punctuation">}</span>
 <span class="punctuation">}</span>
 
+<span class="macro">macro_rules!</span> <span class="macro declaration">keyword_frag</span> <span class="punctuation">{</span>
+    <span class="punctuation">(</span><span class="punctuation">$</span>type<span class="punctuation">:</span>ty<span class="punctuation">)</span> <span class="operator">=</span><span class="punctuation">&gt;</span> <span class="punctuation">(</span><span class="punctuation">$</span>type<span class="punctuation">)</span>
+<span class="punctuation">}</span>
+
 <span class="comment">// comment</span>
 <span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span>
     <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello, {}!"</span><span class="punctuation">,</span> <span class="numeric_literal">92</span><span class="punctuation">)</span><span class="punctuation">;</span>
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index 694c4b7fa1..126363b8be 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -89,6 +89,10 @@ macro_rules! noop {
     }
 }
 
+macro_rules! keyword_frag {
+    ($type:ty) => ($type)
+}
+
 // comment
 fn main() {
     println!("Hello, {}!", 92);