]> git.lizzy.rs Git - rust.git/commitdiff
internal: consistent module naming
authorAleksey Kladov <aleksey.kladov@gmail.com>
Sun, 10 Oct 2021 12:44:03 +0000 (15:44 +0300)
committerAleksey Kladov <aleksey.kladov@gmail.com>
Sun, 10 Oct 2021 12:44:03 +0000 (15:44 +0300)
crates/hir_def/src/macro_expansion_tests.rs
crates/hir_def/src/macro_expansion_tests/builtin.rs [deleted file]
crates/hir_def/src/macro_expansion_tests/builtin_fn_macro.rs [new file with mode: 0644]
crates/hir_def/src/nameres/collector.rs
crates/hir_expand/src/builtin_attr.rs [deleted file]
crates/hir_expand/src/builtin_attr_macro.rs [new file with mode: 0644]
crates/hir_expand/src/builtin_derive.rs [deleted file]
crates/hir_expand/src/builtin_derive_macro.rs [new file with mode: 0644]
crates/hir_expand/src/builtin_fn_macro.rs [new file with mode: 0644]
crates/hir_expand/src/builtin_macro.rs [deleted file]
crates/hir_expand/src/lib.rs

index c317dc27a58350cae02bf3dd99e8b7c1f753add2..f29d1443b921596852c39d5d9fdeea4dfec5a449 100644 (file)
@@ -10,7 +10,7 @@
 //! and harder to understand.
 
 mod mbe;
-mod builtin;
+mod builtin_fn_macro;
 
 use std::{iter, ops::Range};
 
