Skip to content

Commit afaffa1

Browse files
authored
Auto merge of #3064 - dtolnay:rustc-macro, r=alexcrichton
Macros 1.1 Tested with serde-rs/serde#530. This should be able to merge independently of rust-lang/rust#35957. r? @alexcrichton
2 parents 553e6d1 + 7ee6f57 commit afaffa1

File tree

5 files changed

+227
-6
lines changed

5 files changed

+227
-6
lines changed

src/cargo/core/manifest.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ pub enum LibKind {
6161
Lib,
6262
Rlib,
6363
Dylib,
64+
RustcMacro,
6465
Other(String),
6566
}
6667

@@ -70,6 +71,7 @@ impl LibKind {
7071
"lib" => LibKind::Lib,
7172
"rlib" => LibKind::Rlib,
7273
"dylib" => LibKind::Dylib,
74+
"rustc-macro" => LibKind::RustcMacro,
7375
s => LibKind::Other(s.to_string()),
7476
}
7577
}
@@ -80,6 +82,7 @@ impl LibKind {
8082
LibKind::Lib => "lib",
8183
LibKind::Rlib => "rlib",
8284
LibKind::Dylib => "dylib",
85+
LibKind::RustcMacro => "rustc-macro",
8386
LibKind::Other(ref s) => s,
8487
}
8588
}
@@ -88,7 +91,8 @@ impl LibKind {
8891
match *self {
8992
LibKind::Lib |
9093
LibKind::Rlib |
91-
LibKind::Dylib => true,
94+
LibKind::Dylib |
95+
LibKind::RustcMacro => true,
9296
LibKind::Other(..) => false,
9397
}
9498
}

