Skip to content

Commit 8256c47

Browse files
committed
Auto merge of #31089 - fhahn:macro-ice, r=pnkfelix
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.
2 parents b8b18aa + ecb7b01 commit 8256c47

12 files changed

+354
-30
lines changed

src/libsyntax/parse/parser.rs

+60-30
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,6 @@ macro_rules! maybe_whole {
233233
)
234234
}
235235

236-
237236
fn maybe_append(mut lhs: Vec<Attribute>, rhs: Option<Vec<Attribute>>)
238237
-> Vec<Attribute> {
239238
if let Some(ref attrs) = rhs {
@@ -255,6 +254,7 @@ pub struct Parser<'a> {
255254
pub cfg: CrateConfig,
256255
/// the previous token or None (only stashed sometimes).
257256
pub last_token: Option<Box<token::Token>>,
257+
last_token_interpolated: bool,
258258
pub buffer: [TokenAndSpan; 4],
259259
pub buffer_start: isize,
260260
pub buffer_end: isize,
@@ -362,6 +362,7 @@ impl<'a> Parser<'a> {
362362
span: span,
363363
last_span: span,
364364
last_token: None,
365+
last_token_interpolated: false,
365366
buffer: [
366367
placeholder.clone(),
367368
placeholder.clone(),
@@ -542,6 +543,19 @@ impl<'a> Parser<'a> {
542543
self.commit_stmt(&[edible], &[])
543544
}
544545

546+
/// returns the span of expr, if it was not interpolated or the span of the interpolated token
547+
fn interpolated_or_expr_span(&self,
548+
expr: PResult<'a, P<Expr>>)
549+
-> PResult<'a, (Span, P<Expr>)> {
550+
expr.map(|e| {
551+
if self.last_token_interpolated {
552+
(self.last_span, e)
553+
} else {
554+
(e.span, e)
555+
}
556+
})
557+
}
558+
545559
pub fn parse_ident(&mut self) -> PResult<'a, ast::Ident> {
546560
self.check_strict_keywords();
547561
self.check_reserved_keywords();
@@ -933,6 +947,7 @@ impl<'a> Parser<'a> {
933947
} else {
934948
None
935949
};
950+
self.last_token_interpolated = self.token.is_interpolated();
936951
let next = if self.buffer_start == self.buffer_end {
937952
self.reader.real_token()
938953
} else {
@@ -2328,18 +2343,20 @@ impl<'a> Parser<'a> {
23282343
-> PResult<'a, P<Expr>> {
23292344
let attrs = try!(self.parse_or_use_outer_attributes(already_parsed_attrs));
23302345

2331-
let b = try!(self.parse_bottom_expr());
2332-
self.parse_dot_or_call_expr_with(b, attrs)
2346+
let b = self.parse_bottom_expr();
2347+
let (span, b) = try!(self.interpolated_or_expr_span(b));
2348+
self.parse_dot_or_call_expr_with(b, span.lo, attrs)
23332349
}
23342350

23352351
pub fn parse_dot_or_call_expr_with(&mut self,
23362352
e0: P<Expr>,
2353+
lo: BytePos,
23372354
attrs: ThinAttributes)
23382355
-> PResult<'a, P<Expr>> {
23392356
// Stitch the list of outer attributes onto the return value.
23402357
// A little bit ugly, but the best way given the current code
23412358
// structure
2342-
self.parse_dot_or_call_expr_with_(e0)
2359+
self.parse_dot_or_call_expr_with_(e0, lo)
23432360
.map(|expr|
23442361
expr.map(|mut expr| {
23452362
expr.attrs.update(|a| a.prepend(attrs));
@@ -2366,7 +2383,8 @@ impl<'a> Parser<'a> {
23662383
fn parse_dot_suffix(&mut self,
23672384
ident: Ident,
23682385
ident_span: Span,
2369-
self_value: P<Expr>)
2386+
self_value: P<Expr>,
2387+
lo: BytePos)
23702388
-> PResult<'a, P<Expr>> {
23712389
let (_, tys, bindings) = if self.eat(&token::ModSep) {
23722390
try!(self.expect_lt());
@@ -2380,8 +2398,6 @@ impl<'a> Parser<'a> {
23802398
self.span_err(last_span, "type bindings are only permitted on trait paths");
23812399
}
23822400

2383-
let lo = self_value.span.lo;
2384-
23852401
Ok(match self.token {
23862402
// expr.f() method call.
23872403
token::OpenDelim(token::Paren) => {
@@ -2414,9 +2430,8 @@ impl<'a> Parser<'a> {
24142430
})
24152431
}
24162432

2417-
fn parse_dot_or_call_expr_with_(&mut self, e0: P<Expr>) -> PResult<'a, P<Expr>> {
2433+
fn parse_dot_or_call_expr_with_(&mut self, e0: P<Expr>, lo: BytePos) -> PResult<'a, P<Expr>> {
24182434
let mut e = e0;
2419-
let lo = e.span.lo;
24202435
let mut hi;
24212436
loop {
24222437
// expr.f
@@ -2427,7 +2442,7 @@ impl<'a> Parser<'a> {
24272442
hi = self.span.hi;
24282443
self.bump();
24292444

2430-
e = try!(self.parse_dot_suffix(i, mk_sp(dot_pos, hi), e));
2445+
e = try!(self.parse_dot_suffix(i, mk_sp(dot_pos, hi), e, lo));
24312446
}
24322447
token::Literal(token::Integer(n), suf) => {
24332448
let sp = self.span;
@@ -2480,7 +2495,7 @@ impl<'a> Parser<'a> {
24802495
let dot_pos = self.last_span.hi;
24812496
e = try!(self.parse_dot_suffix(special_idents::invalid,
24822497
mk_sp(dot_pos, dot_pos),
2483-
e));
2498+
e, lo));
24842499
}
24852500
}
24862501
continue;
@@ -2715,27 +2730,31 @@ impl<'a> Parser<'a> {
27152730
let ex = match self.token {
27162731
token::Not => {
27172732
self.bump();
2718-
let e = try!(self.parse_prefix_expr(None));
2719-
hi = e.span.hi;
2733+
let e = self.parse_prefix_expr(None);
2734+
let (span, e) = try!(self.interpolated_or_expr_span(e));
2735+
hi = span.hi;
27202736
self.mk_unary(UnNot, e)
27212737
}
27222738
token::BinOp(token::Minus) => {
27232739
self.bump();
2724-
let e = try!(self.parse_prefix_expr(None));
2725-
hi = e.span.hi;
2740+
let e = self.parse_prefix_expr(None);
2741+
let (span, e) = try!(self.interpolated_or_expr_span(e));
2742+
hi = span.hi;
27262743
self.mk_unary(UnNeg, e)
27272744
}
27282745
token::BinOp(token::Star) => {
27292746
self.bump();
2730-
let e = try!(self.parse_prefix_expr(None));
2731-
hi = e.span.hi;
2747+
let e = self.parse_prefix_expr(None);
2748+
let (span, e) = try!(self.interpolated_or_expr_span(e));
2749+
hi = span.hi;
27322750
self.mk_unary(UnDeref, e)
27332751
}
27342752
token::BinOp(token::And) | token::AndAnd => {
27352753
try!(self.expect_and());
27362754
let m = try!(self.parse_mutability());
2737-
let e = try!(self.parse_prefix_expr(None));
2738-
hi = e.span.hi;
2755+
let e = self.parse_prefix_expr(None);
2756+
let (span, e) = try!(self.interpolated_or_expr_span(e));
2757+
hi = span.hi;
27392758
ExprAddrOf(m, e)
27402759
}
27412760
token::Ident(..) if self.token.is_keyword(keywords::In) => {
@@ -2753,9 +2772,10 @@ impl<'a> Parser<'a> {
27532772
}
27542773
token::Ident(..) if self.token.is_keyword(keywords::Box) => {
27552774
self.bump();
2756-
let subexpression = try!(self.parse_prefix_expr(None));
2757-
hi = subexpression.span.hi;
2758-
ExprBox(subexpression)
2775+
let e = self.parse_prefix_expr(None);
2776+
let (span, e) = try!(self.interpolated_or_expr_span(e));
2777+
hi = span.hi;
2778+
ExprBox(e)
27592779
}
27602780
_ => return self.parse_dot_or_call_expr(Some(attrs))
27612781
};
@@ -2790,12 +2810,21 @@ impl<'a> Parser<'a> {
27902810
try!(self.parse_prefix_expr(attrs))
27912811
}
27922812
};
2813+
2814+
27932815
if self.expr_is_complete(&*lhs) {
27942816
// Semi-statement forms are odd. See https://github.com/rust-lang/rust/issues/29071
27952817
return Ok(lhs);
27962818
}
27972819
self.expected_tokens.push(TokenType::Operator);
27982820
while let Some(op) = AssocOp::from_token(&self.token) {
2821+
2822+
let lhs_span = if self.last_token_interpolated {
2823+
self.last_span
2824+
} else {
2825+
lhs.span
2826+
};
2827+
27992828
let cur_op_span = self.span;
28002829
let restrictions = if op.is_assign_like() {
28012830
self.restrictions & Restrictions::RESTRICTION_NO_STRUCT_LITERAL
@@ -2812,12 +2841,12 @@ impl<'a> Parser<'a> {
28122841
// Special cases:
28132842
if op == AssocOp::As {
28142843
let rhs = try!(self.parse_ty());
2815-
lhs = self.mk_expr(lhs.span.lo, rhs.span.hi,
2844+
lhs = self.mk_expr(lhs_span.lo, rhs.span.hi,
28162845
ExprCast(lhs, rhs), None);
28172846
continue
28182847
} else if op == AssocOp::Colon {
28192848
let rhs = try!(self.parse_ty());
2820-
lhs = self.mk_expr(lhs.span.lo, rhs.span.hi,
2849+
lhs = self.mk_expr(lhs_span.lo, rhs.span.hi,
28212850
ExprType(lhs, rhs), None);
28222851
continue
28232852
} else if op == AssocOp::DotDot {
@@ -2839,7 +2868,7 @@ impl<'a> Parser<'a> {
28392868
} else {
28402869
None
28412870
};
2842-
let (lhs_span, rhs_span) = (lhs.span, if let Some(ref x) = rhs {
2871+
let (lhs_span, rhs_span) = (lhs_span, if let Some(ref x) = rhs {
28432872
x.span
28442873
} else {
28452874
cur_op_span
@@ -2879,14 +2908,14 @@ impl<'a> Parser<'a> {
28792908
AssocOp::Equal | AssocOp::Less | AssocOp::LessEqual | AssocOp::NotEqual |
28802909
AssocOp::Greater | AssocOp::GreaterEqual => {
28812910
let ast_op = op.to_ast_binop().unwrap();
2882-
let (lhs_span, rhs_span) = (lhs.span, rhs.span);
2911+
let (lhs_span, rhs_span) = (lhs_span, rhs.span);
28832912
let binary = self.mk_binary(codemap::respan(cur_op_span, ast_op), lhs, rhs);
28842913
self.mk_expr(lhs_span.lo, rhs_span.hi, binary, None)
28852914
}
28862915
AssocOp::Assign =>
2887-
self.mk_expr(lhs.span.lo, rhs.span.hi, ExprAssign(lhs, rhs), None),
2916+
self.mk_expr(lhs_span.lo, rhs.span.hi, ExprAssign(lhs, rhs), None),
28882917
AssocOp::Inplace =>
2889-
self.mk_expr(lhs.span.lo, rhs.span.hi, ExprInPlace(lhs, rhs), None),
2918+
self.mk_expr(lhs_span.lo, rhs.span.hi, ExprInPlace(lhs, rhs), None),
28902919
AssocOp::AssignOp(k) => {
28912920
let aop = match k {
28922921
token::Plus => BiAdd,
@@ -2900,7 +2929,7 @@ impl<'a> Parser<'a> {
29002929
token::Shl => BiShl,
29012930
token::Shr => BiShr
29022931
};
2903-
let (lhs_span, rhs_span) = (lhs.span, rhs.span);
2932+
let (lhs_span, rhs_span) = (lhs_span, rhs.span);
29042933
let aopexpr = self.mk_assign_op(codemap::respan(cur_op_span, aop), lhs, rhs);
29052934
self.mk_expr(lhs_span.lo, rhs_span.hi, aopexpr, None)
29062935
}
@@ -3834,7 +3863,8 @@ impl<'a> Parser<'a> {
38343863
let e = self.mk_mac_expr(span.lo, span.hi,
38353864
mac.and_then(|m| m.node),
38363865
None);
3837-
let e = try!(self.parse_dot_or_call_expr_with(e, attrs));
3866+
let lo = e.span.lo;
3867+
let e = try!(self.parse_dot_or_call_expr_with(e, lo, attrs));
38383868
let e = try!(self.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(e)));
38393869
try!(self.handle_expression_like_statement(
38403870
e,

src/libsyntax/parse/token.rs

+8
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,14 @@ impl Token {
223223
}
224224
}
225225

226+
/// Returns `true` if the token is interpolated.
227+
pub fn is_interpolated(&self) -> bool {
228+
match *self {
229+
Interpolated(..) => true,
230+
_ => false,
231+
}
232+
}
233+
226234
/// Returns `true` if the token is an interpolated path.
227235
pub fn is_path(&self) -> bool {
228236
match *self {

src/test/compile-fail/issue-25385.rs

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
12+
macro_rules! foo {
13+
($e:expr) => { $e.foo() }
14+
//~^ ERROR no method named `foo` found for type `i32` in the current scope
15+
}
16+
17+
fn main() {
18+
let a = 1i32;
19+
foo!(a);
20+
//~^ NOTE in this expansion of foo!
21+
22+
foo!(1i32.foo());
23+
//~^ ERROR no method named `foo` found for type `i32` in the current scope
24+
}

src/test/compile-fail/issue-25386.rs

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
mod stuff {
12+
pub struct Item {
13+
c_object: Box<CObj>,
14+
}
15+
pub struct CObj {
16+
name: Option<String>,
17+
}
18+
impl Item {
19+
pub fn new() -> Item {
20+
Item {
21+
c_object: Box::new(CObj { name: None }),
22+
}
23+
}
24+
}
25+
}
26+
27+
macro_rules! check_ptr_exist {
28+
($var:expr, $member:ident) => (
29+
(*$var.c_object).$member.is_some()
30+
//~^ ERROR field `name` of struct `stuff::CObj` is private
31+
//~^^ ERROR field `c_object` of struct `stuff::Item` is private
32+
);
33+
}
34+
35+
fn main() {
36+
let item = stuff::Item::new();
37+
println!("{}", check_ptr_exist!(item, name));
38+
//~^ NOTE in this expansion of check_ptr_exist!
39+
//~^^ NOTE in this expansion of check_ptr_exist!
40+
}

src/test/compile-fail/issue-25793.rs

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
macro_rules! width(
12+
($this:expr) => {
13+
$this.width.unwrap()
14+
//~^ ERROR cannot use `self.width` because it was mutably borrowed
15+
}
16+
);
17+
18+
struct HasInfo {
19+
width: Option<usize>
20+
}
21+
22+
impl HasInfo {
23+
fn get_size(&mut self, n: usize) -> usize {
24+
n
25+
}
26+
27+
fn get_other(&mut self) -> usize {
28+
self.get_size(width!(self))
29+
//~^ NOTE in this expansion of width!
30+
}
31+
}
32+
33+
fn main() {}

src/test/compile-fail/issue-26093.rs

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
macro_rules! not_an_lvalue {
12+
($thing:expr) => {
13+
$thing = 42;
14+
//~^ ERROR invalid left-hand side expression
15+
}
16+
}
17+
18+
fn main() {
19+
not_an_lvalue!(99);
20+
//~^ NOTE in this expansion of not_an_lvalue!
21+
}

0 commit comments

Comments
 (0)