diff --git a/crates/hir_def/src/macro_expansion_tests/builtin.rs b/crates/hir_def/src/macro_expansion_tests/builtin.rs
deleted file mode 100644 (file)
index 6982116..0000000
+++ /dev/null
@@ -1,332 +0,0 @@
-//! Tests for builtin macros (see `builtin_macro.rs` in `hir_expand`).
-
-use expect_test::expect;
-
-use crate::macro_expansion_tests::check;
-
-#[test]
-fn test_column_expand() {
-    check(
-        r#"
-#[rustc_builtin_macro]
-macro_rules! column {() => {}}
-
-fn main() { column!(); }
-"#,
-        expect![[r##"
-#[rustc_builtin_macro]
-macro_rules! column {() => {}}
-
-fn main() { 0; }
-"##]],
-    );
-}
-
-#[test]
-fn test_line_expand() {
-    check(
-        r#"
-#[rustc_builtin_macro]
-macro_rules! line {() => {}}
-
-fn main() { line!() }
-"#,
-        expect![[r##"
-#[rustc_builtin_macro]
-macro_rules! line {() => {}}
-
-fn main() { 0 }
-"##]],
-    );
-}
-
-#[test]
-fn test_stringify_expand() {
-    check(
-        r#"
-#[rustc_builtin_macro]
-macro_rules! stringify {() => {}}
-
-fn main() {
-    stringify!(
-        a
-        b
-        c
-    );
-}
-"#,
-        expect![[r##"
-#[rustc_builtin_macro]
-macro_rules! stringify {() => {}}
-
-fn main() {
-    "a b c";
-}
-"##]],
-    );
-}
-
-#[test]
-fn test_env_expand() {
-    check(
-        r#"
-#[rustc_builtin_macro]
-macro_rules! env {() => {}}
-
-fn main() { env!("TEST_ENV_VAR"); }
-"#,
-        expect![[r##"
-#[rustc_builtin_macro]
-macro_rules! env {() => {}}
-
-fn main() { "__RA_UNIMPLEMENTED__"; }
-"##]],
-    );
-}
-
-#[test]
-fn test_option_env_expand() {
-    check(
-        r#"
-#[rustc_builtin_macro]
-macro_rules! option_env {() => {}}
-
-fn main() { option_env!("TEST_ENV_VAR"); }
-"#,
-        expect![[r##"
-#[rustc_builtin_macro]
-macro_rules! option_env {() => {}}
-
-fn main() { std::option::Option::None:: < &str>; }
-"##]],
-    );
-}
-
-#[test]
-fn test_file_expand() {
-    check(
-        r#"
-#[rustc_builtin_macro]
-macro_rules! file {() => {}}
-
-fn main() { file!(); }
-"#,
-        expect![[r##"
-#[rustc_builtin_macro]
-macro_rules! file {() => {}}
-
-fn main() { ""; }
-"##]],
-    );
-}
-
-#[test]
-fn test_assert_expand() {
-    check(
-        r#"
-#[rustc_builtin_macro]
-macro_rules! assert {
-    ($cond:expr) => ({ /* compiler built-in */ });
-    ($cond:expr, $($args:tt)*) => ({ /* compiler built-in */ })
-}
-
-fn main() {
-    assert!(true, "{} {:?}", arg1(a, b, c), arg2);
-}
-"#,
-        expect![[r##"
-#[rustc_builtin_macro]
-macro_rules! assert {
-    ($cond:expr) => ({ /* compiler built-in */ });
-    ($cond:expr, $($args:tt)*) => ({ /* compiler built-in */ })
-}
-
-fn main() {
-     {
-        if !true {
-            $crate::panic!("{} {:?}", arg1(a, b, c), arg2);
-        }
-    };
-}
-"##]],
-    );
-}
-
-#[test]
-fn test_compile_error_expand() {
-    check(
-        r#"
-#[rustc_builtin_macro]
-macro_rules! compile_error {
-    ($msg:expr) => ({ /* compiler built-in */ });
-    ($msg:expr,) => ({ /* compiler built-in */ })
-}
-
-// This expands to nothing (since it's in item position), but emits an error.
-compile_error!("error!");
-"#,
-        expect![[r##"
-#[rustc_builtin_macro]
-macro_rules! compile_error {
-    ($msg:expr) => ({ /* compiler built-in */ });
-    ($msg:expr,) => ({ /* compiler built-in */ })
-}
-
-/* error: error! */
-"##]],
-    );
-}
-
-#[test]
-fn test_format_args_expand() {
-    check(
-        r#"
-#[rustc_builtin_macro]
-macro_rules! format_args {
-    ($fmt:expr) => ({ /* compiler built-in */ });
-    ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
-}
-
-fn main() {
-    format_args!("{} {:?}", arg1(a, b, c), arg2);
-}
-"#,
-        expect![[r##"
-#[rustc_builtin_macro]
-macro_rules! format_args {
-    ($fmt:expr) => ({ /* compiler built-in */ });
-    ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
-}
-
-fn main() {
-    unsafe {
-        std::fmt::Arguments::new_v1(&[], &[std::fmt::ArgumentV1::new(&(arg1(a, b, c)), std::fmt::Display::fmt), std::fmt::ArgumentV1::new(&(arg2), std::fmt::Display::fmt), ])
-    };
-}
-"##]],
-    );
-}
-
-#[test]
-fn test_format_args_expand_with_comma_exprs() {
-    check(
-        r#"
-#[rustc_builtin_macro]
-macro_rules! format_args {
-    ($fmt:expr) => ({ /* compiler built-in */ });
-    ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
-}
-
-fn main() {
-    format_args!("{} {:?}", a::<A,B>(), b);
-}
-"#,
-        expect![[r##"
-#[rustc_builtin_macro]
-macro_rules! format_args {
-    ($fmt:expr) => ({ /* compiler built-in */ });
-    ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
-}
-
-fn main() {
-    unsafe {
-        std::fmt::Arguments::new_v1(&[], &[std::fmt::ArgumentV1::new(&(a::<A, B>()), std::fmt::Display::fmt), std::fmt::ArgumentV1::new(&(b), std::fmt::Display::fmt), ])
-    };
-}
-"##]],
-    );
-}
-
-#[test]
-fn test_format_args_expand_with_broken_member_access() {
-    check(
-        r#"
-#[rustc_builtin_macro]
-macro_rules! format_args {
-    ($fmt:expr) => ({ /* compiler built-in */ });
-    ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
-}
-
-fn main() {
-    let _ =
-        // +errors
-        format_args!("{} {:?}", a.);
-}
-"#,
-        expect![[r##"
-#[rustc_builtin_macro]
-macro_rules! format_args {
-    ($fmt:expr) => ({ /* compiler built-in */ });
-    ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
-}
-
-fn main() {
-    let _ =
-        /* parse error: expected field name or number */
-unsafe {
-            std::fmt::Arguments::new_v1(&[], &[std::fmt::ArgumentV1::new(&(a.), std::fmt::Display::fmt), ])
-        };
-}
-"##]],
-    );
-}
-
-#[test]
-fn test_include_bytes_expand() {
-    check(
-        r#"
-#[rustc_builtin_macro]
-macro_rules! include_bytes {
-    ($file:expr) => {{ /* compiler built-in */ }};
-    ($file:expr,) => {{ /* compiler built-in */ }};
-}
-
-fn main() { include_bytes("foo"); }
-"#,
-        expect![[r##"
-#[rustc_builtin_macro]
-macro_rules! include_bytes {
-    ($file:expr) => {{ /* compiler built-in */ }};
-    ($file:expr,) => {{ /* compiler built-in */ }};
-}
-
-fn main() { include_bytes("foo"); }
-"##]],
-    );
-}
-
-#[test]
-fn test_concat_expand() {
-    check(
-        r##"
-#[rustc_builtin_macro]
-macro_rules! concat {}
-
-fn main() { concat!("foo", "r", 0, r#"bar"#, "\n", false); }
-"##,
-        expect![[r##"
-#[rustc_builtin_macro]
-macro_rules! concat {}
-
-fn main() { "foor0bar\nfalse"; }
-"##]],
-    );
-}
-
-#[test]
-fn test_concat_idents_expand() {
-    check(
-        r##"
-#[rustc_builtin_macro]
-macro_rules! concat_idents {}
-
-fn main() { concat_idents!(foo, bar); }
-"##,
-        expect![[r##"
-#[rustc_builtin_macro]
-macro_rules! concat_idents {}
-
-fn main() { foobar; }
-"##]],
-    );
-}
diff --git a/crates/hir_def/src/macro_expansion_tests/builtin_fn_macro.rs b/crates/hir_def/src/macro_expansion_tests/builtin_fn_macro.rs
new file mode 100644 (file)
index 0000000..6982116
--- /dev/null
@@ -0,0 +1,332 @@
+//! Tests for builtin macros (see `builtin_macro.rs` in `hir_expand`).
+
+use expect_test::expect;
+
+use crate::macro_expansion_tests::check;
+
+#[test]
+fn test_column_expand() {
+    check(
+        r#"
+#[rustc_builtin_macro]
+macro_rules! column {() => {}}
+
+fn main() { column!(); }
+"#,
+        expect![[r##"
+#[rustc_builtin_macro]
+macro_rules! column {() => {}}
+
+fn main() { 0; }
+"##]],
+    );
+}
+
+#[test]
+fn test_line_expand() {
+    check(
+        r#"
+#[rustc_builtin_macro]
+macro_rules! line {() => {}}
+
+fn main() { line!() }
+"#,
+        expect![[r##"
+#[rustc_builtin_macro]
+macro_rules! line {() => {}}
+
+fn main() { 0 }
+"##]],
+    );
+}
+
+#[test]
+fn test_stringify_expand() {
+    check(
+        r#"
+#[rustc_builtin_macro]
+macro_rules! stringify {() => {}}
+
+fn main() {
+    stringify!(
+        a
+        b
+        c
+    );
+}
+"#,
+        expect![[r##"
+#[rustc_builtin_macro]
+macro_rules! stringify {() => {}}
+
+fn main() {
+    "a b c";
+}
+"##]],
+    );
+}
+
+#[test]
+fn test_env_expand() {
+    check(
+        r#"
+#[rustc_builtin_macro]
+macro_rules! env {() => {}}
+
+fn main() { env!("TEST_ENV_VAR"); }
+"#,
+        expect![[r##"
+#[rustc_builtin_macro]
+macro_rules! env {() => {}}
+
+fn main() { "__RA_UNIMPLEMENTED__"; }
+"##]],
+    );
+}
+
+#[test]
+fn test_option_env_expand() {
+    check(
+        r#"
+#[rustc_builtin_macro]
+macro_rules! option_env {() => {}}
+
+fn main() { option_env!("TEST_ENV_VAR"); }
+"#,
+        expect![[r##"
+#[rustc_builtin_macro]
+macro_rules! option_env {() => {}}
+
+fn main() { std::option::Option::None:: < &str>; }
+"##]],
+    );
+}
+
+#[test]
+fn test_file_expand() {
+    check(
+        r#"
+#[rustc_builtin_macro]
+macro_rules! file {() => {}}
+
+fn main() { file!(); }
+"#,
+        expect![[r##"
+#[rustc_builtin_macro]
+macro_rules! file {() => {}}
+
+fn main() { ""; }
+"##]],
+    );
+}
+
+#[test]
+fn test_assert_expand() {
+    check(
+        r#"
+#[rustc_builtin_macro]
+macro_rules! assert {
+    ($cond:expr) => ({ /* compiler built-in */ });
+    ($cond:expr, $($args:tt)*) => ({ /* compiler built-in */ })
+}
+
+fn main() {
+    assert!(true, "{} {:?}", arg1(a, b, c), arg2);
+}
+"#,
+        expect![[r##"
+#[rustc_builtin_macro]
+macro_rules! assert {
+    ($cond:expr) => ({ /* compiler built-in */ });
+    ($cond:expr, $($args:tt)*) => ({ /* compiler built-in */ })
+}
+
+fn main() {
+     {
+        if !true {
+            $crate::panic!("{} {:?}", arg1(a, b, c), arg2);
+        }
+    };
+}
+"##]],
+    );
+}
+
+#[test]
+fn test_compile_error_expand() {
+    check(
+        r#"
+#[rustc_builtin_macro]
+macro_rules! compile_error {
+    ($msg:expr) => ({ /* compiler built-in */ });
+    ($msg:expr,) => ({ /* compiler built-in */ })
+}
+
+// This expands to nothing (since it's in item position), but emits an error.
+compile_error!("error!");
+"#,
+        expect![[r##"
+#[rustc_builtin_macro]
+macro_rules! compile_error {
+    ($msg:expr) => ({ /* compiler built-in */ });
+    ($msg:expr,) => ({ /* compiler built-in */ })
+}
+
+/* error: error! */
+"##]],
+    );
+}
+
+#[test]
+fn test_format_args_expand() {
+    check(
+        r#"
+#[rustc_builtin_macro]
+macro_rules! format_args {
+    ($fmt:expr) => ({ /* compiler built-in */ });
+    ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
+}
+
+fn main() {
+    format_args!("{} {:?}", arg1(a, b, c), arg2);
+}
+"#,
+        expect![[r##"
+#[rustc_builtin_macro]
+macro_rules! format_args {
+    ($fmt:expr) => ({ /* compiler built-in */ });
+    ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
+}
+
+fn main() {
+    unsafe {
+        std::fmt::Arguments::new_v1(&[], &[std::fmt::ArgumentV1::new(&(arg1(a, b, c)), std::fmt::Display::fmt), std::fmt::ArgumentV1::new(&(arg2), std::fmt::Display::fmt), ])
+    };
+}
+"##]],
+    );
+}
+
+#[test]
+fn test_format_args_expand_with_comma_exprs() {
+    check(
+        r#"
+#[rustc_builtin_macro]
+macro_rules! format_args {
+    ($fmt:expr) => ({ /* compiler built-in */ });
+    ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
+}
+
+fn main() {
+    format_args!("{} {:?}", a::<A,B>(), b);
+}
+"#,
+        expect![[r##"
+#[rustc_builtin_macro]
+macro_rules! format_args {
+    ($fmt:expr) => ({ /* compiler built-in */ });
+    ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
+}
+
+fn main() {
+    unsafe {
+        std::fmt::Arguments::new_v1(&[], &[std::fmt::ArgumentV1::new(&(a::<A, B>()), std::fmt::Display::fmt), std::fmt::ArgumentV1::new(&(b), std::fmt::Display::fmt), ])
+    };
+}
+"##]],
+    );
+}
+
+#[test]
+fn test_format_args_expand_with_broken_member_access() {
+    check(
+        r#"
+#[rustc_builtin_macro]
+macro_rules! format_args {
+    ($fmt:expr) => ({ /* compiler built-in */ });
+    ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
+}
+
+fn main() {
+    let _ =
+        // +errors
+        format_args!("{} {:?}", a.);
+}
+"#,
+        expect![[r##"
+#[rustc_builtin_macro]
+macro_rules! format_args {
+    ($fmt:expr) => ({ /* compiler built-in */ });
+    ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
+}
+
+fn main() {
+    let _ =
+        /* parse error: expected field name or number */
+unsafe {
+            std::fmt::Arguments::new_v1(&[], &[std::fmt::ArgumentV1::new(&(a.), std::fmt::Display::fmt), ])
+        };
+}
+"##]],
+    );
+}
+
+#[test]
+fn test_include_bytes_expand() {
+    check(
+        r#"
+#[rustc_builtin_macro]
+macro_rules! include_bytes {
+    ($file:expr) => {{ /* compiler built-in */ }};
+    ($file:expr,) => {{ /* compiler built-in */ }};
+}
+
+fn main() { include_bytes("foo"); }
+"#,
+        expect![[r##"
+#[rustc_builtin_macro]
+macro_rules! include_bytes {
+    ($file:expr) => {{ /* compiler built-in */ }};
+    ($file:expr,) => {{ /* compiler built-in */ }};
+}
+
+fn main() { include_bytes("foo"); }
+"##]],
+    );
+}
+
+#[test]
+fn test_concat_expand() {
+    check(
+        r##"
+#[rustc_builtin_macro]
+macro_rules! concat {}
+
+fn main() { concat!("foo", "r", 0, r#"bar"#, "\n", false); }
+"##,
+        expect![[r##"
+#[rustc_builtin_macro]
+macro_rules! concat {}
+
+fn main() { "foor0bar\nfalse"; }
+"##]],
+    );
+}
+
+#[test]
+fn test_concat_idents_expand() {
+    check(
+        r##"
+#[rustc_builtin_macro]
+macro_rules! concat_idents {}
+
+fn main() { concat_idents!(foo, bar); }
+"##,
+        expect![[r##"
+#[rustc_builtin_macro]
+macro_rules! concat_idents {}
+
+fn main() { foobar; }
+"##]],
+    );
+}
index dbd4378968cc9c0135861486d682e2318fd29eb4..d1cc61e235f7ed38fe07012499fa302f3b814cbf 100644 (file)
@@ -9,9 +9,9 @@
 use cfg::{CfgExpr, CfgOptions};
 use hir_expand::{
     ast_id_map::FileAstId,
-    builtin_attr::find_builtin_attr,
-    builtin_derive::find_builtin_derive,
-    builtin_macro::find_builtin_macro,
+    builtin_attr_macro::find_builtin_attr,
+    builtin_derive_macro::find_builtin_derive,
+    builtin_fn_macro::find_builtin_macro,
     name::{name, AsName, Name},
     proc_macro::ProcMacroExpander,
     ExpandTo, HirFileId, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind,
diff --git a/crates/hir_expand/src/builtin_attr.rs b/crates/hir_expand/src/builtin_attr.rs
deleted file mode 100644 (file)
index 2e46186..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-//! Builtin attributes.
-
-use mbe::ExpandResult;
-use syntax::ast;
-
-use crate::{db::AstDatabase, name, AstId, CrateId, MacroCallId, MacroDefId, MacroDefKind};
-
-macro_rules! register_builtin {
-    ( $(($name:ident, $variant:ident) => $expand:ident),* ) => {
-        #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-        pub enum BuiltinAttrExpander {
-            $($variant),*
-        }
-
-        impl BuiltinAttrExpander {
-            pub fn expand(
-                &self,
-                db: &dyn AstDatabase,
-                id: MacroCallId,
-                tt: &tt::Subtree,
-            ) -> ExpandResult<tt::Subtree> {
-                let expander = match *self {
-                    $( BuiltinAttrExpander::$variant => $expand, )*
-                };
-                expander(db, id, tt)
-            }
-
-            fn find_by_name(name: &name::Name) -> Option<Self> {
-                match name {
-                    $( id if id == &name::name![$name] => Some(BuiltinAttrExpander::$variant), )*
-                     _ => None,
-                }
-            }
-        }
-
-    };
-}
-
-register_builtin! {
-    (bench, Bench) => dummy_attr_expand,
-    (cfg_accessible, CfgAccessible) => dummy_attr_expand,
-    (cfg_eval, CfgEval) => dummy_attr_expand,
-    (derive, Derive) => dummy_attr_expand,
-    (global_allocator, GlobalAllocator) => dummy_attr_expand,
-    (test, Test) => dummy_attr_expand,
-    (test_case, TestCase) => dummy_attr_expand
-}
-
-pub fn find_builtin_attr(
-    ident: &name::Name,
-    krate: CrateId,
-    ast_id: AstId<ast::Macro>,
-) -> Option<MacroDefId> {
-    let expander = BuiltinAttrExpander::find_by_name(ident)?;
-    Some(MacroDefId {
-        krate,
-        kind: MacroDefKind::BuiltInAttr(expander, ast_id),
-        local_inner: false,
-    })
-}
-
-fn dummy_attr_expand(
-    _db: &dyn AstDatabase,
-    _id: MacroCallId,
-    tt: &tt::Subtree,
-) -> ExpandResult<tt::Subtree> {
-    ExpandResult::ok(tt.clone())
-}
diff --git a/crates/hir_expand/src/builtin_attr_macro.rs b/crates/hir_expand/src/builtin_attr_macro.rs
new file mode 100644 (file)
index 0000000..2e46186
--- /dev/null
@@ -0,0 +1,68 @@
+//! Builtin attributes.
+
+use mbe::ExpandResult;
+use syntax::ast;
+
+use crate::{db::AstDatabase, name, AstId, CrateId, MacroCallId, MacroDefId, MacroDefKind};
+
+macro_rules! register_builtin {
+    ( $(($name:ident, $variant:ident) => $expand:ident),* ) => {
+        #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+        pub enum BuiltinAttrExpander {
+            $($variant),*
+        }
+
+        impl BuiltinAttrExpander {
+            pub fn expand(
+                &self,
+                db: &dyn AstDatabase,
+                id: MacroCallId,
+                tt: &tt::Subtree,
+            ) -> ExpandResult<tt::Subtree> {
+                let expander = match *self {
+                    $( BuiltinAttrExpander::$variant => $expand, )*
+                };
+                expander(db, id, tt)
+            }
+
+            fn find_by_name(name: &name::Name) -> Option<Self> {
+                match name {
+                    $( id if id == &name::name![$name] => Some(BuiltinAttrExpander::$variant), )*
+                     _ => None,
+                }
+            }
+        }
+
+    };
+}
+
+register_builtin! {
+    (bench, Bench) => dummy_attr_expand,
+    (cfg_accessible, CfgAccessible) => dummy_attr_expand,
+    (cfg_eval, CfgEval) => dummy_attr_expand,
+    (derive, Derive) => dummy_attr_expand,
+    (global_allocator, GlobalAllocator) => dummy_attr_expand,
+    (test, Test) => dummy_attr_expand,
+    (test_case, TestCase) => dummy_attr_expand
+}
+
+pub fn find_builtin_attr(
+    ident: &name::Name,
+    krate: CrateId,
+    ast_id: AstId<ast::Macro>,
+) -> Option<MacroDefId> {
+    let expander = BuiltinAttrExpander::find_by_name(ident)?;
+    Some(MacroDefId {
+        krate,
+        kind: MacroDefKind::BuiltInAttr(expander, ast_id),
+        local_inner: false,
+    })
+}
+
+fn dummy_attr_expand(
+    _db: &dyn AstDatabase,
+    _id: MacroCallId,
+    tt: &tt::Subtree,
+) -> ExpandResult<tt::Subtree> {
+    ExpandResult::ok(tt.clone())
+}
diff --git a/crates/hir_expand/src/builtin_derive.rs b/crates/hir_expand/src/builtin_derive.rs
deleted file mode 100644 (file)
index eeebe87..0000000
+++ /dev/null
@@ -1,385 +0,0 @@
-//! Builtin derives.
-
-use tracing::debug;
-
-use mbe::ExpandResult;
-use syntax::{
-    ast::{self, AstNode, HasGenericParams, HasModuleItem, HasName},
-    match_ast,
-};
-
-use crate::{db::AstDatabase, name, quote, AstId, CrateId, MacroCallId, MacroDefId, MacroDefKind};
-
-macro_rules! register_builtin {
-    ( $($trait:ident => $expand:ident),* ) => {
-        #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-        pub enum BuiltinDeriveExpander {
-            $($trait),*
-        }
-
-        impl BuiltinDeriveExpander {
-            pub fn expand(
-                &self,
-                db: &dyn AstDatabase,
-                id: MacroCallId,
-                tt: &tt::Subtree,
-            ) -> ExpandResult<tt::Subtree> {
-                let expander = match *self {
-                    $( BuiltinDeriveExpander::$trait => $expand, )*
-                };
-                expander(db, id, tt)
-            }
-
-            fn find_by_name(name: &name::Name) -> Option<Self> {
-                match name {
-                    $( id if id == &name::name![$trait] => Some(BuiltinDeriveExpander::$trait), )*
-                     _ => None,
-                }
-            }
-        }
-
-    };
-}
-
-register_builtin! {
-    Copy => copy_expand,
-    Clone => clone_expand,
-    Default => default_expand,
-    Debug => debug_expand,
-    Hash => hash_expand,
-    Ord => ord_expand,
-    PartialOrd => partial_ord_expand,
-    Eq => eq_expand,
-    PartialEq => partial_eq_expand
-}
-
-pub fn find_builtin_derive(
-    ident: &name::Name,
-    krate: CrateId,
-    ast_id: AstId<ast::Macro>,
-) -> Option<MacroDefId> {
-    let expander = BuiltinDeriveExpander::find_by_name(ident)?;
-    Some(MacroDefId {
-        krate,
-        kind: MacroDefKind::BuiltInDerive(expander, ast_id),
-        local_inner: false,
-    })
-}
-
-struct BasicAdtInfo {
-    name: tt::Ident,
-    type_params: usize,
-}
-
-fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, mbe::ExpandError> {
-    let (parsed, token_map) = mbe::token_tree_to_syntax_node(tt, mbe::ParserEntryPoint::Items)?; // FragmentKind::Items doesn't parse attrs?
-    let macro_items = ast::MacroItems::cast(parsed.syntax_node()).ok_or_else(|| {
-        debug!("derive node didn't parse");
-        mbe::ExpandError::UnexpectedToken
-    })?;
-    let item = macro_items.items().next().ok_or_else(|| {
-        debug!("no module item parsed");
-        mbe::ExpandError::NoMatchingRule
-    })?;
-    let node = item.syntax();
-    let (name, params) = match_ast! {
-        match node {
-            ast::Struct(it) => (it.name(), it.generic_param_list()),
-            ast::Enum(it) => (it.name(), it.generic_param_list()),
-            ast::Union(it) => (it.name(), it.generic_param_list()),
-            _ => {
-                debug!("unexpected node is {:?}", node);
-                return Err(mbe::ExpandError::ConversionError)
-            },
-        }
-    };
-    let name = name.ok_or_else(|| {
-        debug!("parsed item has no name");
-        mbe::ExpandError::NoMatchingRule
-    })?;
-    let name_token_id = token_map.token_by_range(name.syntax().text_range()).ok_or_else(|| {
-        debug!("name token not found");
-        mbe::ExpandError::ConversionError
-    })?;
-    let name_token = tt::Ident { id: name_token_id, text: name.text().into() };
-    let type_params = params.map_or(0, |type_param_list| type_param_list.type_params().count());
-    Ok(BasicAdtInfo { name: name_token, type_params })
-}
-
-fn make_type_args(n: usize, bound: Vec<tt::TokenTree>) -> Vec<tt::TokenTree> {
-    let mut result = Vec::<tt::TokenTree>::with_capacity(n * 2);
-    result.push(
-        tt::Leaf::Punct(tt::Punct {
-            char: '<',
-            spacing: tt::Spacing::Alone,
-            id: tt::TokenId::unspecified(),
-        })
-        .into(),
-    );
-    for i in 0..n {
-        if i > 0 {
-            result.push(
-                tt::Leaf::Punct(tt::Punct {
-                    char: ',',
-                    spacing: tt::Spacing::Alone,
-                    id: tt::TokenId::unspecified(),
-                })
-                .into(),
-            );
-        }
-        result.push(
-            tt::Leaf::Ident(tt::Ident {
-                id: tt::TokenId::unspecified(),
-                text: format!("T{}", i).into(),
-            })
-            .into(),
-        );
-        result.extend(bound.iter().cloned());
-    }
-    result.push(
-        tt::Leaf::Punct(tt::Punct {
-            char: '>',
-            spacing: tt::Spacing::Alone,
-            id: tt::TokenId::unspecified(),
-        })
-        .into(),
-    );
-    result
-}
-
-fn expand_simple_derive(tt: &tt::Subtree, trait_path: tt::Subtree) -> ExpandResult<tt::Subtree> {
-    let info = match parse_adt(tt) {
-        Ok(info) => info,
-        Err(e) => return ExpandResult::only_err(e),
-    };
-    let name = info.name;
-    let trait_path_clone = trait_path.token_trees.clone();
-    let bound = (quote! { : ##trait_path_clone }).token_trees;
-    let type_params = make_type_args(info.type_params, bound);
-    let type_args = make_type_args(info.type_params, Vec::new());
-    let trait_path = trait_path.token_trees;
-    let expanded = quote! {
-        impl ##type_params ##trait_path for #name ##type_args {}
-    };
-    ExpandResult::ok(expanded)
-}
-
-fn find_builtin_crate(db: &dyn AstDatabase, id: MacroCallId) -> tt::TokenTree {
-    // FIXME: make hygiene works for builtin derive macro
-    // such that $crate can be used here.
-    let cg = db.crate_graph();
-    let krate = db.lookup_intern_macro(id).krate;
-
-    // XXX
-    //  All crates except core itself should have a dependency on core,
-    //  We detect `core` by seeing whether it doesn't have such a dependency.
-    let tt = if cg[krate].dependencies.iter().any(|dep| &*dep.name == "core") {
-        quote! { core }
-    } else {
-        quote! { crate }
-    };
-
-    tt.token_trees[0].clone()
-}
-
-fn copy_expand(
-    db: &dyn AstDatabase,
-    id: MacroCallId,
-    tt: &tt::Subtree,
-) -> ExpandResult<tt::Subtree> {
-    let krate = find_builtin_crate(db, id);
-    expand_simple_derive(tt, quote! { #krate::marker::Copy })
-}
-
-fn clone_expand(
-    db: &dyn AstDatabase,
-    id: MacroCallId,
-    tt: &tt::Subtree,
-) -> ExpandResult<tt::Subtree> {
-    let krate = find_builtin_crate(db, id);
-    expand_simple_derive(tt, quote! { #krate::clone::Clone })
-}
-
-fn default_expand(
-    db: &dyn AstDatabase,
-    id: MacroCallId,
-    tt: &tt::Subtree,
-) -> ExpandResult<tt::Subtree> {
-    let krate = find_builtin_crate(db, id);
-    expand_simple_derive(tt, quote! { #krate::default::Default })
-}
-
-fn debug_expand(
-    db: &dyn AstDatabase,
-    id: MacroCallId,
-    tt: &tt::Subtree,
-) -> ExpandResult<tt::Subtree> {
-    let krate = find_builtin_crate(db, id);
-    expand_simple_derive(tt, quote! { #krate::fmt::Debug })
-}
-
-fn hash_expand(
-    db: &dyn AstDatabase,
-    id: MacroCallId,
-    tt: &tt::Subtree,
-) -> ExpandResult<tt::Subtree> {
-    let krate = find_builtin_crate(db, id);
-    expand_simple_derive(tt, quote! { #krate::hash::Hash })
-}
-
-fn eq_expand(db: &dyn AstDatabase, id: MacroCallId, tt: &tt::Subtree) -> ExpandResult<tt::Subtree> {
-    let krate = find_builtin_crate(db, id);
-    expand_simple_derive(tt, quote! { #krate::cmp::Eq })
-}
-
-fn partial_eq_expand(
-    db: &dyn AstDatabase,
-    id: MacroCallId,
-    tt: &tt::Subtree,
-) -> ExpandResult<tt::Subtree> {
-    let krate = find_builtin_crate(db, id);
-    expand_simple_derive(tt, quote! { #krate::cmp::PartialEq })
-}
-
-fn ord_expand(
-    db: &dyn AstDatabase,
-    id: MacroCallId,
-    tt: &tt::Subtree,
-) -> ExpandResult<tt::Subtree> {
-    let krate = find_builtin_crate(db, id);
-    expand_simple_derive(tt, quote! { #krate::cmp::Ord })
-}
-
-fn partial_ord_expand(
-    db: &dyn AstDatabase,
-    id: MacroCallId,
-    tt: &tt::Subtree,
-) -> ExpandResult<tt::Subtree> {
-    let krate = find_builtin_crate(db, id);
-    expand_simple_derive(tt, quote! { #krate::cmp::PartialOrd })
-}
-
-#[cfg(test)]
-mod tests {
-    use base_db::{fixture::WithFixture, CrateId, SourceDatabase};
-    use expect_test::{expect, Expect};
-    use name::AsName;
-
-    use crate::{test_db::TestDB, AstId, MacroCallId, MacroCallKind, MacroCallLoc};
-
-    use super::*;
-
-    fn expand_builtin_derive(ra_fixture: &str) -> String {
-        let fixture = format!(
-            r#"//- /main.rs crate:main deps:core
-$0
-{}
-//- /lib.rs crate:core
-// empty
-"#,
-            ra_fixture
-        );
-
-        let (db, file_pos) = TestDB::with_position(&fixture);
-        let file_id = file_pos.file_id;
-        let ast_id_map = db.ast_id_map(file_id.into());
-        let parsed = db.parse(file_id);
-        let macros: Vec<_> =
-            parsed.syntax_node().descendants().filter_map(ast::Macro::cast).collect();
-        let items: Vec<_> = parsed
-            .syntax_node()
-            .descendants()
-            .filter(|node| !ast::Macro::can_cast(node.kind()))
-            .filter_map(ast::Item::cast)
-            .collect();
-
-        assert_eq!(macros.len(), 1, "test must contain exactly 1 macro definition");
-        assert_eq!(items.len(), 1, "test must contain exactly 1 item");
-
-        let macro_ast_id = AstId::new(file_id.into(), ast_id_map.ast_id(&macros[0]));
-        let name = match &macros[0] {
-            ast::Macro::MacroRules(rules) => rules.name().unwrap().as_name(),
-            ast::Macro::MacroDef(def) => def.name().unwrap().as_name(),
-        };
-
-        let expander = BuiltinDeriveExpander::find_by_name(&name).unwrap();
-
-        let ast_id = AstId::new(file_id.into(), ast_id_map.ast_id(&items[0]));
-
-        let loc = MacroCallLoc {
-            def: MacroDefId {
-                krate: CrateId(0),
-                kind: MacroDefKind::BuiltInDerive(expander, macro_ast_id),
-                local_inner: false,
-            },
-            krate: CrateId(0),
-            eager: None,
-            kind: MacroCallKind::Derive {
-                ast_id,
-                derive_name: name.to_string(),
-                derive_attr_index: 0,
-            },
-        };
-
-        let id: MacroCallId = db.intern_macro(loc);
-        let parsed = db.parse_or_expand(id.as_file()).unwrap();
-
-        // FIXME text() for syntax nodes parsed from token tree looks weird
-        // because there's no whitespace, see below
-        parsed.text().to_string()
-    }
-
-    fn check_derive(ra_fixture: &str, expected: Expect) {
-        let expanded = expand_builtin_derive(ra_fixture);
-        expected.assert_eq(&expanded);
-    }
-
-    #[test]
-    fn test_copy_expand_simple() {
-        check_derive(
-            r#"
-            macro Copy {}
-            #[derive(Copy)]
-            struct Foo;
-            "#,
-            expect![["impl< >core::marker::CopyforFoo< >{}"]],
-        );
-    }
-
-    #[test]
-    fn test_copy_expand_with_type_params() {
-        check_derive(
-            r#"
-            macro Copy {}
-            #[derive(Copy)]
-            struct Foo<A, B>;
-            "#,
-            expect![["impl<T0:core::marker::Copy,T1:core::marker::Copy>core::marker::CopyforFoo<T0,T1>{}"]],
-        );
-    }
-
-    #[test]
-    fn test_copy_expand_with_lifetimes() {
-        check_derive(
-            r#"
-            macro Copy {}
-            #[derive(Copy)]
-            struct Foo<A, B, 'a, 'b>;
-            "#,
-            // We currently just ignore lifetimes
-            expect![["impl<T0:core::marker::Copy,T1:core::marker::Copy>core::marker::CopyforFoo<T0,T1>{}"]],
-        );
-    }
-
-    #[test]
-    fn test_clone_expand() {
-        check_derive(
-            r#"
-            macro Clone {}
-            #[derive(Clone)]
-            struct Foo<A, B>;
-            "#,
-            expect![["impl<T0:core::clone::Clone,T1:core::clone::Clone>core::clone::CloneforFoo<T0,T1>{}"]],
-        );
-    }
-}
diff --git a/crates/hir_expand/src/builtin_derive_macro.rs b/crates/hir_expand/src/builtin_derive_macro.rs
new file mode 100644 (file)
index 0000000..eeebe87
--- /dev/null
@@ -0,0 +1,385 @@
+//! Builtin derives.
+
+use tracing::debug;
+
+use mbe::ExpandResult;
+use syntax::{
+    ast::{self, AstNode, HasGenericParams, HasModuleItem, HasName},
+    match_ast,
+};
+
+use crate::{db::AstDatabase, name, quote, AstId, CrateId, MacroCallId, MacroDefId, MacroDefKind};
+
+macro_rules! register_builtin {
+    ( $($trait:ident => $expand:ident),* ) => {
+        #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+        pub enum BuiltinDeriveExpander {
+            $($trait),*
+        }
+
+        impl BuiltinDeriveExpander {
+            pub fn expand(
+                &self,
+                db: &dyn AstDatabase,
+                id: MacroCallId,
+                tt: &tt::Subtree,
+            ) -> ExpandResult<tt::Subtree> {
+                let expander = match *self {
+                    $( BuiltinDeriveExpander::$trait => $expand, )*
+                };
+                expander(db, id, tt)
+            }
+
+            fn find_by_name(name: &name::Name) -> Option<Self> {
+                match name {
+                    $( id if id == &name::name![$trait] => Some(BuiltinDeriveExpander::$trait), )*
+                     _ => None,
+                }
+            }
+        }
+
+    };
+}
+
+register_builtin! {
+    Copy => copy_expand,
+    Clone => clone_expand,
+    Default => default_expand,
+    Debug => debug_expand,
+    Hash => hash_expand,
+    Ord => ord_expand,
+    PartialOrd => partial_ord_expand,
+    Eq => eq_expand,
+    PartialEq => partial_eq_expand
+}
+
+pub fn find_builtin_derive(
+    ident: &name::Name,
+    krate: CrateId,
+    ast_id: AstId<ast::Macro>,
+) -> Option<MacroDefId> {
+    let expander = BuiltinDeriveExpander::find_by_name(ident)?;
+    Some(MacroDefId {
+        krate,
+        kind: MacroDefKind::BuiltInDerive(expander, ast_id),
+        local_inner: false,
+    })
+}
+
+struct BasicAdtInfo {
+    name: tt::Ident,
+    type_params: usize,
+}
+
+fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, mbe::ExpandError> {
+    let (parsed, token_map) = mbe::token_tree_to_syntax_node(tt, mbe::ParserEntryPoint::Items)?; // FragmentKind::Items doesn't parse attrs?
+    let macro_items = ast::MacroItems::cast(parsed.syntax_node()).ok_or_else(|| {
+        debug!("derive node didn't parse");
+        mbe::ExpandError::UnexpectedToken
+    })?;
+    let item = macro_items.items().next().ok_or_else(|| {
+        debug!("no module item parsed");
+        mbe::ExpandError::NoMatchingRule
+    })?;
+    let node = item.syntax();
+    let (name, params) = match_ast! {
+        match node {
+            ast::Struct(it) => (it.name(), it.generic_param_list()),
+            ast::Enum(it) => (it.name(), it.generic_param_list()),
+            ast::Union(it) => (it.name(), it.generic_param_list()),
+            _ => {
+                debug!("unexpected node is {:?}", node);
+                return Err(mbe::ExpandError::ConversionError)
+            },
+        }
+    };
+    let name = name.ok_or_else(|| {
+        debug!("parsed item has no name");
+        mbe::ExpandError::NoMatchingRule
+    })?;
+    let name_token_id = token_map.token_by_range(name.syntax().text_range()).ok_or_else(|| {
+        debug!("name token not found");
+        mbe::ExpandError::ConversionError
+    })?;
+    let name_token = tt::Ident { id: name_token_id, text: name.text().into() };
+    let type_params = params.map_or(0, |type_param_list| type_param_list.type_params().count());
+    Ok(BasicAdtInfo { name: name_token, type_params })
+}
+
+fn make_type_args(n: usize, bound: Vec<tt::TokenTree>) -> Vec<tt::TokenTree> {
+    let mut result = Vec::<tt::TokenTree>::with_capacity(n * 2);
+    result.push(
+        tt::Leaf::Punct(tt::Punct {
+            char: '<',
+            spacing: tt::Spacing::Alone,
+            id: tt::TokenId::unspecified(),
+        })
+        .into(),
+    );
+    for i in 0..n {
+        if i > 0 {
+            result.push(
+                tt::Leaf::Punct(tt::Punct {
+                    char: ',',
+                    spacing: tt::Spacing::Alone,
+                    id: tt::TokenId::unspecified(),
+                })
+                .into(),
+            );
+        }
+        result.push(
+            tt::Leaf::Ident(tt::Ident {
+                id: tt::TokenId::unspecified(),
+                text: format!("T{}", i).into(),
+            })
+            .into(),
+        );
+        result.extend(bound.iter().cloned());
+    }
+    result.push(
+        tt::Leaf::Punct(tt::Punct {
+            char: '>',
+            spacing: tt::Spacing::Alone,
+            id: tt::TokenId::unspecified(),
+        })
+        .into(),
+    );
+    result
+}
+
+fn expand_simple_derive(tt: &tt::Subtree, trait_path: tt::Subtree) -> ExpandResult<tt::Subtree> {
+    let info = match parse_adt(tt) {
+        Ok(info) => info,
+        Err(e) => return ExpandResult::only_err(e),
+    };
+    let name = info.name;
+    let trait_path_clone = trait_path.token_trees.clone();
+    let bound = (quote! { : ##trait_path_clone }).token_trees;
+    let type_params = make_type_args(info.type_params, bound);
+    let type_args = make_type_args(info.type_params, Vec::new());
+    let trait_path = trait_path.token_trees;
+    let expanded = quote! {
+        impl ##type_params ##trait_path for #name ##type_args {}
+    };
+    ExpandResult::ok(expanded)
+}
+
+fn find_builtin_crate(db: &dyn AstDatabase, id: MacroCallId) -> tt::TokenTree {
+    // FIXME: make hygiene works for builtin derive macro
+    // such that $crate can be used here.
+    let cg = db.crate_graph();
+    let krate = db.lookup_intern_macro(id).krate;
+
+    // XXX
+    //  All crates except core itself should have a dependency on core,
+    //  We detect `core` by seeing whether it doesn't have such a dependency.
+    let tt = if cg[krate].dependencies.iter().any(|dep| &*dep.name == "core") {
+        quote! { core }
+    } else {
+        quote! { crate }
+    };
+
+    tt.token_trees[0].clone()
+}
+
+fn copy_expand(
+    db: &dyn AstDatabase,
+    id: MacroCallId,
+    tt: &tt::Subtree,
+) -> ExpandResult<tt::Subtree> {
+    let krate = find_builtin_crate(db, id);
+    expand_simple_derive(tt, quote! { #krate::marker::Copy })
+}
+
+fn clone_expand(
+    db: &dyn AstDatabase,
+    id: MacroCallId,
+    tt: &tt::Subtree,
+) -> ExpandResult<tt::Subtree> {
+    let krate = find_builtin_crate(db, id);
+    expand_simple_derive(tt, quote! { #krate::clone::Clone })
+}
+
+fn default_expand(
+    db: &dyn AstDatabase,
+    id: MacroCallId,
+    tt: &tt::Subtree,
+) -> ExpandResult<tt::Subtree> {
+    let krate = find_builtin_crate(db, id);
+    expand_simple_derive(tt, quote! { #krate::default::Default })
+}
+
+fn debug_expand(
+    db: &dyn AstDatabase,
+    id: MacroCallId,
+    tt: &tt::Subtree,
+) -> ExpandResult<tt::Subtree> {
+    let krate = find_builtin_crate(db, id);
+    expand_simple_derive(tt, quote! { #krate::fmt::Debug })
+}
+
+fn hash_expand(
+    db: &dyn AstDatabase,
+    id: MacroCallId,
+    tt: &tt::Subtree,
+) -> ExpandResult<tt::Subtree> {
+    let krate = find_builtin_crate(db, id);
+    expand_simple_derive(tt, quote! { #krate::hash::Hash })
+}
+
+fn eq_expand(db: &dyn AstDatabase, id: MacroCallId, tt: &tt::Subtree) -> ExpandResult<tt::Subtree> {
+    let krate = find_builtin_crate(db, id);
+    expand_simple_derive(tt, quote! { #krate::cmp::Eq })
+}
+
+fn partial_eq_expand(
+    db: &dyn AstDatabase,
+    id: MacroCallId,
+    tt: &tt::Subtree,
+) -> ExpandResult<tt::Subtree> {
+    let krate = find_builtin_crate(db, id);
+    expand_simple_derive(tt, quote! { #krate::cmp::PartialEq })
+}
+
+fn ord_expand(
+    db: &dyn AstDatabase,
+    id: MacroCallId,
+    tt: &tt::Subtree,
+) -> ExpandResult<tt::Subtree> {
+    let krate = find_builtin_crate(db, id);
+    expand_simple_derive(tt, quote! { #krate::cmp::Ord })
+}
+
+fn partial_ord_expand(
+    db: &dyn AstDatabase,
+    id: MacroCallId,
+    tt: &tt::Subtree,
+) -> ExpandResult<tt::Subtree> {
+    let krate = find_builtin_crate(db, id);
+    expand_simple_derive(tt, quote! { #krate::cmp::PartialOrd })
+}
+
+#[cfg(test)]
+mod tests {
+    use base_db::{fixture::WithFixture, CrateId, SourceDatabase};
+    use expect_test::{expect, Expect};
+    use name::AsName;
+
+    use crate::{test_db::TestDB, AstId, MacroCallId, MacroCallKind, MacroCallLoc};
+
+    use super::*;
+
+    fn expand_builtin_derive(ra_fixture: &str) -> String {
+        let fixture = format!(
+            r#"//- /main.rs crate:main deps:core
+$0
+{}
+//- /lib.rs crate:core
+// empty
+"#,
+            ra_fixture
+        );
+
+        let (db, file_pos) = TestDB::with_position(&fixture);
+        let file_id = file_pos.file_id;
+        let ast_id_map = db.ast_id_map(file_id.into());
+        let parsed = db.parse(file_id);
+        let macros: Vec<_> =
+            parsed.syntax_node().descendants().filter_map(ast::Macro::cast).collect();
+        let items: Vec<_> = parsed
+            .syntax_node()
+            .descendants()
+            .filter(|node| !ast::Macro::can_cast(node.kind()))
+            .filter_map(ast::Item::cast)
+            .collect();
+
+        assert_eq!(macros.len(), 1, "test must contain exactly 1 macro definition");
+        assert_eq!(items.len(), 1, "test must contain exactly 1 item");
+
+        let macro_ast_id = AstId::new(file_id.into(), ast_id_map.ast_id(&macros[0]));
+        let name = match &macros[0] {
+            ast::Macro::MacroRules(rules) => rules.name().unwrap().as_name(),
+            ast::Macro::MacroDef(def) => def.name().unwrap().as_name(),
+        };
+
+        let expander = BuiltinDeriveExpander::find_by_name(&name).unwrap();
+
+        let ast_id = AstId::new(file_id.into(), ast_id_map.ast_id(&items[0]));
+
+        let loc = MacroCallLoc {
+            def: MacroDefId {
+                krate: CrateId(0),
+                kind: MacroDefKind::BuiltInDerive(expander, macro_ast_id),
+                local_inner: false,
+            },
+            krate: CrateId(0),
+            eager: None,
+            kind: MacroCallKind::Derive {
+                ast_id,
+                derive_name: name.to_string(),
+                derive_attr_index: 0,
+            },
+        };
+
+        let id: MacroCallId = db.intern_macro(loc);
+        let parsed = db.parse_or_expand(id.as_file()).unwrap();
+
+        // FIXME text() for syntax nodes parsed from token tree looks weird
+        // because there's no whitespace, see below
+        parsed.text().to_string()
+    }
+
+    fn check_derive(ra_fixture: &str, expected: Expect) {
+        let expanded = expand_builtin_derive(ra_fixture);
+        expected.assert_eq(&expanded);
+    }
+
+    #[test]
+    fn test_copy_expand_simple() {
+        check_derive(
+            r#"
+            macro Copy {}
+            #[derive(Copy)]
+            struct Foo;
+            "#,
+            expect![["impl< >core::marker::CopyforFoo< >{}"]],
+        );
+    }
+
+    #[test]
+    fn test_copy_expand_with_type_params() {
+        check_derive(
+            r#"
+            macro Copy {}
+            #[derive(Copy)]
+            struct Foo<A, B>;
+            "#,
+            expect![["impl<T0:core::marker::Copy,T1:core::marker::Copy>core::marker::CopyforFoo<T0,T1>{}"]],
+        );
+    }
+
+    #[test]
+    fn test_copy_expand_with_lifetimes() {
+        check_derive(
+            r#"
+            macro Copy {}
+            #[derive(Copy)]
+            struct Foo<A, B, 'a, 'b>;
+            "#,
+            // We currently just ignore lifetimes
+            expect![["impl<T0:core::marker::Copy,T1:core::marker::Copy>core::marker::CopyforFoo<T0,T1>{}"]],
+        );
+    }
+
+    #[test]
+    fn test_clone_expand() {
+        check_derive(
+            r#"
+            macro Clone {}
+            #[derive(Clone)]
+            struct Foo<A, B>;
+            "#,
+            expect![["impl<T0:core::clone::Clone,T1:core::clone::Clone>core::clone::CloneforFoo<T0,T1>{}"]],
+        );
+    }
+}
diff --git a/crates/hir_expand/src/builtin_fn_macro.rs b/crates/hir_expand/src/builtin_fn_macro.rs
new file mode 100644 (file)
index 0000000..4b801eb
--- /dev/null
@@ -0,0 +1,567 @@
+//! Builtin macro
+use crate::{
+    db::AstDatabase, name, quote, AstId, CrateId, MacroCallId, MacroCallLoc, MacroDefId,
+    MacroDefKind,
+};
+
+use base_db::{AnchoredPath, Edition, FileId};
+use cfg::CfgExpr;
+use either::Either;
+use mbe::{parse_exprs_with_sep, parse_to_token_tree, ExpandResult};
+use syntax::ast::{self, AstToken};
+
+macro_rules! register_builtin {
+    ( LAZY: $(($name:ident, $kind: ident) => $expand:ident),* , EAGER: $(($e_name:ident, $e_kind: ident) => $e_expand:ident),*  ) => {
+        #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+        pub enum BuiltinFnLikeExpander {
+            $($kind),*
+        }
+
+        #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+        pub enum EagerExpander {
+            $($e_kind),*
+        }
+
+        impl BuiltinFnLikeExpander {
+            pub fn expand(
+                &self,
+                db: &dyn AstDatabase,
+                id: MacroCallId,
+                tt: &tt::Subtree,
+            ) -> ExpandResult<tt::Subtree> {
+                let expander = match *self {
+                    $( BuiltinFnLikeExpander::$kind => $expand, )*
+                };
+                expander(db, id, tt)
+            }
+        }
+
+        impl EagerExpander {
+            pub fn expand(
+                &self,
+                db: &dyn AstDatabase,
+                arg_id: MacroCallId,
+                tt: &tt::Subtree,
+            ) -> ExpandResult<Option<ExpandedEager>> {
+                let expander = match *self {
+                    $( EagerExpander::$e_kind => $e_expand, )*
+                };
+                expander(db, arg_id, tt)
+            }
+        }
+
+        fn find_by_name(ident: &name::Name) -> Option<Either<BuiltinFnLikeExpander, EagerExpander>> {
+            match ident {
+                $( id if id == &name::name![$name] => Some(Either::Left(BuiltinFnLikeExpander::$kind)), )*
+                $( id if id == &name::name![$e_name] => Some(Either::Right(EagerExpander::$e_kind)), )*
+                _ => return None,
+            }
+        }
+    };
+}
+
+#[derive(Debug)]
+pub struct ExpandedEager {
+    pub(crate) subtree: tt::Subtree,
+    /// The included file ID of the include macro.
+    pub(crate) included_file: Option<FileId>,
+}
+
+impl ExpandedEager {
+    fn new(subtree: tt::Subtree) -> Self {
+        ExpandedEager { subtree, included_file: None }
+    }
+}
+
+pub fn find_builtin_macro(
+    ident: &name::Name,
+    krate: CrateId,
+    ast_id: AstId<ast::Macro>,
+) -> Option<MacroDefId> {
+    let kind = find_by_name(ident)?;
+
+    match kind {
+        Either::Left(kind) => Some(MacroDefId {
+            krate,
+            kind: MacroDefKind::BuiltIn(kind, ast_id),
+            local_inner: false,
+        }),
+        Either::Right(kind) => Some(MacroDefId {
+            krate,
+            kind: MacroDefKind::BuiltInEager(kind, ast_id),
+            local_inner: false,
+        }),
+    }
+}
+
+register_builtin! {
+    LAZY:
+    (column, Column) => column_expand,
+    (file, File) => file_expand,
+    (line, Line) => line_expand,
+    (module_path, ModulePath) => module_path_expand,
+    (assert, Assert) => assert_expand,
+    (stringify, Stringify) => stringify_expand,
+    (format_args, FormatArgs) => format_args_expand,
+    (const_format_args, ConstFormatArgs) => format_args_expand,
+    // format_args_nl only differs in that it adds a newline in the end,
+    // so we use the same stub expansion for now
+    (format_args_nl, FormatArgsNl) => format_args_expand,
+    (llvm_asm, LlvmAsm) => asm_expand,
+    (asm, Asm) => asm_expand,
+    (global_asm, GlobalAsm) => global_asm_expand,
+    (cfg, Cfg) => cfg_expand,
+    (core_panic, CorePanic) => panic_expand,
+    (std_panic, StdPanic) => panic_expand,
+
+    EAGER:
+    (compile_error, CompileError) => compile_error_expand,
+    (concat, Concat) => concat_expand,
+    (concat_idents, ConcatIdents) => concat_idents_expand,
+    (include, Include) => include_expand,
+    (include_bytes, IncludeBytes) => include_bytes_expand,
+    (include_str, IncludeStr) => include_str_expand,
+    (env, Env) => env_expand,
+    (option_env, OptionEnv) => option_env_expand
+}
+
+fn module_path_expand(
+    _db: &dyn AstDatabase,
+    _id: MacroCallId,
+    _tt: &tt::Subtree,
+) -> ExpandResult<tt::Subtree> {
+    // Just return a dummy result.
+    ExpandResult::ok(quote! { "module::path" })
+}
+
+fn line_expand(
+    _db: &dyn AstDatabase,
+    _id: MacroCallId,
+    _tt: &tt::Subtree,
+) -> ExpandResult<tt::Subtree> {
+    // dummy implementation for type-checking purposes
+    let line_num = 0;
+    let expanded = quote! {
+        #line_num
+    };
+
+    ExpandResult::ok(expanded)
+}
+
+fn stringify_expand(
+    _db: &dyn AstDatabase,
+    _id: MacroCallId,
+    tt: &tt::Subtree,
+) -> ExpandResult<tt::Subtree> {
+    let pretty = tt::pretty(&tt.token_trees);
+
+    let expanded = quote! {
+        #pretty
+    };
+
+    ExpandResult::ok(expanded)
+}
+
+fn column_expand(
+    _db: &dyn AstDatabase,
+    _id: MacroCallId,
+    _tt: &tt::Subtree,
+) -> ExpandResult<tt::Subtree> {
+    // dummy implementation for type-checking purposes
+    let col_num = 0;
+    let expanded = quote! {
+        #col_num
+    };
+
+    ExpandResult::ok(expanded)
+}
+
+fn assert_expand(
+    _db: &dyn AstDatabase,
+    _id: MacroCallId,
+    tt: &tt::Subtree,
+) -> ExpandResult<tt::Subtree> {
+    let krate = tt::Ident { text: "$crate".into(), id: tt::TokenId::unspecified() };
+    let args = parse_exprs_with_sep(tt, ',');
+    let expanded = match &*args {
+        [cond, panic_args @ ..] => {
+            let comma = tt::Subtree {
+                delimiter: None,
+                token_trees: vec![tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct {
+                    char: ',',
+                    spacing: tt::Spacing::Alone,
+                    id: tt::TokenId::unspecified(),
+                }))],
+            };
+            let cond = cond.clone();
+            let panic_args = itertools::Itertools::intersperse(panic_args.iter().cloned(), comma);
+            quote! {{
+                if !#cond {
+                    #krate::panic!(##panic_args);
+                }
+            }}
+        }
+        [] => quote! {{}},
+    };
+
+    ExpandResult::ok(expanded)
+}
+
+fn file_expand(
+    _db: &dyn AstDatabase,
+    _id: MacroCallId,
+    _tt: &tt::Subtree,
+) -> ExpandResult<tt::Subtree> {
+    // FIXME: RA purposefully lacks knowledge of absolute file names
+    // so just return "".
+    let file_name = "";
+
+    let expanded = quote! {
+        #file_name
+    };
+
+    ExpandResult::ok(expanded)
+}
+
+fn format_args_expand(
+    _db: &dyn AstDatabase,
+    _id: MacroCallId,
+    tt: &tt::Subtree,
+) -> ExpandResult<tt::Subtree> {
+    // We expand `format_args!("", a1, a2)` to
+    // ```
+    // std::fmt::Arguments::new_v1(&[], &[
+    //   std::fmt::ArgumentV1::new(&arg1,std::fmt::Display::fmt),
+    //   std::fmt::ArgumentV1::new(&arg2,std::fmt::Display::fmt),
+    // ])
+    // ```,
+    // which is still not really correct, but close enough for now
+    let mut args = parse_exprs_with_sep(tt, ',');
+
+    if args.is_empty() {
+        return ExpandResult::only_err(mbe::ExpandError::NoMatchingRule);
+    }
+    for arg in &mut args {
+        // Remove `key =`.
+        if matches!(arg.token_trees.get(1), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=' && p.spacing != tt::Spacing::Joint)
+        {
+            arg.token_trees.drain(..2);
+        }
+    }
+    let _format_string = args.remove(0);
+    let arg_tts = args.into_iter().flat_map(|arg| {
+        quote! { std::fmt::ArgumentV1::new(&(#arg), std::fmt::Display::fmt), }
+    }.token_trees);
+    let expanded = quote! {
+        // It's unsafe since https://github.com/rust-lang/rust/pull/83302
+        // Wrap an unsafe block to avoid false-positive `missing-unsafe` lint.
+        // FIXME: Currently we don't have `unused_unsafe` lint so an extra unsafe block won't cause issues on early
+        // stable rust-src.
+        unsafe {
+            std::fmt::Arguments::new_v1(&[], &[##arg_tts])
+        }
+    };
+    ExpandResult::ok(expanded)
+}
+
+fn asm_expand(
+    _db: &dyn AstDatabase,
+    _id: MacroCallId,
+    tt: &tt::Subtree,
+) -> ExpandResult<tt::Subtree> {
+    // We expand all assembly snippets to `format_args!` invocations to get format syntax
+    // highlighting for them.
+
+    let krate = tt::Ident { text: "$crate".into(), id: tt::TokenId::unspecified() };
+
+    let mut literals = Vec::new();
+    for tt in tt.token_trees.chunks(2) {
+        match tt {
+            [tt::TokenTree::Leaf(tt::Leaf::Literal(lit))]
+            | [tt::TokenTree::Leaf(tt::Leaf::Literal(lit)), tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: ',', id: _, spacing: _ }))] =>
+            {
+                let krate = krate.clone();
+                literals.push(quote!(#krate::format_args!(#lit);));
+            }
+            _ => break,
+        }
+    }
+
+    let expanded = quote! {{
+        ##literals
+        ()
+    }};
+    ExpandResult::ok(expanded)
+}
+
+fn global_asm_expand(
+    _db: &dyn AstDatabase,
+    _id: MacroCallId,
+    _tt: &tt::Subtree,
+) -> ExpandResult<tt::Subtree> {
+    // Expand to nothing (at item-level)
+    ExpandResult::ok(quote! {})
+}
+
+fn cfg_expand(
+    db: &dyn AstDatabase,
+    id: MacroCallId,
+    tt: &tt::Subtree,
+) -> ExpandResult<tt::Subtree> {
+    let loc = db.lookup_intern_macro(id);
+    let expr = CfgExpr::parse(tt);
+    let enabled = db.crate_graph()[loc.krate].cfg_options.check(&expr) != Some(false);
+    let expanded = if enabled { quote!(true) } else { quote!(false) };
+    ExpandResult::ok(expanded)
+}
+
+fn panic_expand(
+    db: &dyn AstDatabase,
+    id: MacroCallId,
+    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)?;
+    token.value().map(|it| it.into_owned())
+}
+
+fn compile_error_expand(
+    _db: &dyn AstDatabase,
+    _id: MacroCallId,
+    tt: &tt::Subtree,
+) -> ExpandResult<Option<ExpandedEager>> {
+    let err = match &*tt.token_trees {
+        [tt::TokenTree::Leaf(tt::Leaf::Literal(it))] => {
+            let text = it.text.as_str();
+            if text.starts_with('"') && text.ends_with('"') {
+                // FIXME: does not handle raw strings
+                mbe::ExpandError::Other(text[1..text.len() - 1].to_string())
+            } else {
+                mbe::ExpandError::BindingError("`compile_error!` argument must be a string".into())
+            }
+        }
+        _ => mbe::ExpandError::BindingError("`compile_error!` argument must be a string".into()),
+    };
+
+    ExpandResult { value: Some(ExpandedEager::new(quote! {})), err: Some(err) }
+}
+
+fn concat_expand(
+    _db: &dyn AstDatabase,
+    _arg_id: MacroCallId,
+    tt: &tt::Subtree,
+) -> ExpandResult<Option<ExpandedEager>> {
+    let mut err = None;
+    let mut text = String::new();
+    for (i, t) in tt.token_trees.iter().enumerate() {
+        match t {
+            tt::TokenTree::Leaf(tt::Leaf::Literal(it)) if i % 2 == 0 => {
+                // concat works with string and char literals, so remove any quotes.
+                // It also works with integer, float and boolean literals, so just use the rest
+                // as-is.
+                let component = unquote_str(it).unwrap_or_else(|| it.text.to_string());
+                text.push_str(&component);
+            }
+            // handle boolean literals
+            tt::TokenTree::Leaf(tt::Leaf::Ident(id))
+                if i % 2 == 0 && (id.text == "true" || id.text == "false") =>
+            {
+                text.push_str(id.text.as_str());
+            }
+            tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (),
+            _ => {
+                err.get_or_insert(mbe::ExpandError::UnexpectedToken);
+            }
+        }
+    }
+    ExpandResult { value: Some(ExpandedEager::new(quote!(#text))), err }
+}
+
+fn concat_idents_expand(
+    _db: &dyn AstDatabase,
+    _arg_id: MacroCallId,
+    tt: &tt::Subtree,
+) -> ExpandResult<Option<ExpandedEager>> {
+    let mut err = None;
+    let mut ident = String::new();
+    for (i, t) in tt.token_trees.iter().enumerate() {
+        match t {
+            tt::TokenTree::Leaf(tt::Leaf::Ident(id)) => {
+                ident.push_str(id.text.as_str());
+            }
+            tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (),
+            _ => {
+                err.get_or_insert(mbe::ExpandError::UnexpectedToken);
+            }
+        }
+    }
+    let ident = tt::Ident { text: ident.into(), id: tt::TokenId::unspecified() };
+    ExpandResult { value: Some(ExpandedEager::new(quote!(#ident))), err }
+}
+
+fn relative_file(
+    db: &dyn AstDatabase,
+    call_id: MacroCallId,
+    path_str: &str,
+    allow_recursion: bool,
+) -> Result<FileId, mbe::ExpandError> {
+    let call_site = call_id.as_file().original_file(db);
+    let path = AnchoredPath { anchor: call_site, path: path_str };
+    let res = db
+        .resolve_path(path)
+        .ok_or_else(|| mbe::ExpandError::Other(format!("failed to load file `{}`", path_str)))?;
+    // Prevent include itself
+    if res == call_site && !allow_recursion {
+        Err(mbe::ExpandError::Other(format!("recursive inclusion of `{}`", path_str)))
+    } else {
+        Ok(res)
+    }
+}
+
+fn parse_string(tt: &tt::Subtree) -> Result<String, mbe::ExpandError> {
+    tt.token_trees
+        .get(0)
+        .and_then(|tt| match tt {
+            tt::TokenTree::Leaf(tt::Leaf::Literal(it)) => unquote_str(it),
+            _ => None,
+        })
+        .ok_or(mbe::ExpandError::ConversionError)
+}
+
+fn include_expand(
+    db: &dyn AstDatabase,
+    arg_id: MacroCallId,
+    tt: &tt::Subtree,
+) -> ExpandResult<Option<ExpandedEager>> {
+    let res = (|| {
+        let path = parse_string(tt)?;
+        let file_id = relative_file(db, arg_id, &path, false)?;
+
+        let subtree =
+            parse_to_token_tree(&db.file_text(file_id)).ok_or(mbe::ExpandError::ConversionError)?.0;
+        Ok((subtree, file_id))
+    })();
+
+    match res {
+        Ok((subtree, file_id)) => {
+            ExpandResult::ok(Some(ExpandedEager { subtree, included_file: Some(file_id) }))
+        }
+        Err(e) => ExpandResult::only_err(e),
+    }
+}
+
+fn include_bytes_expand(
+    _db: &dyn AstDatabase,
+    _arg_id: MacroCallId,
+    tt: &tt::Subtree,
+) -> ExpandResult<Option<ExpandedEager>> {
+    if let Err(e) = parse_string(tt) {
+        return ExpandResult::only_err(e);
+    }
+
+    // FIXME: actually read the file here if the user asked for macro expansion
+    let res = tt::Subtree {
+        delimiter: None,
+        token_trees: vec![tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
+            text: r#"b"""#.into(),
+            id: tt::TokenId::unspecified(),
+        }))],
+    };
+    ExpandResult::ok(Some(ExpandedEager::new(res)))
+}
+
+fn include_str_expand(
+    db: &dyn AstDatabase,
+    arg_id: MacroCallId,
+    tt: &tt::Subtree,
+) -> ExpandResult<Option<ExpandedEager>> {
+    let path = match parse_string(tt) {
+        Ok(it) => it,
+        Err(e) => return ExpandResult::only_err(e),
+    };
+
+    // FIXME: we're not able to read excluded files (which is most of them because
+    // it's unusual to `include_str!` a Rust file), but we can return an empty string.
+    // Ideally, we'd be able to offer a precise expansion if the user asks for macro
+    // expansion.
+    let file_id = match relative_file(db, arg_id, &path, true) {
+        Ok(file_id) => file_id,
+        Err(_) => {
+            return ExpandResult::ok(Some(ExpandedEager::new(quote!(""))));
+        }
+    };
+
+    let text = db.file_text(file_id);
+    let text = &*text;
+
+    ExpandResult::ok(Some(ExpandedEager::new(quote!(#text))))
+}
+
+fn get_env_inner(db: &dyn AstDatabase, arg_id: MacroCallId, key: &str) -> Option<String> {
+    let krate = db.lookup_intern_macro(arg_id).krate;
+    db.crate_graph()[krate].env.get(key)
+}
+
+fn env_expand(
+    db: &dyn AstDatabase,
+    arg_id: MacroCallId,
+    tt: &tt::Subtree,
+) -> ExpandResult<Option<ExpandedEager>> {
+    let key = match parse_string(tt) {
+        Ok(it) => it,
+        Err(e) => return ExpandResult::only_err(e),
+    };
+
+    let mut err = None;
+    let s = get_env_inner(db, arg_id, &key).unwrap_or_else(|| {
+        // The only variable rust-analyzer ever sets is `OUT_DIR`, so only diagnose that to avoid
+        // unnecessary diagnostics for eg. `CARGO_PKG_NAME`.
+        if key == "OUT_DIR" {
+            err = Some(mbe::ExpandError::Other(
+                r#"`OUT_DIR` not set, enable "run build scripts" to fix"#.into(),
+            ));
+        }
+
+        // If the variable is unset, still return a dummy string to help type inference along.
+        // We cannot use an empty string here, because for
+        // `include!(concat!(env!("OUT_DIR"), "/foo.rs"))` will become
+        // `include!("foo.rs"), which might go to infinite loop
+        "__RA_UNIMPLEMENTED__".to_string()
+    });
+    let expanded = quote! { #s };
+
+    ExpandResult { value: Some(ExpandedEager::new(expanded)), err }
+}
+
+fn option_env_expand(
+    db: &dyn AstDatabase,
+    arg_id: MacroCallId,
+    tt: &tt::Subtree,
+) -> ExpandResult<Option<ExpandedEager>> {
+    let key = match parse_string(tt) {
+        Ok(it) => it,
+        Err(e) => return ExpandResult::only_err(e),
+    };
+
+    let expanded = match get_env_inner(db, arg_id, &key) {
+        None => quote! { std::option::Option::None::<&str> },
+        Some(s) => quote! { std::option::Some(#s) },
+    };
+
+    ExpandResult::ok(Some(ExpandedEager::new(expanded)))
+}
diff --git a/crates/hir_expand/src/builtin_macro.rs b/crates/hir_expand/src/builtin_macro.rs
deleted file mode 100644 (file)
index 4b801eb..0000000
+++ /dev/null
@@ -1,567 +0,0 @@
-//! Builtin macro
-use crate::{
-    db::AstDatabase, name, quote, AstId, CrateId, MacroCallId, MacroCallLoc, MacroDefId,
-    MacroDefKind,
-};
-
-use base_db::{AnchoredPath, Edition, FileId};
-use cfg::CfgExpr;
-use either::Either;
-use mbe::{parse_exprs_with_sep, parse_to_token_tree, ExpandResult};
-use syntax::ast::{self, AstToken};
-
-macro_rules! register_builtin {
-    ( LAZY: $(($name:ident, $kind: ident) => $expand:ident),* , EAGER: $(($e_name:ident, $e_kind: ident) => $e_expand:ident),*  ) => {
-        #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-        pub enum BuiltinFnLikeExpander {
-            $($kind),*
-        }
-
-        #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-        pub enum EagerExpander {
-            $($e_kind),*
-        }
-
-        impl BuiltinFnLikeExpander {
-            pub fn expand(
-                &self,
-                db: &dyn AstDatabase,
-                id: MacroCallId,
-                tt: &tt::Subtree,
-            ) -> ExpandResult<tt::Subtree> {
-                let expander = match *self {
-                    $( BuiltinFnLikeExpander::$kind => $expand, )*
-                };
-                expander(db, id, tt)
-            }
-        }
-
-        impl EagerExpander {
-            pub fn expand(
-                &self,
-                db: &dyn AstDatabase,
-                arg_id: MacroCallId,
-                tt: &tt::Subtree,
-            ) -> ExpandResult<Option<ExpandedEager>> {
-                let expander = match *self {
-                    $( EagerExpander::$e_kind => $e_expand, )*
-                };
-                expander(db, arg_id, tt)
-            }
-        }
-
-        fn find_by_name(ident: &name::Name) -> Option<Either<BuiltinFnLikeExpander, EagerExpander>> {
-            match ident {
-                $( id if id == &name::name![$name] => Some(Either::Left(BuiltinFnLikeExpander::$kind)), )*
-                $( id if id == &name::name![$e_name] => Some(Either::Right(EagerExpander::$e_kind)), )*
-                _ => return None,
-            }
-        }
-    };
-}
-
-#[derive(Debug)]
-pub struct ExpandedEager {
-    pub(crate) subtree: tt::Subtree,
-    /// The included file ID of the include macro.
-    pub(crate) included_file: Option<FileId>,
-}
-
-impl ExpandedEager {
-    fn new(subtree: tt::Subtree) -> Self {
-        ExpandedEager { subtree, included_file: None }
-    }
-}
-
-pub fn find_builtin_macro(
-    ident: &name::Name,
-    krate: CrateId,
-    ast_id: AstId<ast::Macro>,
-) -> Option<MacroDefId> {
-    let kind = find_by_name(ident)?;
-
-    match kind {
-        Either::Left(kind) => Some(MacroDefId {
-            krate,
-            kind: MacroDefKind::BuiltIn(kind, ast_id),
-            local_inner: false,
-        }),
-        Either::Right(kind) => Some(MacroDefId {
-            krate,
-            kind: MacroDefKind::BuiltInEager(kind, ast_id),
-            local_inner: false,
-        }),
-    }
-}
-
-register_builtin! {
-    LAZY:
-    (column, Column) => column_expand,
-    (file, File) => file_expand,
-    (line, Line) => line_expand,
-    (module_path, ModulePath) => module_path_expand,
-    (assert, Assert) => assert_expand,
-    (stringify, Stringify) => stringify_expand,
-    (format_args, FormatArgs) => format_args_expand,
-    (const_format_args, ConstFormatArgs) => format_args_expand,
-    // format_args_nl only differs in that it adds a newline in the end,
-    // so we use the same stub expansion for now
-    (format_args_nl, FormatArgsNl) => format_args_expand,
-    (llvm_asm, LlvmAsm) => asm_expand,
-    (asm, Asm) => asm_expand,
-    (global_asm, GlobalAsm) => global_asm_expand,
-    (cfg, Cfg) => cfg_expand,
-    (core_panic, CorePanic) => panic_expand,
-    (std_panic, StdPanic) => panic_expand,
-
-    EAGER:
-    (compile_error, CompileError) => compile_error_expand,
-    (concat, Concat) => concat_expand,
-    (concat_idents, ConcatIdents) => concat_idents_expand,
-    (include, Include) => include_expand,
-    (include_bytes, IncludeBytes) => include_bytes_expand,
-    (include_str, IncludeStr) => include_str_expand,
-    (env, Env) => env_expand,
-    (option_env, OptionEnv) => option_env_expand
-}
-
-fn module_path_expand(
-    _db: &dyn AstDatabase,
-    _id: MacroCallId,
-    _tt: &tt::Subtree,
-) -> ExpandResult<tt::Subtree> {
-    // Just return a dummy result.
-    ExpandResult::ok(quote! { "module::path" })
-}
-
-fn line_expand(
-    _db: &dyn AstDatabase,
-    _id: MacroCallId,
-    _tt: &tt::Subtree,
-) -> ExpandResult<tt::Subtree> {
-    // dummy implementation for type-checking purposes
-    let line_num = 0;
-    let expanded = quote! {
-        #line_num
-    };
-
-    ExpandResult::ok(expanded)
-}
-
-fn stringify_expand(
-    _db: &dyn AstDatabase,
-    _id: MacroCallId,
-    tt: &tt::Subtree,
-) -> ExpandResult<tt::Subtree> {
-    let pretty = tt::pretty(&tt.token_trees);
-
-    let expanded = quote! {
-        #pretty
-    };
-
-    ExpandResult::ok(expanded)
-}
-
-fn column_expand(
-    _db: &dyn AstDatabase,
-    _id: MacroCallId,
-    _tt: &tt::Subtree,
-) -> ExpandResult<tt::Subtree> {
-    // dummy implementation for type-checking purposes
-    let col_num = 0;
-    let expanded = quote! {
-        #col_num
-    };
-
-    ExpandResult::ok(expanded)
-}
-
-fn assert_expand(
-    _db: &dyn AstDatabase,
-    _id: MacroCallId,
-    tt: &tt::Subtree,
-) -> ExpandResult<tt::Subtree> {
-    let krate = tt::Ident { text: "$crate".into(), id: tt::TokenId::unspecified() };
-    let args = parse_exprs_with_sep(tt, ',');
-    let expanded = match &*args {
-        [cond, panic_args @ ..] => {
-            let comma = tt::Subtree {
-                delimiter: None,
-                token_trees: vec![tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct {
-                    char: ',',
-                    spacing: tt::Spacing::Alone,
-                    id: tt::TokenId::unspecified(),
-                }))],
-            };
-            let cond = cond.clone();
-            let panic_args = itertools::Itertools::intersperse(panic_args.iter().cloned(), comma);
-            quote! {{
-                if !#cond {
-                    #krate::panic!(##panic_args);
-                }
-            }}
-        }
-        [] => quote! {{}},
-    };
-
-    ExpandResult::ok(expanded)
-}
-
-fn file_expand(
-    _db: &dyn AstDatabase,
-    _id: MacroCallId,
-    _tt: &tt::Subtree,
-) -> ExpandResult<tt::Subtree> {
-    // FIXME: RA purposefully lacks knowledge of absolute file names
-    // so just return "".
-    let file_name = "";
-
-    let expanded = quote! {
-        #file_name
-    };
-
-    ExpandResult::ok(expanded)
-}
-
-fn format_args_expand(
-    _db: &dyn AstDatabase,
-    _id: MacroCallId,
-    tt: &tt::Subtree,
-) -> ExpandResult<tt::Subtree> {
-    // We expand `format_args!("", a1, a2)` to
-    // ```
-    // std::fmt::Arguments::new_v1(&[], &[
-    //   std::fmt::ArgumentV1::new(&arg1,std::fmt::Display::fmt),
-    //   std::fmt::ArgumentV1::new(&arg2,std::fmt::Display::fmt),
-    // ])
-    // ```,
-    // which is still not really correct, but close enough for now
-    let mut args = parse_exprs_with_sep(tt, ',');
-
-    if args.is_empty() {
-        return ExpandResult::only_err(mbe::ExpandError::NoMatchingRule);
-    }
-    for arg in &mut args {
-        // Remove `key =`.
-        if matches!(arg.token_trees.get(1), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=' && p.spacing != tt::Spacing::Joint)
-        {
-            arg.token_trees.drain(..2);
-        }
-    }
-    let _format_string = args.remove(0);
-    let arg_tts = args.into_iter().flat_map(|arg| {
-        quote! { std::fmt::ArgumentV1::new(&(#arg), std::fmt::Display::fmt), }
-    }.token_trees);
-    let expanded = quote! {
-        // It's unsafe since https://github.com/rust-lang/rust/pull/83302
-        // Wrap an unsafe block to avoid false-positive `missing-unsafe` lint.
-        // FIXME: Currently we don't have `unused_unsafe` lint so an extra unsafe block won't cause issues on early
-        // stable rust-src.
-        unsafe {
-            std::fmt::Arguments::new_v1(&[], &[##arg_tts])
-        }
-    };
-    ExpandResult::ok(expanded)
-}
-
-fn asm_expand(
-    _db: &dyn AstDatabase,
-    _id: MacroCallId,
-    tt: &tt::Subtree,
-) -> ExpandResult<tt::Subtree> {
-    // We expand all assembly snippets to `format_args!` invocations to get format syntax
-    // highlighting for them.
-
-    let krate = tt::Ident { text: "$crate".into(), id: tt::TokenId::unspecified() };
-
-    let mut literals = Vec::new();
-    for tt in tt.token_trees.chunks(2) {
-        match tt {
-            [tt::TokenTree::Leaf(tt::Leaf::Literal(lit))]
-            | [tt::TokenTree::Leaf(tt::Leaf::Literal(lit)), tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: ',', id: _, spacing: _ }))] =>
-            {
-                let krate = krate.clone();
-                literals.push(quote!(#krate::format_args!(#lit);));
-            }
-            _ => break,
-        }
-    }
-
-    let expanded = quote! {{
-        ##literals
-        ()
-    }};
-    ExpandResult::ok(expanded)
-}
-
-fn global_asm_expand(
-    _db: &dyn AstDatabase,
-    _id: MacroCallId,
-    _tt: &tt::Subtree,
-) -> ExpandResult<tt::Subtree> {
-    // Expand to nothing (at item-level)
-    ExpandResult::ok(quote! {})
-}
-
-fn cfg_expand(
-    db: &dyn AstDatabase,
-    id: MacroCallId,
-    tt: &tt::Subtree,
-) -> ExpandResult<tt::Subtree> {
-    let loc = db.lookup_intern_macro(id);
-    let expr = CfgExpr::parse(tt);
-    let enabled = db.crate_graph()[loc.krate].cfg_options.check(&expr) != Some(false);
-    let expanded = if enabled { quote!(true) } else { quote!(false) };
-    ExpandResult::ok(expanded)
-}
-
-fn panic_expand(
-    db: &dyn AstDatabase,
-    id: MacroCallId,
-    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)?;
-    token.value().map(|it| it.into_owned())
-}
-
-fn compile_error_expand(
-    _db: &dyn AstDatabase,
-    _id: MacroCallId,
-    tt: &tt::Subtree,
-) -> ExpandResult<Option<ExpandedEager>> {
-    let err = match &*tt.token_trees {
-        [tt::TokenTree::Leaf(tt::Leaf::Literal(it))] => {
-            let text = it.text.as_str();
-            if text.starts_with('"') && text.ends_with('"') {
-                // FIXME: does not handle raw strings
-                mbe::ExpandError::Other(text[1..text.len() - 1].to_string())
-            } else {
-                mbe::ExpandError::BindingError("`compile_error!` argument must be a string".into())
-            }
-        }
-        _ => mbe::ExpandError::BindingError("`compile_error!` argument must be a string".into()),
-    };
-
-    ExpandResult { value: Some(ExpandedEager::new(quote! {})), err: Some(err) }
-}
-
-fn concat_expand(
-    _db: &dyn AstDatabase,
-    _arg_id: MacroCallId,
-    tt: &tt::Subtree,
-) -> ExpandResult<Option<ExpandedEager>> {
-    let mut err = None;
-    let mut text = String::new();
-    for (i, t) in tt.token_trees.iter().enumerate() {
-        match t {
-            tt::TokenTree::Leaf(tt::Leaf::Literal(it)) if i % 2 == 0 => {
-                // concat works with string and char literals, so remove any quotes.
-                // It also works with integer, float and boolean literals, so just use the rest
-                // as-is.
-                let component = unquote_str(it).unwrap_or_else(|| it.text.to_string());
-                text.push_str(&component);
-            }
-            // handle boolean literals
-            tt::TokenTree::Leaf(tt::Leaf::Ident(id))
-                if i % 2 == 0 && (id.text == "true" || id.text == "false") =>
-            {
-                text.push_str(id.text.as_str());
-            }
-            tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (),
-            _ => {
-                err.get_or_insert(mbe::ExpandError::UnexpectedToken);
-            }
-        }
-    }
-    ExpandResult { value: Some(ExpandedEager::new(quote!(#text))), err }
-}
-
-fn concat_idents_expand(
-    _db: &dyn AstDatabase,
-    _arg_id: MacroCallId,
-    tt: &tt::Subtree,
-) -> ExpandResult<Option<ExpandedEager>> {
-    let mut err = None;
-    let mut ident = String::new();
-    for (i, t) in tt.token_trees.iter().enumerate() {
-        match t {
-            tt::TokenTree::Leaf(tt::Leaf::Ident(id)) => {
-                ident.push_str(id.text.as_str());
-            }
-            tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (),
-            _ => {
-                err.get_or_insert(mbe::ExpandError::UnexpectedToken);
-            }
-        }
-    }
-    let ident = tt::Ident { text: ident.into(), id: tt::TokenId::unspecified() };
-    ExpandResult { value: Some(ExpandedEager::new(quote!(#ident))), err }
-}
-
-fn relative_file(
-    db: &dyn AstDatabase,
-    call_id: MacroCallId,
-    path_str: &str,
-    allow_recursion: bool,
-) -> Result<FileId, mbe::ExpandError> {
-    let call_site = call_id.as_file().original_file(db);
-    let path = AnchoredPath { anchor: call_site, path: path_str };
-    let res = db
-        .resolve_path(path)
-        .ok_or_else(|| mbe::ExpandError::Other(format!("failed to load file `{}`", path_str)))?;
-    // Prevent include itself
-    if res == call_site && !allow_recursion {
-        Err(mbe::ExpandError::Other(format!("recursive inclusion of `{}`", path_str)))
-    } else {
-        Ok(res)
-    }
-}
-
-fn parse_string(tt: &tt::Subtree) -> Result<String, mbe::ExpandError> {
-    tt.token_trees
-        .get(0)
-        .and_then(|tt| match tt {
-            tt::TokenTree::Leaf(tt::Leaf::Literal(it)) => unquote_str(it),
-            _ => None,
-        })
-        .ok_or(mbe::ExpandError::ConversionError)
-}
-
-fn include_expand(
-    db: &dyn AstDatabase,
-    arg_id: MacroCallId,
-    tt: &tt::Subtree,
-) -> ExpandResult<Option<ExpandedEager>> {
-    let res = (|| {
-        let path = parse_string(tt)?;
-        let file_id = relative_file(db, arg_id, &path, false)?;
-
-        let subtree =
-            parse_to_token_tree(&db.file_text(file_id)).ok_or(mbe::ExpandError::ConversionError)?.0;
-        Ok((subtree, file_id))
-    })();
-
-    match res {
-        Ok((subtree, file_id)) => {
-            ExpandResult::ok(Some(ExpandedEager { subtree, included_file: Some(file_id) }))
-        }
-        Err(e) => ExpandResult::only_err(e),
-    }
-}
-
-fn include_bytes_expand(
-    _db: &dyn AstDatabase,
-    _arg_id: MacroCallId,
-    tt: &tt::Subtree,
-) -> ExpandResult<Option<ExpandedEager>> {
-    if let Err(e) = parse_string(tt) {
-        return ExpandResult::only_err(e);
-    }
-
-    // FIXME: actually read the file here if the user asked for macro expansion
-    let res = tt::Subtree {
-        delimiter: None,
-        token_trees: vec![tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
-            text: r#"b"""#.into(),
-            id: tt::TokenId::unspecified(),
-        }))],
-    };
-    ExpandResult::ok(Some(ExpandedEager::new(res)))
-}
-
-fn include_str_expand(
-    db: &dyn AstDatabase,
-    arg_id: MacroCallId,
-    tt: &tt::Subtree,
-) -> ExpandResult<Option<ExpandedEager>> {
-    let path = match parse_string(tt) {
-        Ok(it) => it,
-        Err(e) => return ExpandResult::only_err(e),
-    };
-
-    // FIXME: we're not able to read excluded files (which is most of them because
-    // it's unusual to `include_str!` a Rust file), but we can return an empty string.
-    // Ideally, we'd be able to offer a precise expansion if the user asks for macro
-    // expansion.
-    let file_id = match relative_file(db, arg_id, &path, true) {
-        Ok(file_id) => file_id,
-        Err(_) => {
-            return ExpandResult::ok(Some(ExpandedEager::new(quote!(""))));
-        }
-    };
-
-    let text = db.file_text(file_id);
-    let text = &*text;
-
-    ExpandResult::ok(Some(ExpandedEager::new(quote!(#text))))
-}
-
-fn get_env_inner(db: &dyn AstDatabase, arg_id: MacroCallId, key: &str) -> Option<String> {
-    let krate = db.lookup_intern_macro(arg_id).krate;
-    db.crate_graph()[krate].env.get(key)
-}
-
-fn env_expand(
-    db: &dyn AstDatabase,
-    arg_id: MacroCallId,
-    tt: &tt::Subtree,
-) -> ExpandResult<Option<ExpandedEager>> {
-    let key = match parse_string(tt) {
-        Ok(it) => it,
-        Err(e) => return ExpandResult::only_err(e),
-    };
-
-    let mut err = None;
-    let s = get_env_inner(db, arg_id, &key).unwrap_or_else(|| {
-        // The only variable rust-analyzer ever sets is `OUT_DIR`, so only diagnose that to avoid
-        // unnecessary diagnostics for eg. `CARGO_PKG_NAME`.
-        if key == "OUT_DIR" {
-            err = Some(mbe::ExpandError::Other(
-                r#"`OUT_DIR` not set, enable "run build scripts" to fix"#.into(),
-            ));
-        }
-
-        // If the variable is unset, still return a dummy string to help type inference along.
-        // We cannot use an empty string here, because for
-        // `include!(concat!(env!("OUT_DIR"), "/foo.rs"))` will become
-        // `include!("foo.rs"), which might go to infinite loop
-        "__RA_UNIMPLEMENTED__".to_string()
-    });
-    let expanded = quote! { #s };
-
-    ExpandResult { value: Some(ExpandedEager::new(expanded)), err }
-}
-
-fn option_env_expand(
-    db: &dyn AstDatabase,
-    arg_id: MacroCallId,
-    tt: &tt::Subtree,
-) -> ExpandResult<Option<ExpandedEager>> {
-    let key = match parse_string(tt) {
-        Ok(it) => it,
-        Err(e) => return ExpandResult::only_err(e),
-    };
-
-    let expanded = match get_env_inner(db, arg_id, &key) {
-        None => quote! { std::option::Option::None::<&str> },
-        Some(s) => quote! { std::option::Some(#s) },
-    };
-
-    ExpandResult::ok(Some(ExpandedEager::new(expanded)))
-}
index ad6b84dd17e95fe9771fff055751ac55ce18a5f8..de32d02415d9b7c5087a657188395b6833a84239 100644 (file)
@@ -8,9 +8,9 @@
 pub mod ast_id_map;
 pub mod name;
 pub mod hygiene;
-pub mod builtin_attr;
-pub mod builtin_derive;
-pub mod builtin_macro;
+pub mod builtin_attr_macro;
+pub mod builtin_derive_macro;
+pub mod builtin_fn_macro;
 pub mod proc_macro;
 pub mod quote;
 pub mod eager;
@@ -31,9 +31,9 @@
 
 use crate::{
     ast_id_map::FileAstId,
-    builtin_attr::BuiltinAttrExpander,
-    builtin_derive::BuiltinDeriveExpander,
-    builtin_macro::{BuiltinFnLikeExpander, EagerExpander},
+    builtin_attr_macro::BuiltinAttrExpander,
+    builtin_derive_macro::BuiltinDeriveExpander,
+    builtin_fn_macro::{BuiltinFnLikeExpander, EagerExpander},
     db::TokenExpander,
     proc_macro::ProcMacroExpander,
 };