Repository: rust-analyzer/rust-analyzer
Pull request #3559 (2020-03-11 15:17:09)

Implement builtin assert! macro
This PR add a dummy implementation for `assert!` macro, which mainly make `hover` and `goto-def` works on arguments inside it.


Patch:
diff --git a/crates/ra_hir_expand/src/builtin_macro.rs b/crates/ra_hir_expand/src/builtin_macro.rs
index a90007f264..f9d3787f63 100644
--- a/crates/ra_hir_expand/src/builtin_macro.rs
+++ b/crates/ra_hir_expand/src/builtin_macro.rs
@@ -88,6 +88,7 @@ register_builtin! {
     (compile_error, CompileError) => compile_error_expand,
     (file, File) => file_expand,
     (line, Line) => line_expand,
+    (assert, Assert) => assert_expand,
     (stringify, Stringify) => stringify_expand,
     (format_args, FormatArgs) => format_args_expand,
     // format_args_nl only differs in that it adds a newline in the end,
@@ -151,6 +152,45 @@ fn column_expand(
     Ok(expanded)
 }
 
+fn assert_expand(
+    _db: &dyn AstDatabase,
+    _id: LazyMacroId,
+    tt: &tt::Subtree,
+) -> Result<tt::Subtree, mbe::ExpandError> {
+    // A hacky implementation for goto def and hover
+    // We expand `assert!(cond, arg1, arg2)` to
+    // ```
+    // {(cond, &(arg1), &(arg2));}
+    // ```,
+    // which is wrong but useful.
+
+    let mut args = Vec::new();
+    let mut current = Vec::new();
+    for tt in tt.token_trees.iter().cloned() {
+        match tt {
+            tt::TokenTree::Leaf(tt::Leaf::Punct(p)) if p.char == ',' => {
+                args.push(current);
+                current = Vec::new();
+            }
+            _ => {
+                current.push(tt);
+            }
+        }
+    }
+    if !current.is_empty() {
+        args.push(current);
+    }
+
+    let arg_tts = args.into_iter().flat_map(|arg| {
+        quote! { &(##arg), }
+    }.token_trees).collect::<Vec<_>>();
+
+    let expanded = quote! {
+        { { (##arg_tts); } }
+    };
+    Ok(expanded)
+}
+
 fn file_expand(
     _db: &dyn AstDatabase,
     _id: LazyMacroId,
@@ -493,6 +533,22 @@ mod tests {
         assert_eq!(expanded, "\"\"");
     }
 
+    #[test]
+    fn test_assert_expand() {
+        let expanded = expand_builtin_macro(
+            r#"
+            #[rustc_builtin_macro]
+            macro_rules! assert {
+                ($cond:expr) => ({ /* compiler built-in */ });
+                ($cond:expr, $($args:tt)*) => ({ /* compiler built-in */ })
+            }
+            assert!(true, "{} {:?}", arg1(a, b, c), arg2);
+            "#,
+        );
+
+        assert_eq!(expanded, "{{(&(true), &(\"{} {:?}\"), &(arg1(a,b,c)), &(arg2),);}}");
+    }
+
     #[test]
     fn test_compile_error_expand() {
         let expanded = expand_builtin_macro(
diff --git a/crates/ra_hir_expand/src/name.rs b/crates/ra_hir_expand/src/name.rs
index 6d201256f1..25cc1e9fce 100644
--- a/crates/ra_hir_expand/src/name.rs
+++ b/crates/ra_hir_expand/src/name.rs
@@ -172,6 +172,7 @@ pub mod known {
         column,
         compile_error,
         line,
+        assert,
         stringify,
         concat,
         include,
diff --git a/crates/ra_hir_expand/src/quote.rs b/crates/ra_hir_expand/src/quote.rs
index 57e7eebf9e..3fd4233da8 100644
--- a/crates/ra_hir_expand/src/quote.rs
+++ b/crates/ra_hir_expand/src/quote.rs
@@ -99,6 +99,7 @@ macro_rules! __quote {
     ( & ) => {$crate::__quote!(@PUNCT '&')};
     ( , ) => {$crate::__quote!(@PUNCT ',')};
     ( : ) => {$crate::__quote!(@PUNCT ':')};
+    ( ; ) => {$crate::__quote!(@PUNCT ';')};
     ( :: ) => {$crate::__quote!(@PUNCT ':', ':')};
     ( . ) => {$crate::__quote!(@PUNCT '.')};
     ( < ) => {$crate::__quote!(@PUNCT '<')};
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs
index 25e038a55d..0bbba48555 100644
--- a/crates/ra_ide/src/hover.rs
+++ b/crates/ra_ide/src/hover.rs
@@ -832,20 +832,35 @@ fn func(foo: i32) { if true { <|>foo; }; }
         assert_eq!(hover_on, "\"Tracks\"");
     }
 
+    #[test]
+    fn test_hover_through_assert_macro() {
+        let hover_on = check_hover_result(
+            r#"
+            //- /lib.rs
+            #[rustc_builtin_macro]
+            macro_rules! assert {}
+
+            fn bar() -> bool { true }
+            fn foo() {
+                assert!(ba<|>r());
+            }
+            "#,
+            &["fn bar() -> bool"],
+        );
+
+        assert_eq!(hover_on, "bar");
+    }
+
     #[test]
     fn test_hover_through_literal_string_in_builtin_macro() {
         check_hover_no_result(
             r#"
             //- /lib.rs
             #[rustc_builtin_macro]
-            macro_rules! assert {
-                ($cond:expr) => {{ /* compiler built-in */ }};
-                ($cond:expr,) => {{ /* compiler built-in */ }};
-                ($cond:expr, $($arg:tt)+) => {{ /* compiler built-in */ }};
-            }
+            macro_rules! format {}
 
             fn foo() {
-                assert!("hel<|>lo");
+                format!("hel<|>lo {}", 0);
             }
             "#,
         );
diff --git a/crates/ra_parser/src/grammar/expressions.rs b/crates/ra_parser/src/grammar/expressions.rs
index 0c170ac5ec..4163a2cf51 100644
--- a/crates/ra_parser/src/grammar/expressions.rs
+++ b/crates/ra_parser/src/grammar/expressions.rs
@@ -278,7 +278,7 @@ fn current_op(p: &Parser) -> (u8, SyntaxKind) {
 }
 
 // Parses expression with binding power of at least bp.
-fn expr_bp(p: &mut Parser, mut r: Restrictions, bp: u8) -> (Option<CompletedMarker>, BlockLike) {
+fn expr_bp(p: &mut Parser, r: Restrictions, bp: u8) -> (Option<CompletedMarker>, BlockLike) {
     let mut lhs = match lhs(p, r) {
         Some((lhs, blocklike)) => {
             // test stmt_bin_expr_ambiguity
@@ -311,12 +311,6 @@ fn expr_bp(p: &mut Parser, mut r: Restrictions, bp: u8) -> (Option<CompletedMark
         let m = lhs.precede(p);
         p.bump(op);
 
-        // test binop_resets_statementness
-        // fn foo() {
-        //     v = {1}&2;
-        // }
-        r = Restrictions { prefer_stmt: false, ..r };
-
         if is_range {
             // test postfix_range
             // fn foo() {
@@ -333,7 +327,7 @@ fn expr_bp(p: &mut Parser, mut r: Restrictions, bp: u8) -> (Option<CompletedMark
             }
         }
 
-        expr_bp(p, Restrictions { prefer_stmt: false, ..r }, op_bp + 1);
+        expr_bp(p, r, op_bp + 1);
         lhs = m.complete(p, if is_range { RANGE_EXPR } else { BIN_EXPR });
     }
     (Some(lhs), BlockLike::NotBlock)


Test patch:
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0158_binop_resets_statementness.rs b/crates/ra_syntax/test_data/parser/inline/ok/0158_binop_resets_statementness.rs
deleted file mode 100644
index 05acc30f12..0000000000
--- a/crates/ra_syntax/test_data/parser/inline/ok/0158_binop_resets_statementness.rs
+++ /dev/null
@@ -1,3 +0,0 @@
-fn foo() {
-    v = {1}&2;
-}
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0158_binop_resets_statementness.txt b/crates/ra_syntax/test_data/parser/inline/ok/0158_binop_resets_statementness.txt
deleted file mode 100644
index d568a1d457..0000000000
--- a/crates/ra_syntax/test_data/parser/inline/ok/0158_binop_resets_statementness.txt
+++ /dev/null
@@ -1,38 +0,0 @@
-SOURCE_FILE@[0; 28)
-  FN_DEF@[0; 27)
-    FN_KW@[0; 2) "fn"
-    WHITESPACE@[2; 3) " "
-    NAME@[3; 6)
-      IDENT@[3; 6) "foo"
-    PARAM_LIST@[6; 8)
-      L_PAREN@[6; 7) "("
-      R_PAREN@[7; 8) ")"
-    WHITESPACE@[8; 9) " "
-    BLOCK_EXPR@[9; 27)
-      BLOCK@[9; 27)
-        L_CURLY@[9; 10) "{"
-        WHITESPACE@[10; 15) "\n    "
-        EXPR_STMT@[15; 25)
-          BIN_EXPR@[15; 24)
-            PATH_EXPR@[15; 16)
-              PATH@[15; 16)
-                PATH_SEGMENT@[15; 16)
-                  NAME_REF@[15; 16)
-                    IDENT@[15; 16) "v"
-            WHITESPACE@[16; 17) " "
-            EQ@[17; 18) "="
-            WHITESPACE@[18; 19) " "
-            BIN_EXPR@[19; 24)
-              BLOCK_EXPR@[19; 22)
-                BLOCK@[19; 22)
-                  L_CURLY@[19; 20) "{"
-                  LITERAL@[20; 21)
-                    INT_NUMBER@[20; 21) "1"
-                  R_CURLY@[21; 22) "}"
-              AMP@[22; 23) "&"
-              LITERAL@[23; 24)
-                INT_NUMBER@[23; 24) "2"
-          SEMI@[24; 25) ";"
-        WHITESPACE@[25; 26) "\n"
-        R_CURLY@[26; 27) "}"
-  WHITESPACE@[27; 28) "\n"