From 8edd517627f4f9e76e1dd22bed1be322c047b812 Mon Sep 17 00:00:00 2001 From: Oliver Middleton Date: Thu, 5 Jan 2017 22:26:04 +0000 Subject: [PATCH 01/10] rustbuild: Stop building docs for std dependancies --- src/bootstrap/doc.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs index 6a6af295932cc..15cb16fad3ce8 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -147,7 +147,16 @@ pub fn std(build: &Build, stage: u32, target: &str) { cargo.arg("--manifest-path") .arg(build.src.join("src/rustc/std_shim/Cargo.toml")) .arg("--features").arg(build.std_features()) - .arg("-p").arg("std"); + .arg("--no-deps"); + + for krate in &["alloc", "collections", "core", "std", "std_unicode"] { + cargo.arg("-p").arg(krate); + // Create all crate output directories first to make sure rustdoc uses + // relative links. + // FIXME: Cargo should probably do this itself. + t!(fs::create_dir_all(out_dir.join(krate))); + } + build.run(&mut cargo); cp_r(&out_dir, &out) } From 8b3311d40a9c145f606203032156e0d11c926b3f Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 4 Jan 2017 15:32:39 -0800 Subject: [PATCH 02/10] std: Don't pass overlapped handles to processes This commit fixes a mistake introduced in #31618 where overlapped handles were leaked to child processes on Windows. On Windows once a handle is in overlapped mode it should always have I/O executed with an instance of `OVERLAPPED`. Most child processes, however, are not prepared to have their stdio handles in overlapped mode as they don't use `OVERLAPPED` on reads/writes to the handle. Now we haven't had any odd behavior in Rust up to this point, and the original bug was introduced almost a year ago. I believe this is because it turns out that if you *don't* pass an `OVERLAPPED` then the system will [supply one for you][link]. In this case everything will go awry if you concurrently operate on the handle. In Rust, however, the stdio handles are always locked, and there's no way to not use them unlocked in libstd. Due to that change we've always had synchronized access to these handles, which means that Rust programs typically "just work". Conversely, though, this commit fixes the test case included, which exhibits behavior that other programs Rust spawns may attempt to execute. Namely, the stdio handles may be concurrently used and having them in overlapped mode wreaks havoc. [link]: https://blogs.msdn.microsoft.com/oldnewthing/20121012-00/?p=6343 Closes #38811 --- src/libstd/sys/windows/c.rs | 1 + src/libstd/sys/windows/pipe.rs | 77 ++++++++++++++++------ src/libstd/sys/windows/process.rs | 14 ++-- src/test/run-pass/stdio-is-blocking.rs | 90 ++++++++++++++++++++++++++ 4 files changed, 153 insertions(+), 29 deletions(-) create mode 100644 src/test/run-pass/stdio-is-blocking.rs diff --git a/src/libstd/sys/windows/c.rs b/src/libstd/sys/windows/c.rs index 5384ef46e9ae3..e99be0cfc5a09 100644 --- a/src/libstd/sys/windows/c.rs +++ b/src/libstd/sys/windows/c.rs @@ -282,6 +282,7 @@ pub const EXCEPTION_STACK_OVERFLOW: DWORD = 0xc00000fd; pub const EXCEPTION_MAXIMUM_PARAMETERS: usize = 15; pub const PIPE_ACCESS_INBOUND: DWORD = 0x00000001; +pub const PIPE_ACCESS_OUTBOUND: DWORD = 0x00000002; pub const FILE_FLAG_FIRST_PIPE_INSTANCE: DWORD = 0x00080000; pub const FILE_FLAG_OVERLAPPED: DWORD = 0x40000000; pub const PIPE_WAIT: DWORD = 0x00000000; diff --git a/src/libstd/sys/windows/pipe.rs b/src/libstd/sys/windows/pipe.rs index 1eb1730547642..8073473f7ff5b 100644 --- a/src/libstd/sys/windows/pipe.rs +++ b/src/libstd/sys/windows/pipe.rs @@ -29,18 +29,43 @@ pub struct AnonPipe { inner: Handle, } -pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { +pub struct Pipes { + pub ours: AnonPipe, + pub theirs: AnonPipe, +} + +/// Although this looks similar to `anon_pipe` in the Unix module it's actually +/// subtly different. Here we'll return two pipes in the `Pipes` return value, +/// but one is intended for "us" where as the other is intended for "someone +/// else". +/// +/// Currently the only use case for this function is pipes for stdio on +/// processes in the standard library, so "ours" is the one that'll stay in our +/// process whereas "theirs" will be inherited to a child. +/// +/// The ours/theirs pipes are *not* specifically readable or writable. Each +/// one only supports a read or a write, but which is which depends on the +/// boolean flag given. If `ours_readable` is true then `ours` is readable where +/// `theirs` is writable. Conversely if `ours_readable` is false then `ours` is +/// writable where `theirs` is readable. +/// +/// Also note that the `ours` pipe is always a handle opened up in overlapped +/// mode. This means that technically speaking it should only ever be used +/// with `OVERLAPPED` instances, but also works out ok if it's only ever used +/// once at a time (which we do indeed guarantee). +pub fn anon_pipe(ours_readable: bool) -> io::Result { // Note that we specifically do *not* use `CreatePipe` here because // unfortunately the anonymous pipes returned do not support overlapped - // operations. - // - // Instead, we create a "hopefully unique" name and create a named pipe - // which has overlapped operations enabled. + // operations. Instead, we create a "hopefully unique" name and create a + // named pipe which has overlapped operations enabled. // - // Once we do this, we connect do it as usual via `CreateFileW`, and then we - // return those reader/writer halves. + // Once we do this, we connect do it as usual via `CreateFileW`, and then + // we return those reader/writer halves. Note that the `ours` pipe return + // value is always the named pipe, whereas `theirs` is just the normal file. + // This should hopefully shield us from child processes which assume their + // stdout is a named pipe, which would indeed be odd! unsafe { - let reader; + let ours; let mut name; let mut tries = 0; let mut reject_remote_clients_flag = c::PIPE_REJECT_REMOTE_CLIENTS; @@ -54,11 +79,16 @@ pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { .encode_wide() .chain(Some(0)) .collect::>(); + let mut flags = c::FILE_FLAG_FIRST_PIPE_INSTANCE | + c::FILE_FLAG_OVERLAPPED; + if ours_readable { + flags |= c::PIPE_ACCESS_INBOUND; + } else { + flags |= c::PIPE_ACCESS_OUTBOUND; + } let handle = c::CreateNamedPipeW(wide_name.as_ptr(), - c::PIPE_ACCESS_INBOUND | - c::FILE_FLAG_FIRST_PIPE_INSTANCE | - c::FILE_FLAG_OVERLAPPED, + flags, c::PIPE_TYPE_BYTE | c::PIPE_READMODE_BYTE | c::PIPE_WAIT | @@ -101,21 +131,28 @@ pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { } return Err(err) } - reader = Handle::new(handle); + ours = Handle::new(handle); break } - // Connect to the named pipe we just created in write-only mode (also - // overlapped for async I/O below). + // Connect to the named pipe we just created. This handle is going to be + // returned in `theirs`, so if `ours` is readable we want this to be + // writable, otherwise if `ours` is writable we want this to be + // readable. + // + // Additionally we don't enable overlapped mode on this because most + // client processes aren't enabled to work with that. let mut opts = OpenOptions::new(); - opts.write(true); - opts.read(false); + opts.write(ours_readable); + opts.read(!ours_readable); opts.share_mode(0); - opts.attributes(c::FILE_FLAG_OVERLAPPED); - let writer = File::open(Path::new(&name), &opts)?; - let writer = AnonPipe { inner: writer.into_handle() }; + let theirs = File::open(Path::new(&name), &opts)?; + let theirs = AnonPipe { inner: theirs.into_handle() }; - Ok((AnonPipe { inner: reader }, AnonPipe { inner: writer.into_handle() })) + Ok(Pipes { + ours: AnonPipe { inner: ours }, + theirs: AnonPipe { inner: theirs.into_handle() }, + }) } } diff --git a/src/libstd/sys/windows/process.rs b/src/libstd/sys/windows/process.rs index 969de6b85a6aa..7dc8959e1b683 100644 --- a/src/libstd/sys/windows/process.rs +++ b/src/libstd/sys/windows/process.rs @@ -264,19 +264,15 @@ impl Stdio { } Stdio::MakePipe => { - let (reader, writer) = pipe::anon_pipe()?; - let (ours, theirs) = if stdio_id == c::STD_INPUT_HANDLE { - (writer, reader) - } else { - (reader, writer) - }; - *pipe = Some(ours); + let ours_readable = stdio_id != c::STD_INPUT_HANDLE; + let pipes = pipe::anon_pipe(ours_readable)?; + *pipe = Some(pipes.ours); cvt(unsafe { - c::SetHandleInformation(theirs.handle().raw(), + c::SetHandleInformation(pipes.theirs.handle().raw(), c::HANDLE_FLAG_INHERIT, c::HANDLE_FLAG_INHERIT) })?; - Ok(theirs.into_handle()) + Ok(pipes.theirs.into_handle()) } Stdio::Handle(ref handle) => { diff --git a/src/test/run-pass/stdio-is-blocking.rs b/src/test/run-pass/stdio-is-blocking.rs new file mode 100644 index 0000000000000..74170ca6506ec --- /dev/null +++ b/src/test/run-pass/stdio-is-blocking.rs @@ -0,0 +1,90 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::env; +use std::io::prelude::*; +use std::process::Command; +use std::thread; + +const THREADS: usize = 20; +const WRITES: usize = 100; +const WRITE_SIZE: usize = 1024 * 32; + +fn main() { + let args = env::args().collect::>(); + if args.len() == 1 { + parent(); + } else { + child(); + } +} + +fn parent() { + let me = env::current_exe().unwrap(); + let mut cmd = Command::new(me); + cmd.arg("run-the-test"); + let output = cmd.output().unwrap(); + assert!(output.status.success()); + assert_eq!(output.stderr.len(), 0); + assert_eq!(output.stdout.len(), WRITES * THREADS * WRITE_SIZE); + for byte in output.stdout.iter() { + assert_eq!(*byte, b'a'); + } +} + +fn child() { + let threads = (0..THREADS).map(|_| { + thread::spawn(|| { + let buf = [b'a'; WRITE_SIZE]; + for _ in 0..WRITES { + write_all(&buf); + } + }) + }).collect::>(); + + for thread in threads { + thread.join().unwrap(); + } +} + +#[cfg(unix)] +fn write_all(buf: &[u8]) { + use std::fs::File; + use std::mem; + use std::os::unix::prelude::*; + + let mut file = unsafe { File::from_raw_fd(1) }; + let res = file.write_all(buf); + mem::forget(file); + res.unwrap(); +} + +#[cfg(windows)] +fn write_all(buf: &[u8]) { + use std::fs::File; + use std::mem; + use std::os::windows::raw::*; + use std::os::windows::prelude::*; + + const STD_OUTPUT_HANDLE: u32 = (-11i32) as u32; + + extern "system" { + fn GetStdHandle(handle: u32) -> HANDLE; + } + + let mut file = unsafe { + let handle = GetStdHandle(STD_OUTPUT_HANDLE); + assert!(!handle.is_null()); + File::from_raw_handle(handle) + }; + let res = file.write_all(buf); + mem::forget(file); + res.unwrap(); +} From 8b7e7b8bb76b1d49e6a12d277f52c6c4c940ecf3 Mon Sep 17 00:00:00 2001 From: Doug Goldstein Date: Tue, 3 Jan 2017 23:44:10 -0600 Subject: [PATCH 03/10] fix help for the --print option Since 8285ab5c99, which was merged in with #38061, the help for the --print option is missing the surrounding [ ] around the possible options. Signed-off-by: Doug Goldstein --- src/librustc/session/config.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index d45d6f7964c82..5e3c8bc147a02 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -1172,7 +1172,8 @@ pub fn rustc_short_optgroups() -> Vec { the compiler to emit", "[asm|llvm-bc|llvm-ir|obj|link|dep-info]"), opt::multi_s("", "print", "Comma separated list of compiler information to \ - print on stdout", &print_opts.join("|")), + print on stdout", &format!("[{}]", + &print_opts.join("|"))), opt::flagmulti_s("g", "", "Equivalent to -C debuginfo=2"), opt::flagmulti_s("O", "", "Equivalent to -C opt-level=2"), opt::opt_s("o", "", "Write output to ", "FILENAME"), From 6b6036cb5d61bb175a8598f3eb7f56f34aef2d39 Mon Sep 17 00:00:00 2001 From: Mark Simulacrum Date: Thu, 5 Jan 2017 12:22:51 -0700 Subject: [PATCH 04/10] Fix ICE on i686 when calling immediate() on OperandValue::Ref in return --- src/librustc_trans/mir/block.rs | 6 +++++- src/test/run-pass/issue-38727.rs | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 src/test/run-pass/issue-38727.rs diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs index fe087bc495121..ff4fb17e14eaf 100644 --- a/src/librustc_trans/mir/block.rs +++ b/src/librustc_trans/mir/block.rs @@ -222,7 +222,11 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { load } else { let op = self.trans_consume(&bcx, &mir::Lvalue::Local(mir::RETURN_POINTER)); - op.pack_if_pair(&bcx).immediate() + if let Ref(llval) = op.val { + base::load_ty(&bcx, llval, op.ty) + } else { + op.pack_if_pair(&bcx).immediate() + } }; bcx.ret(llval); } diff --git a/src/test/run-pass/issue-38727.rs b/src/test/run-pass/issue-38727.rs new file mode 100644 index 0000000000000..e60b6a99f9213 --- /dev/null +++ b/src/test/run-pass/issue-38727.rs @@ -0,0 +1,21 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[repr(u64)] +enum A { + A = 0u64, + B = !0u64, +} + +fn cmp() -> A { + A::B +} + +fn main() {} From 49f1804c5828bf47fd7d312a3bac03ac16afdf6f Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 6 Jan 2017 08:56:14 -0500 Subject: [PATCH 05/10] rebase backport of #38855 onto beta --- src/librustc_trans/mir/block.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs index ff4fb17e14eaf..bba6574109627 100644 --- a/src/librustc_trans/mir/block.rs +++ b/src/librustc_trans/mir/block.rs @@ -223,7 +223,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } else { let op = self.trans_consume(&bcx, &mir::Lvalue::Local(mir::RETURN_POINTER)); if let Ref(llval) = op.val { - base::load_ty(&bcx, llval, op.ty) + bcx.with_block(|bcx| base::load_ty(&bcx, llval, op.ty)) } else { op.pack_if_pair(&bcx).immediate() } From ca97e84f2bae5a1bf8f9dfe523eb5765e54e773b Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Fri, 6 Jan 2017 10:00:07 +1100 Subject: [PATCH 06/10] Add back in previous logic and remove span shrinking --- src/librustc_typeck/check/mod.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 58dff935a16b8..fb7a7e894fa49 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -4580,7 +4580,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // Check provided lifetime parameters. let lifetime_defs = segment.map_or(&[][..], |(_, generics)| &generics.regions); if lifetimes.len() > lifetime_defs.len() { - let span = lifetimes[lifetime_defs.len()].span; struct_span_err!(self.tcx.sess, span, E0088, "too many lifetime parameters provided: \ expected {}, found {}", @@ -4589,6 +4588,14 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { .span_label(span, &format!("unexpected lifetime parameter{}", match lifetimes.len() { 1 => "", _ => "s" })) .emit(); + } else if lifetimes.len() > 0 && lifetimes.len() < lifetime_defs.len() { + struct_span_err!(self.tcx.sess, span, E0090, + "too few lifetime parameters provided: \ + expected {}, found {}", + count(lifetime_defs.len()), + count(lifetimes.len())) + .span_label(span, &format!("too few lifetime parameters")) + .emit(); } // The case where there is not enough lifetime parameters is not checked, From 27764c4de2ed55f107b5cf38e30eb10fbd4820a5 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Fri, 6 Jan 2017 10:05:55 +1100 Subject: [PATCH 07/10] Add in test for E0090 --- src/test/compile-fail/E0090.rs | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 src/test/compile-fail/E0090.rs diff --git a/src/test/compile-fail/E0090.rs b/src/test/compile-fail/E0090.rs new file mode 100644 index 0000000000000..4656f527f1f4f --- /dev/null +++ b/src/test/compile-fail/E0090.rs @@ -0,0 +1,5 @@ +fn foo<'a: 'b, 'b: 'a>() {} +fn main() { + foo::<'static>();//~ ERROR E0090 + //~^ too few lifetime parameters +} From 48633125413877a769761474f5836c7311d6a731 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Fri, 6 Jan 2017 10:18:11 +1100 Subject: [PATCH 08/10] Fix tidy warning --- src/test/compile-fail/E0090.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/test/compile-fail/E0090.rs b/src/test/compile-fail/E0090.rs index 4656f527f1f4f..4600d2d63856a 100644 --- a/src/test/compile-fail/E0090.rs +++ b/src/test/compile-fail/E0090.rs @@ -1,3 +1,13 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + fn foo<'a: 'b, 'b: 'a>() {} fn main() { foo::<'static>();//~ ERROR E0090 From 62859c2c5e4518fe35ea99a2a123d348172a3453 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Tue, 3 Jan 2017 01:33:40 +0000 Subject: [PATCH 09/10] Fold all spans in the AST. --- src/libsyntax/fold.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 6af8efb2a195c..b7276d82b537f 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -544,19 +544,19 @@ pub fn noop_fold_arg(Arg {id, pat, ty}: Arg, fld: &mut T) -> Arg { pub fn noop_fold_tt(tt: &TokenTree, fld: &mut T) -> TokenTree { match *tt { TokenTree::Token(span, ref tok) => - TokenTree::Token(span, fld.fold_token(tok.clone())), + TokenTree::Token(fld.new_span(span), fld.fold_token(tok.clone())), TokenTree::Delimited(span, ref delimed) => { - TokenTree::Delimited(span, Rc::new( + TokenTree::Delimited(fld.new_span(span), Rc::new( Delimited { delim: delimed.delim, - open_span: delimed.open_span, + open_span: fld.new_span(delimed.open_span), tts: fld.fold_tts(&delimed.tts), - close_span: delimed.close_span, + close_span: fld.new_span(delimed.close_span), } )) }, TokenTree::Sequence(span, ref seq) => - TokenTree::Sequence(span, + TokenTree::Sequence(fld.new_span(span), Rc::new(SequenceRepetition { tts: fld.fold_tts(&seq.tts), separator: seq.separator.clone().map(|tok| fld.fold_token(tok)), @@ -649,7 +649,7 @@ pub fn noop_fold_fn_decl(decl: P, fld: &mut T) -> P { inputs: inputs.move_map(|x| fld.fold_arg(x)), output: match output { FunctionRetTy::Ty(ty) => FunctionRetTy::Ty(fld.fold_ty(ty)), - FunctionRetTy::Default(span) => FunctionRetTy::Default(span), + FunctionRetTy::Default(span) => FunctionRetTy::Default(fld.new_span(span)), }, variadic: variadic }) @@ -676,7 +676,7 @@ pub fn noop_fold_ty_param(tp: TyParam, fld: &mut T) -> TyParam { ident: ident, bounds: fld.fold_bounds(bounds), default: default.map(|x| fld.fold_ty(x)), - span: span + span: fld.new_span(span), } } From 793a39545cbdec08564ddc1f6e96baa0a0f893a9 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Tue, 3 Jan 2017 02:11:32 +0000 Subject: [PATCH 10/10] Add regression test. --- .../proc-macro/auxiliary/issue_38586.rs | 22 +++++++++++++++++++ .../proc-macro/issue-38586.rs | 21 ++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 src/test/compile-fail-fulldeps/proc-macro/auxiliary/issue_38586.rs create mode 100644 src/test/compile-fail-fulldeps/proc-macro/issue-38586.rs diff --git a/src/test/compile-fail-fulldeps/proc-macro/auxiliary/issue_38586.rs b/src/test/compile-fail-fulldeps/proc-macro/auxiliary/issue_38586.rs new file mode 100644 index 0000000000000..10da846a86c5f --- /dev/null +++ b/src/test/compile-fail-fulldeps/proc-macro/auxiliary/issue_38586.rs @@ -0,0 +1,22 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// force-host +// no-prefer-dynamic + +#![feature(proc_macro, proc_macro_lib)] +#![crate_type = "proc-macro"] + +extern crate proc_macro; + +#[proc_macro_derive(A)] +pub fn derive_a(_: proc_macro::TokenStream) -> proc_macro::TokenStream { + "fn f() { println!(\"{}\", foo); }".parse().unwrap() +} diff --git a/src/test/compile-fail-fulldeps/proc-macro/issue-38586.rs b/src/test/compile-fail-fulldeps/proc-macro/issue-38586.rs new file mode 100644 index 0000000000000..42475e6de90c9 --- /dev/null +++ b/src/test/compile-fail-fulldeps/proc-macro/issue-38586.rs @@ -0,0 +1,21 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:issue_38586.rs + +#![feature(proc_macro)] + +#[macro_use] +extern crate issue_38586; + +#[derive(A)] //~ ERROR `foo` +struct A; + +fn main() {}