Repository: rust-analyzer/rust-analyzer
Pull request #3425 (2020-03-03 17:05:51)

Fix a bug for single dollar sign macro
This PR fixed a bug to allow the following valid `macro_rules!` :

```rust
macro_rules! m {
    ($) => ($)
}
```


Patch:
diff --git a/crates/ra_assists/src/handlers/inline_local_variable.rs b/crates/ra_assists/src/handlers/inline_local_variable.rs
index eb5112343c..53a72309b1 100644
--- a/crates/ra_assists/src/handlers/inline_local_variable.rs
+++ b/crates/ra_assists/src/handlers/inline_local_variable.rs
@@ -2,9 +2,9 @@ use ra_syntax::{
     ast::{self, AstNode, AstToken},
     TextRange,
 };
-use test_utils::tested_by;
 
-use crate::{assist_ctx::ActionBuilder, Assist, AssistCtx, AssistId};
+use crate::assist_ctx::ActionBuilder;
+use crate::{Assist, AssistCtx, AssistId};
 
 // Assist: inline_local_variable
 //
@@ -29,11 +29,6 @@ pub(crate) fn inline_local_variable(ctx: AssistCtx) -> Option<Assist> {
         _ => return None,
     };
     if bind_pat.is_mutable() {
-        tested_by!(test_not_inline_mut_variable);
-        return None;
-    }
-    if !bind_pat.syntax().text_range().contains_inclusive(ctx.frange.range.start()) {
-        tested_by!(not_applicable_outside_of_bind_pat);
         return None;
     }
     let initializer_expr = let_stmt.initializer()?;
