diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 2249faac6d701..b8e5642474c78 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -233,7 +233,6 @@ macro_rules! maybe_whole { ) } - fn maybe_append(mut lhs: Vec, rhs: Option>) -> Vec { 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>, + last_token_interpolated: bool, pub buffer: [TokenAndSpan; 4], pub buffer_start: isize, pub buffer_end: isize, @@ -362,6 +362,7 @@ impl<'a> Parser<'a> { span: span, last_span: span, last_token: None, + last_token_interpolated: false, buffer: [ placeholder.clone(), placeholder.clone(), @@ -542,6 +543,19 @@ impl<'a> Parser<'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>) + -> PResult<'a, (Span, P)> { + 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 @@ impl<'a> Parser<'a> { } 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 @@ impl<'a> Parser<'a> { -> PResult<'a, P> { 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, + lo: BytePos, attrs: ThinAttributes) -> PResult<'a, P> { // 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 @@ impl<'a> Parser<'a> { fn parse_dot_suffix(&mut self, ident: Ident, ident_span: Span, - self_value: P) + self_value: P, + lo: BytePos) -> PResult<'a, P> { let (_, tys, bindings) = if self.eat(&token::ModSep) { try!(self.expect_lt()); @@ -2380,8 +2398,6 @@ impl<'a> Parser<'a> { 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 @@ impl<'a> Parser<'a> { }) } - fn parse_dot_or_call_expr_with_(&mut self, e0: P) -> PResult<'a, P> { + fn parse_dot_or_call_expr_with_(&mut self, e0: P, lo: BytePos) -> PResult<'a, P> { let mut e = e0; - let lo = e.span.lo; let mut hi; loop { // expr.f @@ -2427,7 +2442,7 @@ impl<'a> Parser<'a> { 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 @@ impl<'a> Parser<'a> { 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 @@ impl<'a> Parser<'a> { 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 @@ impl<'a> Parser<'a> { } 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 @@ impl<'a> Parser<'a> { 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 @@ impl<'a> Parser<'a> { // 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 @@ impl<'a> Parser<'a> { } 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 @@ impl<'a> Parser<'a> { 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 @@ impl<'a> Parser<'a> { 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 @@ impl<'a> Parser<'a> { 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, diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 242626154fc8c..220d0aff2e3af 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -223,6 +223,14 @@ impl Token { } } + /// 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 index 0000000000000..4aacb6840e9d5 --- /dev/null +++ b/src/test/compile-fail/issue-25385.rs @@ -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 or the MIT license +// , 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 index 0000000000000..297d3aacfd51e --- /dev/null +++ b/src/test/compile-fail/issue-25386.rs @@ -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 or the MIT license +// , 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, + } + pub struct CObj { + name: Option, + } + 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 index 0000000000000..fd3e3186bc5c9 --- /dev/null +++ b/src/test/compile-fail/issue-25793.rs @@ -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 or the MIT license +// , 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 +} + +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 index 0000000000000..2f43388b7afb0 --- /dev/null +++ b/src/test/compile-fail/issue-26093.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. + +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 index 0000000000000..99add95e806f6 --- /dev/null +++ b/src/test/compile-fail/issue-26094.rs @@ -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 or the MIT license +// , 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 index 0000000000000..11e236d22126b --- /dev/null +++ b/src/test/compile-fail/issue-26237.rs @@ -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 or the MIT license +// , 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 index 0000000000000..903df42291c63 --- /dev/null +++ b/src/test/compile-fail/issue-26480.rs @@ -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 or the MIT license +// , 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) -> usize { + ::std::mem::size_of::() +} + +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 index 0000000000000..b0c44b5f33af1 --- /dev/null +++ b/src/test/compile-fail/issue-28308.rs @@ -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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// this error is dispayed in `` +// 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 index 0000000000000..78913e759a1cc --- /dev/null +++ b/src/test/compile-fail/issue-29084.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. + +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 index 0000000000000..b828b11030d71 --- /dev/null +++ b/src/test/compile-fail/issue-31011.rs @@ -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 or the MIT license +// , 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(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"); +}