diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index 367ef8b395f..d19bac3740d 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -1474,7 +1474,14 @@ impl TomlTarget { } fn proc_macro(&self) -> Option { - self.proc_macro.or(self.proc_macro2) + self.proc_macro.or(self.proc_macro2).or_else(|| { + if let Some(types) = self.crate_types() { + if types.contains(&"proc-macro".to_string()) { + return Some(true); + } + } + None + }) } fn crate_types(&self) -> Option<&Vec> { diff --git a/src/cargo/util/toml/targets.rs b/src/cargo/util/toml/targets.rs index cc40b536ce3..b86300225fe 100644 --- a/src/cargo/util/toml/targets.rs +++ b/src/cargo/util/toml/targets.rs @@ -206,6 +206,23 @@ fn clean_lib( // A plugin requires exporting plugin_registrar so a crate cannot be // both at once. let crate_types = match (lib.crate_types(), lib.plugin, lib.proc_macro()) { + (Some(kinds), _, _) if kinds.contains(&"proc-macro".to_string()) => { + if let Some(true) = lib.plugin { + // This is a warning to retain backwards compatibility. + warnings.push(format!( + "proc-macro library `{}` should not specify `plugin = true`", + lib.name() + )); + } + warnings.push(format!( + "library `{}` should only specify `proc-macro = true` instead of setting `crate-type`", + lib.name() + )); + if kinds.len() > 1 { + bail!("cannot mix `proc-macro` crate type with others"); + } + vec![LibKind::ProcMacro] + } (_, Some(true), Some(true)) => bail!("lib.plugin and lib.proc-macro cannot both be true"), (Some(kinds), _, _) => kinds.iter().map(|s| s.into()).collect(), (None, Some(true), _) => vec![LibKind::Dylib], diff --git a/tests/testsuite/proc_macro.rs b/tests/testsuite/proc_macro.rs index e843eeaad6d..b5af731b411 100644 --- a/tests/testsuite/proc_macro.rs +++ b/tests/testsuite/proc_macro.rs @@ -279,3 +279,145 @@ fn a() { .with_stdout_contains_n("test [..] ... ok", 2) .run(); } + +#[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(); +} + +#[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("build") + .with_stderr_contains( + "[WARNING] library `foo` should only specify `proc-macro = true` instead of setting `crate-type`") + .run(); +} + +#[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("build") + .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(); +} + +#[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("build") + .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(); +}