@@ -116,8 +111,6 @@ pub(crate) fn inline_local_variable(ctx: AssistCtx) -> Option<Assist> {
 
 #[cfg(test)]
 mod tests {
-    use test_utils::covers;
-
     use crate::helpers::{check_assist, check_assist_not_applicable};
 
     use super::*;
@@ -126,7 +119,7 @@ mod tests {
     fn test_inline_let_bind_literal_expr() {
         check_assist(
             inline_local_variable,
-            r"
+            "
 fn bar(a: usize) {}
 fn foo() {
     let a<|> = 1;
@@ -140,7 +133,7 @@ fn foo() {
     let b = a * 10;
     bar(a);
 }",
-            r"
+            "
 fn bar(a: usize) {}
 fn foo() {
     <|>1 + 1;
@@ -160,7 +153,7 @@ fn foo() {
     fn test_inline_let_bind_bin_expr() {
         check_assist(
             inline_local_variable,
-            r"
+            "
 fn bar(a: usize) {}
 fn foo() {
     let a<|> = 1 + 1;
@@ -174,7 +167,7 @@ fn foo() {
     let b = a * 10;
     bar(a);
 }",
-            r"
+            "
 fn bar(a: usize) {}
 fn foo() {
     <|>(1 + 1) + 1;
@@ -194,7 +187,7 @@ fn foo() {
     fn test_inline_let_bind_function_call_expr() {
         check_assist(
             inline_local_variable,
-            r"
+            "
 fn bar(a: usize) {}
 fn foo() {
     let a<|> = bar(1);
@@ -208,7 +201,7 @@ fn foo() {
     let b = a * 10;
     bar(a);
 }",
-            r"
+            "
 fn bar(a: usize) {}
 fn foo() {
     <|>bar(1) + 1;
@@ -228,7 +221,7 @@ fn foo() {
     fn test_inline_let_bind_cast_expr() {
         check_assist(
             inline_local_variable,
-            r"
+            "
 fn bar(a: usize): usize { a }
 fn foo() {
     let a<|> = bar(1) as u64;
@@ -242,7 +235,7 @@ fn foo() {
     let b = a * 10;
     bar(a);
 }",
-            r"
+            "
 fn bar(a: usize): usize { a }
 fn foo() {
     <|>(bar(1) as u64) + 1;
@@ -262,7 +255,7 @@ fn foo() {
     fn test_inline_let_bind_block_expr() {
         check_assist(
             inline_local_variable,
-            r"
+            "
 fn foo() {
     let a<|> = { 10 + 1 };
     a + 1;
@@ -275,7 +268,7 @@ fn foo() {
     let b = a * 10;
     bar(a);
 }",
-            r"
+            "
 fn foo() {
     <|>{ 10 + 1 } + 1;
     if { 10 + 1 } > 10 {
@@ -294,7 +287,7 @@ fn foo() {
     fn test_inline_let_bind_paren_expr() {
         check_assist(
             inline_local_variable,
-            r"
+            "
 fn foo() {
     let a<|> = ( 10 + 1 );
     a + 1;
@@ -307,7 +300,7 @@ fn foo() {
     let b = a * 10;
     bar(a);
 }",
-            r"
+            "
 fn foo() {
     <|>( 10 + 1 ) + 1;
     if ( 10 + 1 ) > 10 {
@@ -324,10 +317,9 @@ fn foo() {
 
     #[test]
     fn test_not_inline_mut_variable() {
-        covers!(test_not_inline_mut_variable);
         check_assist_not_applicable(
             inline_local_variable,
-            r"
+            "
 fn foo() {
     let mut a<|> = 1 + 1;
     a + 1;
@@ -339,13 +331,13 @@ fn foo() {
     fn test_call_expr() {
         check_assist(
             inline_local_variable,
-            r"
+            "
 fn foo() {
     let a<|> = bar(10 + 1);
     let b = a * 10;
     let c = a as usize;
 }",
-            r"
+            "
 fn foo() {
     <|>let b = bar(10 + 1) * 10;
     let c = bar(10 + 1) as usize;
@@ -357,14 +349,14 @@ fn foo() {
     fn test_index_expr() {
         check_assist(
             inline_local_variable,
-            r"
+            "
 fn foo() {
     let x = vec![1, 2, 3];
     let a<|> = x[0];
     let b = a * 10;
     let c = a as usize;
 }",
-            r"
+            "
 fn foo() {
     let x = vec![1, 2, 3];
     <|>let b = x[0] * 10;
@@ -377,14 +369,14 @@ fn foo() {
     fn test_method_call_expr() {
         check_assist(
             inline_local_variable,
-            r"
+            "
 fn foo() {
     let bar = vec![1];
     let a<|> = bar.len();
     let b = a * 10;
     let c = a as usize;
 }",
-            r"
+            "
 fn foo() {
     let bar = vec![1];
     <|>let b = bar.len() * 10;
@@ -397,7 +389,7 @@ fn foo() {
     fn test_field_expr() {
         check_assist(
             inline_local_variable,
-            r"
+            "
 struct Bar {
     foo: usize
 }
@@ -408,7 +400,7 @@ fn foo() {
     let b = a * 10;
     let c = a as usize;
 }",
-            r"
+            "
 struct Bar {
     foo: usize
 }
@@ -425,7 +417,7 @@ fn foo() {
     fn test_try_expr() {
         check_assist(
             inline_local_variable,
-            r"
+            "
 fn foo() -> Option<usize> {
     let bar = Some(1);
     let a<|> = bar?;
@@ -433,7 +425,7 @@ fn foo() -> Option<usize> {
     let c = a as usize;
     None
 }",
-            r"
+            "
 fn foo() -> Option<usize> {
     let bar = Some(1);
     <|>let b = bar? * 10;
@@ -447,13 +439,13 @@ fn foo() -> Option<usize> {
     fn test_ref_expr() {
         check_assist(
             inline_local_variable,
-            r"
+            "
 fn foo() {
     let bar = 10;
     let a<|> = &bar;
     let b = a * 10;
 }",
-            r"
+            "
 fn foo() {
     let bar = 10;
     <|>let b = &bar * 10;
@@ -465,12 +457,12 @@ fn foo() {
     fn test_tuple_expr() {
         check_assist(
             inline_local_variable,
-            r"
+            "
 fn foo() {
     let a<|> = (10, 20);
     let b = a[0];
 }",
-            r"
+            "
 fn foo() {
     <|>let b = (10, 20)[0];
 }",
@@ -481,12 +473,12 @@ fn foo() {
     fn test_array_expr() {
         check_assist(
             inline_local_variable,
-            r"
+            "
 fn foo() {
     let a<|> = [1, 2, 3];
     let b = a.len();
 }",
-            r"
+            "
 fn foo() {
     <|>let b = [1, 2, 3].len();
 }",
@@ -497,13 +489,13 @@ fn foo() {
     fn test_paren() {
         check_assist(
             inline_local_variable,
-            r"
+            "
 fn foo() {
     let a<|> = (10 + 20);
     let b = a * 10;
     let c = a as usize;
 }",
-            r"
+            "
 fn foo() {
     <|>let b = (10 + 20) * 10;
     let c = (10 + 20) as usize;
@@ -515,14 +507,14 @@ fn foo() {
     fn test_path_expr() {
         check_assist(
             inline_local_variable,
-            r"
+            "
 fn foo() {
     let d = 10;
     let a<|> = d;
     let b = a * 10;
     let c = a as usize;
 }",
-            r"
+            "
 fn foo() {
     let d = 10;
     <|>let b = d * 10;
@@ -535,13 +527,13 @@ fn foo() {
     fn test_block_expr() {
         check_assist(
             inline_local_variable,
-            r"
+            "
 fn foo() {
     let a<|> = { 10 };
     let b = a * 10;
     let c = a as usize;
 }",
-            r"
+            "
 fn foo() {
     <|>let b = { 10 } * 10;
     let c = { 10 } as usize;
@@ -553,7 +545,7 @@ fn foo() {
     fn test_used_in_different_expr1() {
         check_assist(
             inline_local_variable,
-            r"
+            "
 fn foo() {
     let a<|> = 10 + 20;
     let b = a * 10;
@@ -561,7 +553,7 @@ fn foo() {
     let d = [a, 10];
     let e = (a);
 }",
-            r"
+            "
 fn foo() {
     <|>let b = (10 + 20) * 10;
     let c = (10 + 20, 20);
@@ -575,12 +567,12 @@ fn foo() {
     fn test_used_in_for_expr() {
         check_assist(
             inline_local_variable,
-            r"
+            "
 fn foo() {
     let a<|> = vec![10, 20];
     for i in a {}
 }",
-            r"
+            "
 fn foo() {
     <|>for i in vec![10, 20] {}
 }",
@@ -591,12 +583,12 @@ fn foo() {
     fn test_used_in_while_expr() {
         check_assist(
             inline_local_variable,
-            r"
+            "
 fn foo() {
     let a<|> = 1 > 0;
     while a {}
 }",
-            r"
+            "
 fn foo() {
     <|>while 1 > 0 {}
 }",
@@ -607,14 +599,14 @@ fn foo() {
     fn test_used_in_break_expr() {
         check_assist(
             inline_local_variable,
-            r"
+            "
 fn foo() {
     let a<|> = 1 + 1;
     loop {
         break a;
     }
 }",
-            r"
+            "
 fn foo() {
     <|>loop {
         break 1 + 1;
@@ -627,12 +619,12 @@ fn foo() {
     fn test_used_in_return_expr() {
         check_assist(
             inline_local_variable,
-            r"
+            "
 fn foo() {
     let a<|> = 1 > 0;
     return a;
 }",
-            r"
+            "
 fn foo() {
     <|>return 1 > 0;
 }",
@@ -643,12 +635,12 @@ fn foo() {
     fn test_used_in_match_expr() {
         check_assist(
             inline_local_variable,
-            r"
+            "
 fn foo() {
     let a<|> = 1 > 0;
     match a {}
 }",
-            r"
+            "
 fn foo() {
     <|>match 1 > 0 {}
 }",
@@ -659,25 +651,11 @@ fn foo() {
     fn test_not_applicable_if_variable_unused() {
         check_assist_not_applicable(
             inline_local_variable,
-            r"
+            "
 fn foo() {
     let <|>a = 0;
 }
             ",
         )
     }
-
-    #[test]
-    fn not_applicable_outside_of_bind_pat() {
-        covers!(not_applicable_outside_of_bind_pat);
-        check_assist_not_applicable(
-            inline_local_variable,
-            r"
-fn main() {
-    let x = <|>1 + 2;
-    x * 4;
-}
-",
-        )
-    }
 }
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index 50a15f9786..deeada2de5 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -178,23 +178,19 @@ mod helpers {
         (db, file_id)
     }
 
-    pub(crate) fn check_assist(
-        assist: AssistHandler,
-        ra_fixture_before: &str,
-        ra_fixture_after: &str,
-    ) {
-        check(assist, ra_fixture_before, ExpectedResult::After(ra_fixture_after));
+    pub(crate) fn check_assist(assist: AssistHandler, before: &str, after: &str) {
+        check(assist, before, ExpectedResult::After(after));
     }
 
     // FIXME: instead of having a separate function here, maybe use
     // `extract_ranges` and mark the target as `<target> </target>` in the
     // fixuture?
-    pub(crate) fn check_assist_target(assist: AssistHandler, ra_fixture: &str, target: &str) {
-        check(assist, ra_fixture, ExpectedResult::Target(target));
+    pub(crate) fn check_assist_target(assist: AssistHandler, before: &str, target: &str) {
+        check(assist, before, ExpectedResult::Target(target));
     }
 
-    pub(crate) fn check_assist_not_applicable(assist: AssistHandler, ra_fixture: &str) {
-        check(assist, ra_fixture, ExpectedResult::NotApplicable);
+    pub(crate) fn check_assist_not_applicable(assist: AssistHandler, before: &str) {
+        check(assist, before, ExpectedResult::NotApplicable);
     }
 
     enum ExpectedResult<'a> {
diff --git a/crates/ra_assists/src/marks.rs b/crates/ra_assists/src/marks.rs
index cef3df4e5a..c20e4db9e9 100644
--- a/crates/ra_assists/src/marks.rs
+++ b/crates/ra_assists/src/marks.rs
@@ -4,6 +4,4 @@ test_utils::marks!(
     introduce_var_in_comment_is_not_applicable
     test_introduce_var_expr_stmt
     test_introduce_var_last_expr
-    not_applicable_outside_of_bind_pat
-    test_not_inline_mut_variable
 );
diff --git a/crates/ra_hir_expand/src/db.rs b/crates/ra_hir_expand/src/db.rs
index af5b22d1c6..70d9692383 100644
--- a/crates/ra_hir_expand/src/db.rs
+++ b/crates/ra_hir_expand/src/db.rs
@@ -86,10 +86,13 @@ pub(crate) fn macro_def(
                 log::warn!("fail on macro_def to token tree: {:#?}", arg);
                 None
             })?;
-            let rules = MacroRules::parse(&tt).ok().or_else(|| {
-                log::warn!("fail on macro_def parse: {:#?}", tt);
-                None
-            })?;
+            let rules = match MacroRules::parse(&tt) {
+                Ok(it) => it,
+                Err(err) => {
+                    log::warn!("fail on macro_def parse: error: {:#?} {:#?}", err, tt);
+                    return None;
+                }
+            };
             Some(Arc::new((TokenExpander::MacroRules(rules), tmap)))
         }
         MacroDefKind::BuiltIn(expander) => {
@@ -150,7 +153,9 @@ pub(crate) fn parse_macro(
             // Note:
             // The final goal we would like to make all parse_macro success,
             // such that the following log will not call anyway.
-            log::warn!("fail on macro_parse: (reason: {})", err,);
+            let loc: MacroCallLoc = db.lookup_intern_macro(macro_call_id);
+            let node = loc.kind.node(db);
+            log::warn!("fail on macro_parse: (reason: {} macro_call: {:#})", err, node.value);
         })
         .ok()?;
 
diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs
index 9b0785e1d8..2eb047c968 100644
--- a/crates/ra_ide/src/references.rs
+++ b/crates/ra_ide/src/references.rs
@@ -114,7 +114,6 @@ pub(crate) fn find_all_refs(
     position: FilePosition,
     search_scope: Option<SearchScope>,
 ) -> Option<RangeInfo<ReferenceSearchResult>> {
-    let _p = profile("find_all_refs");
     let sema = Semantics::new(db);
     let syntax = sema.parse(position.file_id).syntax().clone();
 
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index 7e41db5301..b89501aff6 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -219,10 +219,10 @@ fn highlight_element(
         CHAR => HighlightTag::CharLiteral.into(),
         LIFETIME => {
             let h = Highlight::new(HighlightTag::Lifetime);
-            match element.parent().map(|it| it.kind()) {
+            dbg!(match element.parent().map(|it| it.kind()) {
                 Some(LIFETIME_PARAM) | Some(LABEL) => h | HighlightModifier::Definition,
                 _ => h,
-            }
+            })
         }
 
         k if k.is_keyword() => {
@@ -320,7 +320,7 @@ fn highlight_injection(
     let call_info = call_info_for_token(&sema, expanded)?;
     let idx = call_info.active_parameter?;
     let name = call_info.signature.parameter_names.get(idx)?;
-    if !name.starts_with("ra_fixture") {
+    if name != "ra_fixture" {
         return None;
     }
     let value = literal.value()?;
diff --git a/crates/ra_mbe/src/parser.rs b/crates/ra_mbe/src/parser.rs
index 10a6f300a7..0341504326 100644
--- a/crates/ra_mbe/src/parser.rs
+++ b/crates/ra_mbe/src/parser.rs
@@ -90,7 +90,11 @@ fn next_op<'a>(
 ) -> Result<Op<'a>, ExpandError> {
     let res = match first {
         tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: '$', .. })) => {
-            let second = src.next().ok_or_else(|| err!("bad var 1"))?;
+            // Note that the '$' itself is a valid token inside macro_rules.
+            let second = match src.next() {
+                None => return Ok(Op::TokenTree(first)),
+                Some(it) => it,
+            };
             match second {
                 tt::TokenTree::Subtree(subtree) => {
                     let (separator, kind) = parse_repeat(src)?;


Test patch:
diff --git a/crates/ra_mbe/src/tests.rs b/crates/ra_mbe/src/tests.rs
index cb228702fb..1dba829154 100644
--- a/crates/ra_mbe/src/tests.rs
+++ b/crates/ra_mbe/src/tests.rs
@@ -23,6 +23,7 @@ mod rule_parsing {
         check("($($i:ident)*) => ($_)");
         check("($($true:ident)*) => ($true)");
         check("($($false:ident)*) => ($false)");
+        check("($) => ($)");
     }
 
     #[test]