Repository: rust-analyzer/rust-analyzer
Pull request #8304 (2021-04-03 01:14:42)

Support the new `panic!()` macro
Includes a minor fixup to macro 2.0 parsing.

bors r+


Patch:
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index 6f946a6dd9..c8f4947078 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -1402,8 +1402,18 @@ impl ModCollector<'_, '_> {
 
         // Case 1: builtin macros
         if attrs.by_key("rustc_builtin_macro").exists() {
+            // `#[rustc_builtin_macro = "builtin_name"]` overrides the `macro_rules!` name.
+            let name;
+            let name = match attrs.by_key("rustc_builtin_macro").string_value() {
+                Some(it) => {
+                    // FIXME: a hacky way to create a Name from string.
+                    name = tt::Ident { text: it.clone(), id: tt::TokenId::unspecified() }.as_name();
+                    &name
+                }
+                None => &mac.name,
+            };
             let krate = self.def_collector.def_map.krate;
-            if let Some(macro_id) = find_builtin_macro(&mac.name, krate, ast_id) {
+            if let Some(macro_id) = find_builtin_macro(name, krate, ast_id) {
                 self.def_collector.define_macro_rules(
                     self.module_id,
                     mac.name.clone(),
diff --git a/crates/hir_expand/src/builtin_macro.rs b/crates/hir_expand/src/builtin_macro.rs
index 4d52904b94..3aa3d8997d 100644
--- a/crates/hir_expand/src/builtin_macro.rs
+++ b/crates/hir_expand/src/builtin_macro.rs
@@ -1,10 +1,10 @@
 //! Builtin macro
 use crate::{
     db::AstDatabase, name, quote, AstId, CrateId, EagerMacroId, LazyMacroId, MacroCallId,
-    MacroDefId, MacroDefKind, TextSize,
+    MacroCallLoc, MacroDefId, MacroDefKind, TextSize,
 };
 
-use base_db::{AnchoredPath, FileId};
+use base_db::{AnchoredPath, Edition, FileId};
 use cfg::CfgExpr;
 use either::Either;
 use mbe::{parse_exprs_with_sep, parse_to_token_tree, ExpandResult};
@@ -111,6 +111,8 @@ register_builtin! {
     (llvm_asm, LlvmAsm) => asm_expand,
     (asm, Asm) => asm_expand,
     (cfg, Cfg) => cfg_expand,
+    (core_panic, CorePanic) => panic_expand,
+    (std_panic, StdPanic) => panic_expand,
 
     EAGER:
     (compile_error, CompileError) => compile_error_expand,
@@ -284,6 +286,25 @@ fn cfg_expand(
     ExpandResult::ok(expanded)
 }
 
+fn panic_expand(
+    db: &dyn AstDatabase,
+    id: LazyMacroId,
+    tt: &tt::Subtree,
+) -> ExpandResult<tt::Subtree> {
+    let loc: MacroCallLoc = db.lookup_intern_macro(id);
+    // Expand to a macro call `$crate::panic::panic_{edition}`
+    let krate = tt::Ident { text: "$crate".into(), id: tt::TokenId::unspecified() };
+    let mut call = if db.crate_graph()[loc.krate].edition == Edition::Edition2021 {
+        quote!(#krate::panic::panic_2021!)
+    } else {
+        quote!(#krate::panic::panic_2015!)
+    };
+
+    // Pass the original arguments
+    call.token_trees.push(tt::TokenTree::Subtree(tt.clone()));
+    ExpandResult::ok(call)
+}
+
 fn unquote_str(lit: &tt::Literal) -> Option<String> {
     let lit = ast::make::tokens::literal(&lit.to_string());
     let token = ast::String::cast(lit)?;
diff --git a/crates/hir_expand/src/name.rs b/crates/hir_expand/src/name.rs
index 203ebbe85c..a0f8766b08 100644
--- a/crates/hir_expand/src/name.rs
+++ b/crates/hir_expand/src/name.rs
@@ -208,6 +208,8 @@ pub mod known {
         line,
         module_path,
         assert,
+        core_panic,
+        std_panic,
         stringify,
         concat,
         include,
diff --git a/crates/hir_expand/src/quote.rs b/crates/hir_expand/src/quote.rs
index 08bc5aa49f..c82487ef04 100644
--- a/crates/hir_expand/src/quote.rs
+++ b/crates/hir_expand/src/quote.rs
@@ -104,6 +104,7 @@ macro_rules! __quote {
     ( . ) => {$crate::__quote!(@PUNCT '.')};
     ( < ) => {$crate::__quote!(@PUNCT '<')};
     ( > ) => {$crate::__quote!(@PUNCT '>')};
+    ( ! ) => {$crate::__quote!(@PUNCT '!')};
 
     ( $first:tt $($tail:tt)+ ) => {
         {
diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs
index e74f8cf3f4..3af5bc18b8 100644
--- a/crates/mbe/src/lib.rs
+++ b/crates/mbe/src/lib.rs
@@ -220,9 +220,11 @@ impl MacroDef {
             while src.len() > 0 {
                 let rule = Rule::parse(&mut src, true)?;
                 rules.push(rule);
-                if let Err(()) = src.expect_char(';') {
+                if let Err(()) = src.expect_any_char(&[';', ',']) {
                     if src.len() > 0 {
-                        return Err(ParseError::Expected("expected `;`".to_string()));
+                        return Err(ParseError::Expected(
+                            "expected `;` or `,` to delimit rules".to_string(),
+                        ));
                     }
                     break;
                 }
diff --git a/crates/mbe/src/tt_iter.rs b/crates/mbe/src/tt_iter.rs
index 319a40f2aa..195b8cf30b 100644
--- a/crates/mbe/src/tt_iter.rs
+++ b/crates/mbe/src/tt_iter.rs
@@ -34,6 +34,17 @@ impl<'a> TtIter<'a> {
         }
     }
 
+    pub(crate) fn expect_any_char(&mut self, chars: &[char]) -> Result<(), ()> {
+        match self.next() {
+            Some(tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: c, .. })))
+                if chars.contains(c) =>
+            {
+                Ok(())
+            }
+            _ => Err(()),
+        }
+    }
+
     pub(crate) fn expect_subtree(&mut self) -> Result<&'a tt::Subtree, ()> {
         match self.next() {
             Some(tt::TokenTree::Subtree(it)) => Ok(it),


Test patch:
diff --git a/crates/mbe/src/tests/expand.rs b/crates/mbe/src/tests/expand.rs
index 8951f38137..e02d038b65 100644
--- a/crates/mbe/src/tests/expand.rs
+++ b/crates/mbe/src/tests/expand.rs
@@ -662,6 +662,21 @@ macro foo {
     .assert_expand_items("foo!(bar);", "fn bar () {}");
 }
 
+#[test]
+fn test_macro_2_0_panic_2015() {
+    parse_macro2(
+        r#"
+macro panic_2015 {
+    () => (
+    ),
+    (bar) => (
+    ),
+}
+"#,
+    )
+    .assert_expand_items("panic_2015!(bar);", "");
+}
+
 #[test]
 fn test_path() {
     parse_macro(