mod mbe;
mod builtin_fn_macro;
+mod builtin_derive_macro;
use std::{iter, ops::Range};
};
use crate::{
- db::DefDatabase, nameres::ModuleSource, resolver::HasResolver, test_db::TestDB, AsMacroCall,
+ db::DefDatabase, nameres::ModuleSource, resolver::HasResolver, src::HasSource, test_db::TestDB,
+ AsMacroCall, Lookup,
};
#[track_caller]
ModuleSource::Module(_) | ModuleSource::BlockExpr(_) => panic!(),
};
+ // What we want to do is to replace all macros (fn-like, derive, attr) with
+ // their expansions. Turns out, we don't actually store enough information
+ // to do this precisely though! Specifically, if a macro expands to nothing,
+ // it leaves zero traces in def-map, so we can't get its expansion after the
+ // fact.
+ //
+ // This is the usual
+ // <https://github.com/rust-analyzer/rust-analyzer/issues/3407>
+ // resolve/record tension!
+ //
+ // So here we try to do a resolve, which is necessary a heuristic. For macro
+ // calls, we use `as_call_id_with_errors`. For derives, we look at the impls
+ // in the module and assume that, if impls's source is a different
+ // `HirFileId`, than it came from macro expansion.
+
let mut expansions = Vec::new();
for macro_call in source_file.syntax().descendants().filter_map(ast::MacroCall::cast) {
let macro_call = InFile::new(source.file_id, ¯o_call);
}
let mut expanded_text = source_file.to_string();
+
for (call, exp) in expansions.into_iter().rev() {
let mut tree = false;
let mut expect_errors = false;
expanded_text.replace_range(range, &expn_text)
}
+ for impl_id in def_map[local_id].scope.impls() {
+ let src = impl_id.lookup(&db).source(&db);
+ if src.file_id.is_builtin_derive(&db).is_some() {
+ let pp = pretty_print_macro_expansion(src.value.syntax().clone());
+ format_to!(expanded_text, "\n{}", pp)
+ }
+ }
+
expect.indent(false);
expect.assert_eq(&expanded_text);
}
--- /dev/null
+//! Tests for `builtin_derive_macro.rs` from `hir_expand`.
+
+use expect_test::expect;
+
+use crate::macro_expansion_tests::check;
+
+#[test]
+fn test_copy_expand_simple() {
+ check(
+ r#"
+//- minicore: derive, copy
+#[derive(Copy)]
+struct Foo;
+"#,
+ expect![[r##"
+#[derive(Copy)]
+struct Foo;
+
+impl < > core::marker::Copy for Foo< > {}"##]],
+ );
+}
+
+#[test]
+fn test_copy_expand_with_type_params() {
+ check(
+ r#"
+//- minicore: derive, copy
+#[derive(Copy)]
+struct Foo<A, B>;
+"#,
+ expect![[r##"
+#[derive(Copy)]
+struct Foo<A, B>;
+
+impl <T0: core::marker::Copy, T1: core::marker::Copy> core::marker::Copy for Foo<T0, T1> {}"##]],
+ );
+}
+
+#[test]
+fn test_copy_expand_with_lifetimes() {
+ // We currently just ignore lifetimes
+ check(
+ r#"
+//- minicore: derive, copy
+#[derive(Copy)]
+struct Foo<A, B, 'a, 'b>;
+"#,
+ expect![[r##"
+#[derive(Copy)]
+struct Foo<A, B, 'a, 'b>;
+
+impl <T0: core::marker::Copy, T1: core::marker::Copy> core::marker::Copy for Foo<T0, T1> {}"##]],
+ );
+}
+
+#[test]
+fn test_clone_expand() {
+ check(
+ r#"
+//- minicore: derive, clone
+#[derive(Clone)]
+struct Foo<A, B>;
+"#,
+ expect![[r##"
+#[derive(Clone)]
+struct Foo<A, B>;
+
+impl <T0: core::clone::Clone, T1: core::clone::Clone> core::clone::Clone for Foo<T0, T1> {}"##]],
+ );
+}
-//! Tests for builtin macros (see `builtin_macro.rs` in `hir_expand`).
+//! Tests for `builtin_fn_macro.rs` from `hir_expand`.
use expect_test::expect;
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(¯os[0]));
- let name = match ¯os[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>{}"]],
- );
- }
-}