]> git.lizzy.rs Git - rust.git/commitdiff
Auto merge of #31089 - fhahn:macro-ice, r=pnkfelix
authorbors <bors@rust-lang.org>
Wed, 27 Jan 2016 12:12:52 +0000 (12:12 +0000)
committerbors <bors@rust-lang.org>
Wed, 27 Jan 2016 12:12:52 +0000 (12:12 +0000)
This is a  work in progress PR that potentially should fix #29084, #28308, #25385, #28288, #31011. I think this may also adresse parts of  #2887.

The problem in this issues seems to be that when transcribing macro arguments, we just clone the argument Nonterminal, which still has to original spans. This leads to the unprintable spans. One solution would be to update the spans of the inserted argument to match the argument in the macro definition. So for [this testcase](https://github.com/rust-lang/rust/compare/master...fhahn:macro-ice?expand=1#diff-f7def7420c51621640707b6337726876R2) the error message would be displayed in the macro definition:

    src/test/compile-fail/issue-31011.rs:4:12: 4:22 error: attempted access of field `trace` on type `&T`, but no field with that name was found
    src/test/compile-fail/issue-31011.rs:4         if $ctx.trace {

Currently I've added a very simple `update_span` function, which updates the span of the outer-most expression of a `NtExpr`, but this `update_span` function should be updated to handle all Nonterminals. But I'm pretty new to the macro system and would like to check if this approach makes sense, before doing that.

12 files changed:
src/libsyntax/parse/parser.rs
src/libsyntax/parse/token.rs
src/test/compile-fail/issue-25385.rs [new file with mode: 0644]
src/test/compile-fail/issue-25386.rs [new file with mode: 0644]
src/test/compile-fail/issue-25793.rs [new file with mode: 0644]
src/test/compile-fail/issue-26093.rs [new file with mode: 0644]
src/test/compile-fail/issue-26094.rs [new file with mode: 0644]
src/test/compile-fail/issue-26237.rs [new file with mode: 0644]
src/test/compile-fail/issue-26480.rs [new file with mode: 0644]
src/test/compile-fail/issue-28308.rs [new file with mode: 0644]
src/test/compile-fail/issue-29084.rs [new file with mode: 0644]
src/test/compile-fail/issue-31011.rs [new file with mode: 0644]

index 2249faac6d7010627ab2a9120fe20ec1c8da2c30..b8e5642474c78be775e6268b1869f6d730abe570 100644 (file)
@@ -233,7 +233,6 @@ macro_rules! maybe_whole {
     )
 }
 
-
 fn maybe_append(mut lhs: Vec<Attribute>, rhs: Option<Vec<Attribute>>)
                 -> Vec<Attribute> {
     if let Some(ref attrs) = rhs {
@@ -255,6 +254,7 @@ pub struct Parser<'a> {
     pub cfg: CrateConfig,
     /// the previous token or None (only stashed sometimes).
     pub last_token: Option<Box<token::Token>>,
+    last_token_interpolated: bool,
     pub buffer: [TokenAndSpan; 4],
     pub buffer_start: isize,
     pub buffer_end: isize,
@@ -362,6 +362,7 @@ pub fn new(sess: &'a ParseSess,
             span: span,
             last_span: span,
             last_token: None,
+            last_token_interpolated: false,
             buffer: [
                 placeholder.clone(),
                 placeholder.clone(),
@@ -542,6 +543,19 @@ pub fn commit_stmt_expecting(&mut self, edible: token::Token) -> PResult<'a, ()>
         self.commit_stmt(&[edible], &[])
     }
 
+    /// returns the span of expr, if it was not interpolated or the span of the interpolated token
+    fn interpolated_or_expr_span(&self,
+                                 expr: PResult<'a, P<Expr>>)
+                                 -> PResult<'a, (Span, P<Expr>)> {
+        expr.map(|e| {
+            if self.last_token_interpolated {
+                (self.last_span, e)
+            } else {
+                (e.span, e)
+            }
+        })
+    }
+
     pub fn parse_ident(&mut self) -> PResult<'a, ast::Ident> {
         self.check_strict_keywords();
         self.check_reserved_keywords();
@@ -933,6 +947,7 @@ pub fn bump(&mut self) {
         } else {
             None
         };
+        self.last_token_interpolated = self.token.is_interpolated();
         let next = if self.buffer_start == self.buffer_end {
             self.reader.real_token()
         } else {
@@ -2328,18 +2343,20 @@ pub fn parse_dot_or_call_expr(&mut self,
                                   -> PResult<'a, P<Expr>> {
         let attrs = try!(self.parse_or_use_outer_attributes(already_parsed_attrs));
 
-        let b = try!(self.parse_bottom_expr());
-        self.parse_dot_or_call_expr_with(b, attrs)
+        let b = self.parse_bottom_expr();
+        let (span, b) = try!(self.interpolated_or_expr_span(b));
+        self.parse_dot_or_call_expr_with(b, span.lo, attrs)
     }
 
     pub fn parse_dot_or_call_expr_with(&mut self,
                                        e0: P<Expr>,
+                                       lo: BytePos,
                                        attrs: ThinAttributes)
                                        -> PResult<'a, P<Expr>> {
         // Stitch the list of outer attributes onto the return value.
         // A little bit ugly, but the best way given the current code
         // structure
-        self.parse_dot_or_call_expr_with_(e0)
+        self.parse_dot_or_call_expr_with_(e0, lo)
         .map(|expr|
             expr.map(|mut expr| {
                 expr.attrs.update(|a| a.prepend(attrs));
@@ -2366,7 +2383,8 @@ pub fn parse_dot_or_call_expr_with(&mut self,
     fn parse_dot_suffix(&mut self,
                         ident: Ident,
                         ident_span: Span,
-                        self_value: P<Expr>)
+                        self_value: P<Expr>,
+                        lo: BytePos)
                         -> PResult<'a, P<Expr>> {
         let (_, tys, bindings) = if self.eat(&token::ModSep) {
             try!(self.expect_lt());
@@ -2380,8 +2398,6 @@ fn parse_dot_suffix(&mut self,
             self.span_err(last_span, "type bindings are only permitted on trait paths");
         }
 
-        let lo = self_value.span.lo;
-
         Ok(match self.token {
             // expr.f() method call.
             token::OpenDelim(token::Paren) => {
@@ -2414,9 +2430,8 @@ fn parse_dot_suffix(&mut self,
         })
     }
 
-    fn parse_dot_or_call_expr_with_(&mut self, e0: P<Expr>) -> PResult<'a, P<Expr>> {
+    fn parse_dot_or_call_expr_with_(&mut self, e0: P<Expr>, lo: BytePos) -> PResult<'a, P<Expr>> {
         let mut e = e0;
-        let lo = e.span.lo;
         let mut hi;
         loop {
             // expr.f
@@ -2427,7 +2442,7 @@ fn parse_dot_or_call_expr_with_(&mut self, e0: P<Expr>) -> PResult<'a, P<Expr>>
                     hi = self.span.hi;
                     self.bump();
 
-                    e = try!(self.parse_dot_suffix(i, mk_sp(dot_pos, hi), e));
+                    e = try!(self.parse_dot_suffix(i, mk_sp(dot_pos, hi), e, lo));
                   }
                   token::Literal(token::Integer(n), suf) => {
                     let sp = self.span;
@@ -2480,7 +2495,7 @@ fn parse_dot_or_call_expr_with_(&mut self, e0: P<Expr>) -> PResult<'a, P<Expr>>
                     let dot_pos = self.last_span.hi;
                     e = try!(self.parse_dot_suffix(special_idents::invalid,
                                                    mk_sp(dot_pos, dot_pos),
-                                                   e));
+                                                   e, lo));
                   }
                 }
                 continue;
@@ -2715,27 +2730,31 @@ pub fn parse_prefix_expr(&mut self,
         let ex = match self.token {
             token::Not => {
                 self.bump();
-                let e = try!(self.parse_prefix_expr(None));
-                hi = e.span.hi;
+                let e = self.parse_prefix_expr(None);
+                let (span, e) = try!(self.interpolated_or_expr_span(e));
+                hi = span.hi;
                 self.mk_unary(UnNot, e)
             }
             token::BinOp(token::Minus) => {
                 self.bump();
-                let e = try!(self.parse_prefix_expr(None));
-                hi = e.span.hi;
+                let e = self.parse_prefix_expr(None);
+                let (span, e) = try!(self.interpolated_or_expr_span(e));
+                hi = span.hi;
                 self.mk_unary(UnNeg, e)
             }
             token::BinOp(token::Star) => {
                 self.bump();
-                let e = try!(self.parse_prefix_expr(None));
-                hi = e.span.hi;
+                let e = self.parse_prefix_expr(None);
+                let (span, e) = try!(self.interpolated_or_expr_span(e));
+                hi = span.hi;
                 self.mk_unary(UnDeref, e)
             }
             token::BinOp(token::And) | token::AndAnd => {
                 try!(self.expect_and());
                 let m = try!(self.parse_mutability());
-                let e = try!(self.parse_prefix_expr(None));
-                hi = e.span.hi;
+                let e = self.parse_prefix_expr(None);
+                let (span, e) = try!(self.interpolated_or_expr_span(e));
+                hi = span.hi;
                 ExprAddrOf(m, e)
             }
             token::Ident(..) if self.token.is_keyword(keywords::In) => {
@@ -2753,9 +2772,10 @@ pub fn parse_prefix_expr(&mut self,
             }
             token::Ident(..) if self.token.is_keyword(keywords::Box) => {
                 self.bump();
-                let subexpression = try!(self.parse_prefix_expr(None));
-                hi = subexpression.span.hi;
-                ExprBox(subexpression)
+                let e = self.parse_prefix_expr(None);
+                let (span, e) = try!(self.interpolated_or_expr_span(e));
+                hi = span.hi;
+                ExprBox(e)
             }
             _ => return self.parse_dot_or_call_expr(Some(attrs))
         };
@@ -2790,12 +2810,21 @@ pub fn parse_assoc_expr_with(&mut self,
                 try!(self.parse_prefix_expr(attrs))
             }
         };
+
+
         if self.expr_is_complete(&*lhs) {
             // Semi-statement forms are odd. See https://github.com/rust-lang/rust/issues/29071
             return Ok(lhs);
         }
         self.expected_tokens.push(TokenType::Operator);
         while let Some(op) = AssocOp::from_token(&self.token) {
+
+            let lhs_span = if self.last_token_interpolated {
+                self.last_span
+            } else {
+                lhs.span
+            };
+
             let cur_op_span = self.span;
             let restrictions = if op.is_assign_like() {
                 self.restrictions & Restrictions::RESTRICTION_NO_STRUCT_LITERAL
@@ -2812,12 +2841,12 @@ pub fn parse_assoc_expr_with(&mut self,
             // Special cases:
             if op == AssocOp::As {
                 let rhs = try!(self.parse_ty());
-                lhs = self.mk_expr(lhs.span.lo, rhs.span.hi,
+                lhs = self.mk_expr(lhs_span.lo, rhs.span.hi,
                                    ExprCast(lhs, rhs), None);
                 continue
             } else if op == AssocOp::Colon {
                 let rhs = try!(self.parse_ty());
-                lhs = self.mk_expr(lhs.span.lo, rhs.span.hi,
+                lhs = self.mk_expr(lhs_span.lo, rhs.span.hi,
                                    ExprType(lhs, rhs), None);
                 continue
             } else if op == AssocOp::DotDot {
@@ -2839,7 +2868,7 @@ pub fn parse_assoc_expr_with(&mut self,
                     } else {
                         None
                     };
-                    let (lhs_span, rhs_span) = (lhs.span, if let Some(ref x) = rhs {
+                    let (lhs_span, rhs_span) = (lhs_span, if let Some(ref x) = rhs {
                         x.span
                     } else {
                         cur_op_span
@@ -2879,14 +2908,14 @@ pub fn parse_assoc_expr_with(&mut self,
                 AssocOp::Equal | AssocOp::Less | AssocOp::LessEqual | AssocOp::NotEqual |
                 AssocOp::Greater | AssocOp::GreaterEqual => {
                     let ast_op = op.to_ast_binop().unwrap();
-                    let (lhs_span, rhs_span) = (lhs.span, rhs.span);
+                    let (lhs_span, rhs_span) = (lhs_span, rhs.span);
                     let binary = self.mk_binary(codemap::respan(cur_op_span, ast_op), lhs, rhs);
                     self.mk_expr(lhs_span.lo, rhs_span.hi, binary, None)
                 }
                 AssocOp::Assign =>
-                    self.mk_expr(lhs.span.lo, rhs.span.hi, ExprAssign(lhs, rhs), None),
+                    self.mk_expr(lhs_span.lo, rhs.span.hi, ExprAssign(lhs, rhs), None),
                 AssocOp::Inplace =>
-                    self.mk_expr(lhs.span.lo, rhs.span.hi, ExprInPlace(lhs, rhs), None),
+                    self.mk_expr(lhs_span.lo, rhs.span.hi, ExprInPlace(lhs, rhs), None),
                 AssocOp::AssignOp(k) => {
                     let aop = match k {
                         token::Plus =>    BiAdd,
@@ -2900,7 +2929,7 @@ pub fn parse_assoc_expr_with(&mut self,
                         token::Shl =>     BiShl,
                         token::Shr =>     BiShr
                     };
-                    let (lhs_span, rhs_span) = (lhs.span, rhs.span);
+                    let (lhs_span, rhs_span) = (lhs_span, rhs.span);
                     let aopexpr = self.mk_assign_op(codemap::respan(cur_op_span, aop), lhs, rhs);
                     self.mk_expr(lhs_span.lo, rhs_span.hi, aopexpr, None)
                 }
@@ -3834,7 +3863,8 @@ fn parse_block_tail(&mut self, lo: BytePos, s: BlockCheckMode) -> PResult<'a, P<
                             let e = self.mk_mac_expr(span.lo, span.hi,
                                                      mac.and_then(|m| m.node),
                                                      None);
-                            let e = try!(self.parse_dot_or_call_expr_with(e, attrs));
+                            let lo = e.span.lo;
+                            let e = try!(self.parse_dot_or_call_expr_with(e, lo, attrs));
                             let e = try!(self.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(e)));
                             try!(self.handle_expression_like_statement(
                                 e,
index 242626154fc8c4066ff0c617f3bd277274aad598..220d0aff2e3af64ae6c1020b0827631838dd9d76 100644 (file)
@@ -223,6 +223,14 @@ pub fn is_ident(&self) -> bool {
         }
     }
 
+    /// Returns `true` if the token is interpolated.
+    pub fn is_interpolated(&self) -> bool {
+        match *self {
+            Interpolated(..) => true,
+            _                => false,
+        }
+    }
+
     /// Returns `true` if the token is an interpolated path.
     pub fn is_path(&self) -> bool {
         match *self {
diff --git a/src/test/compile-fail/issue-25385.rs b/src/test/compile-fail/issue-25385.rs
new file mode 100644 (file)
index 0000000..4aacb68
--- /dev/null
@@ -0,0 +1,24 @@
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+
+macro_rules! foo {
+    ($e:expr) => { $e.foo() }
+    //~^ ERROR no method named `foo` found for type `i32` in the current scope
+}
+
+fn main() {
+    let a = 1i32;
+    foo!(a);
+    //~^ NOTE in this expansion of foo!
+
+    foo!(1i32.foo());
+    //~^ ERROR no method named `foo` found for type `i32` in the current scope
+}
diff --git a/src/test/compile-fail/issue-25386.rs b/src/test/compile-fail/issue-25386.rs
new file mode 100644 (file)
index 0000000..297d3aa
--- /dev/null
@@ -0,0 +1,40 @@
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+mod stuff {
+    pub struct Item {
+        c_object: Box<CObj>,
+    }
+    pub struct CObj {
+        name: Option<String>,
+    }
+    impl Item {
+        pub fn new() -> Item {
+            Item {
+                c_object: Box::new(CObj { name: None }),
+            }
+        }
+    }
+}
+
+macro_rules! check_ptr_exist {
+    ($var:expr, $member:ident) => (
+        (*$var.c_object).$member.is_some()
+        //~^ ERROR field `name` of struct `stuff::CObj` is private
+        //~^^ ERROR field `c_object` of struct `stuff::Item` is private
+    );
+}
+
+fn main() {
+    let item = stuff::Item::new();
+    println!("{}", check_ptr_exist!(item, name));
+    //~^ NOTE in this expansion of check_ptr_exist!
+    //~^^ NOTE in this expansion of check_ptr_exist!
+}
diff --git a/src/test/compile-fail/issue-25793.rs b/src/test/compile-fail/issue-25793.rs
new file mode 100644 (file)
index 0000000..fd3e318
--- /dev/null
@@ -0,0 +1,33 @@
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+macro_rules! width(
+    ($this:expr) => {
+        $this.width.unwrap()
+        //~^ ERROR cannot use `self.width` because it was mutably borrowed
+    }
+);
+
+struct HasInfo {
+    width: Option<usize>
+}
+
+impl HasInfo {
+    fn get_size(&mut self, n: usize) -> usize {
+        n
+    }
+
+    fn get_other(&mut self) -> usize {
+        self.get_size(width!(self))
+        //~^ NOTE in this expansion of width!
+    }
+}
+
+fn main() {}
diff --git a/src/test/compile-fail/issue-26093.rs b/src/test/compile-fail/issue-26093.rs
new file mode 100644 (file)
index 0000000..2f43388
--- /dev/null
@@ -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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+macro_rules! not_an_lvalue {
+    ($thing:expr) => {
+        $thing = 42;
+        //~^ ERROR invalid left-hand side expression
+    }
+}
+
+fn main() {
+    not_an_lvalue!(99);
+    //~^ NOTE in this expansion of not_an_lvalue!
+}
diff --git a/src/test/compile-fail/issue-26094.rs b/src/test/compile-fail/issue-26094.rs
new file mode 100644 (file)
index 0000000..99add95
--- /dev/null
@@ -0,0 +1,23 @@
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+macro_rules! some_macro {
+    ($other: expr) => ({
+        $other(None)
+        //~^ this function takes 0 parameters but 1 parameter was supplied
+    })
+}
+
+fn some_function() {}
+
+fn main() {
+    some_macro!(some_function);
+    //~^ in this expansion of some_macro!
+}
diff --git a/src/test/compile-fail/issue-26237.rs b/src/test/compile-fail/issue-26237.rs
new file mode 100644 (file)
index 0000000..11e236d
--- /dev/null
@@ -0,0 +1,23 @@
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+macro_rules! macro_panic {
+    ($not_a_function:expr, $some_argument:ident) => {
+        $not_a_function($some_argument)
+        //~^ ERROR expected function, found `_`
+    }
+}
+
+fn main() {
+    let mut value_a = 0;
+    let mut value_b = 0;
+    macro_panic!(value_a, value_b);
+    //~^ in this expansion of macro_panic!
+}
diff --git a/src/test/compile-fail/issue-26480.rs b/src/test/compile-fail/issue-26480.rs
new file mode 100644 (file)
index 0000000..903df42
--- /dev/null
@@ -0,0 +1,44 @@
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+extern {
+    fn write(fildes: i32, buf: *const i8, nbyte: u64) -> i64;
+}
+
+#[inline(always)]
+fn size_of<T>(_: T) -> usize {
+    ::std::mem::size_of::<T>()
+}
+
+macro_rules! write {
+    ($arr:expr) => {{
+        #[allow(non_upper_case_globals)]
+        const stdout: i32 = 1;
+        unsafe {
+            write(stdout, $arr.as_ptr() as *const i8,
+                  $arr.len() * size_of($arr[0]));
+            //~^ ERROR mismatched types
+        }
+    }}
+}
+
+macro_rules! cast {
+    ($x:expr) => ($x as ())
+    //~^ ERROR non-scalar cast: `i32` as `()`
+}
+
+fn main() {
+    let hello = ['H', 'e', 'y'];
+    write!(hello);
+    //~^ NOTE in this expansion of write!
+
+    cast!(2);
+    //~^ NOTE in this expansion of cast!
+}
diff --git a/src/test/compile-fail/issue-28308.rs b/src/test/compile-fail/issue-28308.rs
new file mode 100644 (file)
index 0000000..b0c44b5
--- /dev/null
@@ -0,0 +1,17 @@
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// this error is dispayed in `<std macros>`
+// error-pattern:cannot apply unary operator `!` to type `&'static str`
+// error-pattern:in this expansion of assert!
+
+fn main() {
+    assert!("foo");
+}
diff --git a/src/test/compile-fail/issue-29084.rs b/src/test/compile-fail/issue-29084.rs
new file mode 100644 (file)
index 0000000..78913e7
--- /dev/null
@@ -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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+macro_rules! foo {
+    ($d:expr) => {{
+        fn bar(d: u8) { }
+        bar(&mut $d);
+        //~^ ERROR mismatched types
+    }}
+}
+
+fn main() {
+    foo!(0u8);
+    //~^ NOTE in this expansion of foo!
+}
diff --git a/src/test/compile-fail/issue-31011.rs b/src/test/compile-fail/issue-31011.rs
new file mode 100644 (file)
index 0000000..b828b11
--- /dev/null
@@ -0,0 +1,39 @@
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+macro_rules! log {
+    ( $ctx:expr, $( $args:expr),* ) => {
+        if $ctx.trace {
+        //~^ ERROR attempted access of field `trace` on type `&T`, but no field with that name
+            println!( $( $args, )* );
+        }
+    }
+}
+
+// Create a structure.
+struct Foo {
+  trace: bool,
+}
+
+// Generic wrapper calls log! with a structure.
+fn wrap<T>(context: &T) -> ()
+{
+    log!(context, "entered wrapper");
+    //~^ in this expansion of log!
+}
+
+fn main() {
+    // Create a structure.
+    let x = Foo { trace: true };
+    log!(x, "run started");
+    // Apply a closure which accesses internal fields.
+    wrap(&x);
+    log!(x, "run finished");
+}