src/cargo/util/toml.rs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,7 @@ impl TomlManifest {
444444
let lib = match self.lib {
445445
Some(ref lib) => {
446446
try!(lib.validate_library_name());
447+
try!(lib.validate_crate_type());
447448
Some(
448449
TomlTarget {
449450
name: lib.name.clone().or(Some(project.name.clone())),
@@ -900,6 +901,7 @@ struct TomlTarget {
900901
bench: Option<bool>,
901902
doc: Option<bool>,
902903
plugin: Option<bool>,
904+
rustc_macro: Option<bool>,
903905
harness: Option<bool>,
904906
}
905907

@@ -928,6 +930,7 @@ impl TomlTarget {
928930
bench: None,
929931
doc: None,
930932
plugin: None,
933+
rustc_macro: None,
931934
harness: None,
932935
}
933936
}
@@ -1006,6 +1009,23 @@ impl TomlTarget {
10061009
None => Err(human("bench target bench.name is required".to_string()))
10071010
}
10081011
}
1012+
1013+
fn validate_crate_type(&self) -> CargoResult<()> {
1014+
// Per the Macros 1.1 RFC:
1015+
//
1016+
// > Initially if a crate is compiled with the rustc-macro crate type
1017+
// > (and possibly others) it will forbid exporting any items in the
1018+
// > crate other than those functions tagged #[rustc_macro_derive] and
1019+
// > those functions must also be placed at the crate root.
1020+
//
1021+
// A plugin requires exporting plugin_registrar so a crate cannot be
1022+
// both at once.
1023+
if self.plugin == Some(true) && self.rustc_macro == Some(true) {
1024+
Err(human("lib.plugin and lib.rustc-macro cannot both be true".to_string()))
1025+
} else {
1026+
Ok(())
1027+
}
1028+
}
10091029
}
10101030

10111031
impl PathValue {
@@ -1040,7 +1060,11 @@ fn normalize(lib: &Option<TomlLibTarget>,
10401060
.set_doctest(toml.doctest.unwrap_or(t2.doctested()))
10411061
.set_benched(toml.bench.unwrap_or(t2.benched()))
10421062
.set_harness(toml.harness.unwrap_or(t2.harness()))
1043-
.set_for_host(toml.plugin.unwrap_or(t2.for_host()));
1063+
.set_for_host(match (toml.plugin, toml.rustc_macro) {
1064+
(None, None) => t2.for_host(),
1065+
(Some(true), _) | (_, Some(true)) => true,
1066+
(Some(false), _) | (_, Some(false)) => false,
1067+
});
10441068
}
10451069

10461070
fn lib_target(dst: &mut Vec<Target>,
@@ -1053,6 +1077,7 @@ fn normalize(lib: &Option<TomlLibTarget>,
10531077
Some(kinds) => kinds.iter().map(|s| LibKind::from_str(s)).collect(),
10541078
None => {
10551079
vec![ if l.plugin == Some(true) {LibKind::Dylib}
1080+
else if l.rustc_macro == Some(true) {LibKind::RustcMacro}
10561081
else {LibKind::Lib} ]
10571082
}
10581083
};

src/doc/manifest.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,10 @@ doc = true
510510
# for Cargo to correctly compile it and make it available for all dependencies.
511511
plugin = false
512512

513+
# If the target is meant to be a "macros 1.1" procedural macro, this field must
514+
# be set to true.
515+
rustc-macro = false
516+
513517
# If set to false, `cargo test` will omit the `--test` flag to rustc, which
514518
# stops it from generating a test harness. This is useful when the binary being
515519
# built manages the test runner itself.
@@ -529,9 +533,10 @@ name = "..."
529533
crate-type = ["dylib"] # could be `staticlib` as well
530534
```
531535

532-
The available options are `dylib`, `rlib`, `staticlib`, and `cdylib`. You
533-
should only use this option in a project. Cargo will always compile packages
534-
(dependencies) based on the requirements of the project that includes them.
536+
The available options are `dylib`, `rlib`, `staticlib`, `cdylib`, and
537+
`rustc-macro`. You should only use this option in a project. Cargo will always
538+
compile packages (dependencies) based on the requirements of the project that
539+
includes them.
535540

536541
You can read more about the different crate types in the
537542
[Rust Reference Manual](https://doc.rust-lang.org/reference.html#linkage)

src/rustversion.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2016-07-12
1+
2016-09-05

tests/rustc-macro.rs

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
extern crate cargotest;
2+
extern crate hamcrest;
3+
4+
use cargotest::is_nightly;
5+
use cargotest::support::{project, execs};
6+
use hamcrest::assert_that;
7+
8+
#[test]
9+
fn noop() {
10+
if !is_nightly() {
11+
return;
12+
}
13+
14+
let client = project("client")
15+
.file("Cargo.toml", r#"
16+
[package]
17+
name = "client"
18+
version = "0.0.1"
19+
authors = []
20+
21+
[dependencies.noop]
22+
path = "../noop"
23+
"#)
24+
.file("src/main.rs", r#"
25+
#![feature(rustc_macro)]
26+
27+
#[macro_use]
28+
extern crate noop;
29+
30+
#[derive(Noop)]
31+
struct X;
32+
33+
fn main() {}
34+
"#);
35+
let noop = project("noop")
36+
.file("Cargo.toml", r#"
37+
[package]
38+
name = "noop"
39+
version = "0.0.1"
40+
authors = []
41+
42+
[lib]
43+
rustc-macro = true
44+
"#)
45+
.file("src/lib.rs", r#"
46+
#![feature(rustc_macro, rustc_macro_lib)]
47+
48+
extern crate rustc_macro;
49+
use rustc_macro::TokenStream;
50+
51+
#[rustc_macro_derive(Noop)]
52+
pub fn noop(input: TokenStream) -> TokenStream {
53+
input
54+
}
55+
"#);
56+
noop.build();
57+
58+
assert_that(client.cargo_process("build"),
59+
execs().with_status(0));
60+
assert_that(client.cargo("build"),
61+
execs().with_status(0));
62+
}
63+
64+
#[test]
65+
fn impl_and_derive() {
66+
if !is_nightly() {
67+
return;
68+
}
69+
70+
let client = project("client")
71+
.file("Cargo.toml", r#"
72+
[package]
73+
name = "client"
74+
version = "0.0.1"
75+
authors = []
76+
77+
[dependencies.transmogrify]
78+
path = "../transmogrify"
79+
"#)
80+
.file("src/main.rs", r#"
81+
#![feature(rustc_macro)]
82+
83+
#[macro_use]
84+
extern crate transmogrify;
85+
86+
trait ImplByTransmogrify {
87+
fn impl_by_transmogrify(&self) -> bool;
88+
}
89+
90+
#[derive(Transmogrify)]
91+
struct X;
92+
93+
fn main() {
94+
let x = X::new();
95+
assert!(x.impl_by_transmogrify());
96+
println!("{:?}", x);
97+
}
98+
"#);
99+
let transmogrify = project("transmogrify")
100+
.file("Cargo.toml", r#"
101+
[package]
102+
name = "transmogrify"
103+
version = "0.0.1"
104+
authors = []
105+
106+
[lib]
107+
rustc-macro = true
108+
"#)
109+
.file("src/lib.rs", r#"
110+
#![feature(rustc_macro, rustc_macro_lib)]
111+
112+
extern crate rustc_macro;
113+
use rustc_macro::TokenStream;
114+
115+
#[rustc_macro_derive(Transmogrify)]
116+
#[doc(hidden)]
117+
pub fn transmogrify(input: TokenStream) -> TokenStream {
118+
assert_eq!(input.to_string(), "struct X;\n");
119+
120+
"
121+
impl X {
122+
fn new() -> Self {
123+
X { success: true }
124+
}
125+
}
126+
127+
impl ImplByTransmogrify for X {
128+
fn impl_by_transmogrify(&self) -> bool {
129+
true
130+
}
131+
}
132+
133+
#[derive(Debug)]
134+
struct X {
135+
success: bool,
136+
}
137+
".parse().unwrap()
138+
}
139+
"#);
140+
transmogrify.build();
141+
142+
assert_that(client.cargo_process("build"),
143+
execs().with_status(0));
144+
assert_that(client.cargo("run"),
145+
execs().with_status(0).with_stdout("X { success: true }"));
146+
}
147+
148+
#[test]
149+
fn plugin_and_rustc_macro() {
150+
if !is_nightly() {
151+
return;
152+
}
153+
154+
let questionable = project("questionable")
155+
.file("Cargo.toml", r#"
156+
[package]
157+
name = "questionable"
158+
version = "0.0.1"
159+
authors = []
160+
161+
[lib]
162+
plugin = true
163+
rustc-macro = true
164+
"#)
165+
.file("src/lib.rs", r#"
166+
#![feature(plugin_registrar, rustc_private)]
167+
#![feature(rustc_macro, rustc_macro_lib)]
168+
169+
extern crate rustc_plugin;
170+
use rustc_plugin::Registry;
171+
172+
extern crate rustc_macro;
173+
use rustc_macro::TokenStream;
174+
175+
#[plugin_registrar]
176+
pub fn plugin_registrar(reg: &mut Registry) {}
177+
178+
#[rustc_macro_derive(Questionable)]
179+
pub fn questionable(input: TokenStream) -> TokenStream {
180+
input
181+
}
182+
"#);
183+
184+
let msg = " lib.plugin and lib.rustc-macro cannot both be true";
185+
assert_that(questionable.cargo_process("build"),
186+
execs().with_status(101).with_stderr_contains(msg));
187+
}

0 commit comments

Comments
 (0)