]> git.proxmox.com Git - cargo.git/blob - tests/testsuite/proc_macro.rs
Add RegistryBuilder to help initializing test registries.
[cargo.git] / tests / testsuite / proc_macro.rs
1 //! Tests for proc-macros.
2
3 use cargo_test_support::is_nightly;
4 use cargo_test_support::project;
5
6 #[cargo_test]
7 fn probe_cfg_before_crate_type_discovery() {
8 let p = project()
9 .file(
10 "Cargo.toml",
11 r#"
12 [package]
13 name = "foo"
14 version = "0.0.1"
15 authors = []
16
17 [target.'cfg(not(stage300))'.dependencies.noop]
18 path = "../noop"
19 "#,
20 )
21 .file(
22 "src/main.rs",
23 r#"
24 #[macro_use]
25 extern crate noop;
26
27 #[derive(Noop)]
28 struct X;
29
30 fn main() {}
31 "#,
32 )
33 .build();
34 let _noop = project()
35 .at("noop")
36 .file(
37 "Cargo.toml",
38 r#"
39 [package]
40 name = "noop"
41 version = "0.0.1"
42 authors = []
43
44 [lib]
45 proc-macro = true
46 "#,
47 )
48 .file(
49 "src/lib.rs",
50 r#"
51 extern crate proc_macro;
52 use proc_macro::TokenStream;
53
54 #[proc_macro_derive(Noop)]
55 pub fn noop(_input: TokenStream) -> TokenStream {
56 "".parse().unwrap()
57 }
58 "#,
59 )
60 .build();
61
62 p.cargo("build").run();
63 }
64
65 #[cargo_test]
66 fn noop() {
67 let p = project()
68 .file(
69 "Cargo.toml",
70 r#"
71 [package]
72 name = "foo"
73 version = "0.0.1"
74 authors = []
75
76 [dependencies.noop]
77 path = "../noop"
78 "#,
79 )
80 .file(
81 "src/main.rs",
82 r#"
83 #[macro_use]
84 extern crate noop;
85
86 #[derive(Noop)]
87 struct X;
88
89 fn main() {}
90 "#,
91 )
92 .build();
93 let _noop = project()
94 .at("noop")
95 .file(
96 "Cargo.toml",
97 r#"
98 [package]
99 name = "noop"
100 version = "0.0.1"
101 authors = []
102
103 [lib]
104 proc-macro = true
105 "#,
106 )
107 .file(
108 "src/lib.rs",
109 r#"
110 extern crate proc_macro;
111 use proc_macro::TokenStream;
112
113 #[proc_macro_derive(Noop)]
114 pub fn noop(_input: TokenStream) -> TokenStream {
115 "".parse().unwrap()
116 }
117 "#,
118 )
119 .build();
120
121 p.cargo("build").run();
122 p.cargo("build").run();
123 }
124
125 #[cargo_test]
126 fn impl_and_derive() {
127 let p = project()
128 .file(
129 "Cargo.toml",
130 r#"
131 [package]
132 name = "foo"
133 version = "0.0.1"
134 authors = []
135
136 [dependencies.transmogrify]
137 path = "../transmogrify"
138 "#,
139 )
140 .file(
141 "src/main.rs",
142 r#"
143 #[macro_use]
144 extern crate transmogrify;
145
146 trait ImplByTransmogrify {
147 fn impl_by_transmogrify(&self) -> bool;
148 }
149
150 #[derive(Transmogrify, Debug)]
151 struct X { success: bool }
152
153 fn main() {
154 let x = X::new();
155 assert!(x.impl_by_transmogrify());
156 println!("{:?}", x);
157 }
158 "#,
159 )
160 .build();
161 let _transmogrify = project()
162 .at("transmogrify")
163 .file(
164 "Cargo.toml",
165 r#"
166 [package]
167 name = "transmogrify"
168 version = "0.0.1"
169 authors = []
170
171 [lib]
172 proc-macro = true
173 "#,
174 )
175 .file(
176 "src/lib.rs",
177 r#"
178 extern crate proc_macro;
179 use proc_macro::TokenStream;
180
181 #[proc_macro_derive(Transmogrify)]
182 #[doc(hidden)]
183 pub fn transmogrify(input: TokenStream) -> TokenStream {
184 "
185 impl X {
186 fn new() -> Self {
187 X { success: true }
188 }
189 }
190
191 impl ImplByTransmogrify for X {
192 fn impl_by_transmogrify(&self) -> bool {
193 true
194 }
195 }
196 ".parse().unwrap()
197 }
198 "#,
199 )
200 .build();
201
202 p.cargo("build").run();
203 p.cargo("run").with_stdout("X { success: true }").run();
204 }
205
206 #[cargo_test]
207 fn plugin_and_proc_macro() {
208 if !is_nightly() {
209 // plugins are unstable
210 return;
211 }
212
213 let p = project()
214 .file(
215 "Cargo.toml",
216 r#"
217 [package]
218 name = "foo"
219 version = "0.0.1"
220 authors = []
221
222 [lib]
223 plugin = true
224 proc-macro = true
225 "#,
226 )
227 .file(
228 "src/lib.rs",
229 r#"
230 #![feature(plugin_registrar, rustc_private)]
231 #![feature(proc_macro, proc_macro_lib)]
232
233 extern crate rustc_driver;
234 use rustc_driver::plugin::Registry;
235
236 extern crate proc_macro;
237 use proc_macro::TokenStream;
238
239 #[plugin_registrar]
240 pub fn plugin_registrar(reg: &mut Registry) {}
241
242 #[proc_macro_derive(Questionable)]
243 pub fn questionable(input: TokenStream) -> TokenStream {
244 input
245 }
246 "#,
247 )
248 .build();
249
250 let msg = " `lib.plugin` and `lib.proc-macro` cannot both be `true`";
251 p.cargo("build")
252 .with_status(101)
253 .with_stderr_contains(msg)
254 .run();
255 }
256
257 #[cargo_test]
258 fn proc_macro_doctest() {
259 let foo = project()
260 .file(
261 "Cargo.toml",
262 r#"
263 [package]
264 name = "foo"
265 version = "0.1.0"
266 authors = []
267 [lib]
268 proc-macro = true
269 "#,
270 )
271 .file(
272 "src/lib.rs",
273 r#"
274 #![crate_type = "proc-macro"]
275
276 extern crate proc_macro;
277
278 use proc_macro::TokenStream;
279
280 /// ```
281 /// assert!(true);
282 /// ```
283 #[proc_macro_derive(Bar)]
284 pub fn derive(_input: TokenStream) -> TokenStream {
285 "".parse().unwrap()
286 }
287
288 #[test]
289 fn a() {
290 assert!(true);
291 }
292 "#,
293 )
294 .build();
295
296 foo.cargo("test")
297 .with_stdout_contains("test a ... ok")
298 .with_stdout_contains_n("test [..] ... ok", 2)
299 .run();
300 }
301
302 #[cargo_test]
303 fn proc_macro_crate_type() {
304 // Verify that `crate-type = ["proc-macro"]` is the same as `proc-macro = true`
305 // and that everything, including rustdoc, works correctly.
306 let foo = project()
307 .file(
308 "Cargo.toml",
309 r#"
310 [package]
311 name = "foo"
312 version = "0.1.0"
313 [dependencies]
314 pm = { path = "pm" }
315 "#,
316 )
317 .file(
318 "src/lib.rs",
319 r#"
320 //! ```
321 //! use foo::THING;
322 //! assert_eq!(THING, 123);
323 //! ```
324 #[macro_use]
325 extern crate pm;
326 #[derive(MkItem)]
327 pub struct S;
328 #[cfg(test)]
329 mod tests {
330 use super::THING;
331 #[test]
332 fn it_works() {
333 assert_eq!(THING, 123);
334 }
335 }
336 "#,
337 )
338 .file(
339 "pm/Cargo.toml",
340 r#"
341 [package]
342 name = "pm"
343 version = "0.1.0"
344 [lib]
345 crate-type = ["proc-macro"]
346 "#,
347 )
348 .file(
349 "pm/src/lib.rs",
350 r#"
351 extern crate proc_macro;
352 use proc_macro::TokenStream;
353
354 #[proc_macro_derive(MkItem)]
355 pub fn mk_item(_input: TokenStream) -> TokenStream {
356 "pub const THING: i32 = 123;".parse().unwrap()
357 }
358 "#,
359 )
360 .build();
361
362 foo.cargo("test")
363 .with_stdout_contains("test tests::it_works ... ok")
364 .with_stdout_contains_n("test [..] ... ok", 2)
365 .run();
366 }
367
368 #[cargo_test]
369 fn proc_macro_crate_type_warning() {
370 let foo = project()
371 .file(
372 "Cargo.toml",
373 r#"
374 [package]
375 name = "foo"
376 version = "0.1.0"
377 [lib]
378 crate-type = ["proc-macro"]
379 "#,
380 )
381 .file("src/lib.rs", "")
382 .build();
383
384 foo.cargo("build")
385 .with_stderr_contains(
386 "[WARNING] library `foo` should only specify `proc-macro = true` instead of setting `crate-type`")
387 .run();
388 }
389
390 #[cargo_test]
391 fn proc_macro_crate_type_warning_plugin() {
392 let foo = project()
393 .file(
394 "Cargo.toml",
395 r#"
396 [package]
397 name = "foo"
398 version = "0.1.0"
399 [lib]
400 crate-type = ["proc-macro"]
401 plugin = true
402 "#,
403 )
404 .file("src/lib.rs", "")
405 .build();
406
407 foo.cargo("build")
408 .with_stderr_contains(
409 "[WARNING] proc-macro library `foo` should not specify `plugin = true`")
410 .with_stderr_contains(
411 "[WARNING] library `foo` should only specify `proc-macro = true` instead of setting `crate-type`")
412 .run();
413 }
414
415 #[cargo_test]
416 fn proc_macro_crate_type_multiple() {
417 let foo = project()
418 .file(
419 "Cargo.toml",
420 r#"
421 [package]
422 name = "foo"
423 version = "0.1.0"
424 [lib]
425 crate-type = ["proc-macro", "rlib"]
426 "#,
427 )
428 .file("src/lib.rs", "")
429 .build();
430
431 foo.cargo("build")
432 .with_stderr(
433 "\
434 [ERROR] failed to parse manifest at `[..]/foo/Cargo.toml`
435
436 Caused by:
437 cannot mix `proc-macro` crate type with others
438 ",
439 )
440 .with_status(101)
441 .run();
442 }
443
444 #[cargo_test]
445 fn proc_macro_extern_prelude() {
446 // Check that proc_macro is in the extern prelude.
447 let p = project()
448 .file(
449 "Cargo.toml",
450 r#"
451 [package]
452 name = "foo"
453 version = "0.1.0"
454 edition = "2018"
455 [lib]
456 proc-macro = true
457 "#,
458 )
459 .file(
460 "src/lib.rs",
461 r#"
462 use proc_macro::TokenStream;
463 #[proc_macro]
464 pub fn foo(input: TokenStream) -> TokenStream {
465 "".parse().unwrap()
466 }
467 "#,
468 )
469 .build();
470 p.cargo("test").run();
471 p.cargo("doc").run();
472 }
473
474 #[cargo_test]
475 fn proc_macro_built_once() {
476 let p = project()
477 .file(
478 "Cargo.toml",
479 r#"
480 [workspace]
481 members = ['a', 'b']
482 resolver = "2"
483 "#,
484 )
485 .file(
486 "a/Cargo.toml",
487 r#"
488 [package]
489 name = "a"
490 version = "0.1.0"
491
492 [build-dependencies]
493 the-macro = { path = '../the-macro' }
494 "#,
495 )
496 .file("a/build.rs", "fn main() {}")
497 .file("a/src/main.rs", "fn main() {}")
498 .file(
499 "b/Cargo.toml",
500 r#"
501 [package]
502 name = "b"
503 version = "0.1.0"
504
505 [dependencies]
506 the-macro = { path = '../the-macro', features = ['a'] }
507 "#,
508 )
509 .file("b/src/main.rs", "fn main() {}")
510 .file(
511 "the-macro/Cargo.toml",
512 r#"
513 [package]
514 name = "the-macro"
515 version = "0.1.0"
516
517 [lib]
518 proc_macro = true
519
520 [features]
521 a = []
522 "#,
523 )
524 .file("the-macro/src/lib.rs", "")
525 .build();
526 p.cargo("build --verbose")
527 .with_stderr_unordered(
528 "\
529 [COMPILING] the-macro [..]
530 [RUNNING] `rustc --crate-name the_macro [..]`
531 [COMPILING] b [..]
532 [RUNNING] `rustc --crate-name b [..]`
533 [COMPILING] a [..]
534 [RUNNING] `rustc --crate-name build_script_build [..]`
535 [RUNNING] `[..]build[..]script[..]build[..]`
536 [RUNNING] `rustc --crate-name a [..]`
537 [FINISHED] [..]
538 ",
539 )
540 .run();
541 }