summaryrefslogtreecommitdiffstats
path: root/src/tools/cargo/tests/testsuite/proc_macro.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/cargo/tests/testsuite/proc_macro.rs')
-rw-r--r--src/tools/cargo/tests/testsuite/proc_macro.rs560
1 files changed, 560 insertions, 0 deletions
diff --git a/src/tools/cargo/tests/testsuite/proc_macro.rs b/src/tools/cargo/tests/testsuite/proc_macro.rs
new file mode 100644
index 000000000..7d6f6ba86
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/proc_macro.rs
@@ -0,0 +1,560 @@
+//! Tests for proc-macros.
+
+use cargo_test_support::project;
+
+#[cargo_test]
+fn probe_cfg_before_crate_type_discovery() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [target.'cfg(not(stage300))'.dependencies.noop]
+ path = "../noop"
+ "#,
+ )
+ .file(
+ "src/main.rs",
+ r#"
+ #[macro_use]
+ extern crate noop;
+
+ #[derive(Noop)]
+ struct X;
+
+ fn main() {}
+ "#,
+ )
+ .build();
+ let _noop = project()
+ .at("noop")
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "noop"
+ version = "0.0.1"
+ authors = []
+
+ [lib]
+ proc-macro = true
+ "#,
+ )
+ .file(
+ "src/lib.rs",
+ r#"
+ extern crate proc_macro;
+ use proc_macro::TokenStream;
+
+ #[proc_macro_derive(Noop)]
+ pub fn noop(_input: TokenStream) -> TokenStream {
+ "".parse().unwrap()
+ }
+ "#,
+ )
+ .build();
+
+ p.cargo("check").run();
+}
+
+#[cargo_test]
+fn noop() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [dependencies.noop]
+ path = "../noop"
+ "#,
+ )
+ .file(
+ "src/main.rs",
+ r#"
+ #[macro_use]
+ extern crate noop;
+
+ #[derive(Noop)]
+ struct X;
+
+ fn main() {}
+ "#,
+ )
+ .build();
+ let _noop = project()
+ .at("noop")
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "noop"
+ version = "0.0.1"
+ authors = []
+
+ [lib]
+ proc-macro = true
+ "#,
+ )
+ .file(
+ "src/lib.rs",
+ r#"
+ extern crate proc_macro;
+ use proc_macro::TokenStream;
+
+ #[proc_macro_derive(Noop)]
+ pub fn noop(_input: TokenStream) -> TokenStream {
+ "".parse().unwrap()
+ }
+ "#,
+ )
+ .build();
+
+ p.cargo("check").run();
+ p.cargo("check").run();
+}
+
+#[cargo_test]
+fn impl_and_derive() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [dependencies.transmogrify]
+ path = "../transmogrify"
+ "#,
+ )
+ .file(
+ "src/main.rs",
+ r#"
+ #[macro_use]
+ extern crate transmogrify;
+
+ trait ImplByTransmogrify {
+ fn impl_by_transmogrify(&self) -> bool;
+ }
+
+ #[derive(Transmogrify, Debug)]
+ struct X { success: bool }
+
+ fn main() {
+ let x = X::new();
+ assert!(x.impl_by_transmogrify());
+ println!("{:?}", x);
+ }
+ "#,
+ )
+ .build();
+ let _transmogrify = project()
+ .at("transmogrify")
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "transmogrify"
+ version = "0.0.1"
+ authors = []
+
+ [lib]
+ proc-macro = true
+ "#,
+ )
+ .file(
+ "src/lib.rs",
+ r#"
+ extern crate proc_macro;
+ use proc_macro::TokenStream;
+
+ #[proc_macro_derive(Transmogrify)]
+ #[doc(hidden)]
+ pub fn transmogrify(input: TokenStream) -> TokenStream {
+ "
+ impl X {
+ fn new() -> Self {
+ X { success: true }
+ }
+ }
+
+ impl ImplByTransmogrify for X {
+ fn impl_by_transmogrify(&self) -> bool {
+ true
+ }
+ }
+ ".parse().unwrap()
+ }
+ "#,
+ )
+ .build();
+
+ p.cargo("build").run();
+ p.cargo("run").with_stdout("X { success: true }").run();
+}
+
+#[cargo_test(nightly, reason = "plugins are unstable")]
+fn plugin_and_proc_macro() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [lib]
+ plugin = true
+ proc-macro = true
+ "#,
+ )
+ .file(
+ "src/lib.rs",
+ r#"
+ #![feature(rustc_private)]
+ #![feature(proc_macro, proc_macro_lib)]
+
+ extern crate rustc_driver;
+ use rustc_driver::plugin::Registry;
+
+ extern crate proc_macro;
+ use proc_macro::TokenStream;
+
+ #[no_mangle]
+ pub fn __rustc_plugin_registrar(reg: &mut Registry) {}
+
+ #[proc_macro_derive(Questionable)]
+ pub fn questionable(input: TokenStream) -> TokenStream {
+ input
+ }
+ "#,
+ )
+ .build();
+
+ let msg = " `lib.plugin` and `lib.proc-macro` cannot both be `true`";
+ p.cargo("check")
+ .with_status(101)
+ .with_stderr_contains(msg)
+ .run();
+}
+
+#[cargo_test]
+fn proc_macro_doctest() {
+ let foo = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+ authors = []
+ [lib]
+ proc-macro = true
+ "#,
+ )
+ .file(
+ "src/lib.rs",
+ r#"
+ #![crate_type = "proc-macro"]
+
+ extern crate proc_macro;
+
+ use proc_macro::TokenStream;
+
+ /// ```
+ /// assert!(true);
+ /// ```
+ #[proc_macro_derive(Bar)]
+ pub fn derive(_input: TokenStream) -> TokenStream {
+ "".parse().unwrap()
+ }
+
+ #[test]
+ fn a() {
+ assert!(true);
+ }
+ "#,
+ )
+ .build();
+
+ foo.cargo("test")
+ .with_stdout_contains("test a ... ok")
+ .with_stdout_contains_n("test [..] ... ok", 2)
+ .run();
+}
+
+#[cargo_test]
+fn proc_macro_crate_type() {
+ // Verify that `crate-type = ["proc-macro"]` is the same as `proc-macro = true`
+ // and that everything, including rustdoc, works correctly.
+ let foo = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+ [dependencies]
+ pm = { path = "pm" }
+ "#,
+ )
+ .file(
+ "src/lib.rs",
+ r#"
+ //! ```
+ //! use foo::THING;
+ //! assert_eq!(THING, 123);
+ //! ```
+ #[macro_use]
+ extern crate pm;
+ #[derive(MkItem)]
+ pub struct S;
+ #[cfg(test)]
+ mod tests {
+ use super::THING;
+ #[test]
+ fn it_works() {
+ assert_eq!(THING, 123);
+ }
+ }
+ "#,
+ )
+ .file(
+ "pm/Cargo.toml",
+ r#"
+ [package]
+ name = "pm"
+ version = "0.1.0"
+ [lib]
+ crate-type = ["proc-macro"]
+ "#,
+ )
+ .file(
+ "pm/src/lib.rs",
+ r#"
+ extern crate proc_macro;
+ use proc_macro::TokenStream;
+
+ #[proc_macro_derive(MkItem)]
+ pub fn mk_item(_input: TokenStream) -> TokenStream {
+ "pub const THING: i32 = 123;".parse().unwrap()
+ }
+ "#,
+ )
+ .build();
+
+ foo.cargo("test")
+ .with_stdout_contains("test tests::it_works ... ok")
+ .with_stdout_contains_n("test [..] ... ok", 2)
+ .run();
+}
+
+#[cargo_test]
+fn proc_macro_crate_type_warning() {
+ let foo = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+ [lib]
+ crate-type = ["proc-macro"]
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ foo.cargo("check")
+ .with_stderr_contains(
+ "[WARNING] library `foo` should only specify `proc-macro = true` instead of setting `crate-type`")
+ .run();
+}
+
+#[cargo_test]
+fn proc_macro_conflicting_warning() {
+ let foo = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+ [lib]
+ proc-macro = false
+ proc_macro = true
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ foo.cargo("check")
+ .with_stderr_contains(
+"[WARNING] conflicting between `proc-macro` and `proc_macro` in the `foo` library target.\n
+ `proc_macro` is ignored and not recommended for use in the future",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn proc_macro_crate_type_warning_plugin() {
+ let foo = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+ [lib]
+ crate-type = ["proc-macro"]
+ plugin = true
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ foo.cargo("check")
+ .with_stderr_contains(
+ "[WARNING] proc-macro library `foo` should not specify `plugin = true`")
+ .with_stderr_contains(
+ "[WARNING] library `foo` should only specify `proc-macro = true` instead of setting `crate-type`")
+ .run();
+}
+
+#[cargo_test]
+fn proc_macro_crate_type_multiple() {
+ let foo = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+ [lib]
+ crate-type = ["proc-macro", "rlib"]
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ foo.cargo("check")
+ .with_stderr(
+ "\
+[ERROR] failed to parse manifest at `[..]/foo/Cargo.toml`
+
+Caused by:
+ cannot mix `proc-macro` crate type with others
+",
+ )
+ .with_status(101)
+ .run();
+}
+
+#[cargo_test]
+fn proc_macro_extern_prelude() {
+ // Check that proc_macro is in the extern prelude.
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+ edition = "2018"
+ [lib]
+ proc-macro = true
+ "#,
+ )
+ .file(
+ "src/lib.rs",
+ r#"
+ use proc_macro::TokenStream;
+ #[proc_macro]
+ pub fn foo(input: TokenStream) -> TokenStream {
+ "".parse().unwrap()
+ }
+ "#,
+ )
+ .build();
+ p.cargo("test").run();
+ p.cargo("doc").run();
+}
+
+#[cargo_test]
+fn proc_macro_built_once() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [workspace]
+ members = ['a', 'b']
+ resolver = "2"
+ "#,
+ )
+ .file(
+ "a/Cargo.toml",
+ r#"
+ [package]
+ name = "a"
+ version = "0.1.0"
+
+ [build-dependencies]
+ the-macro = { path = '../the-macro' }
+ "#,
+ )
+ .file("a/build.rs", "fn main() {}")
+ .file("a/src/main.rs", "fn main() {}")
+ .file(
+ "b/Cargo.toml",
+ r#"
+ [package]
+ name = "b"
+ version = "0.1.0"
+
+ [dependencies]
+ the-macro = { path = '../the-macro', features = ['a'] }
+ "#,
+ )
+ .file("b/src/main.rs", "fn main() {}")
+ .file(
+ "the-macro/Cargo.toml",
+ r#"
+ [package]
+ name = "the-macro"
+ version = "0.1.0"
+
+ [lib]
+ proc_macro = true
+
+ [features]
+ a = []
+ "#,
+ )
+ .file("the-macro/src/lib.rs", "")
+ .build();
+ p.cargo("build --verbose")
+ .with_stderr_unordered(
+ "\
+[COMPILING] the-macro [..]
+[RUNNING] `rustc --crate-name the_macro [..]`
+[COMPILING] b [..]
+[RUNNING] `rustc --crate-name b [..]`
+[COMPILING] a [..]
+[RUNNING] `rustc --crate-name build_script_build [..]`
+[RUNNING] `[..]build[..]script[..]build[..]`
+[RUNNING] `rustc --crate-name a [..]`
+[FINISHED] [..]
+",
+ )
+ .run();
+}