Skip to content

Commit 33c6d3f

Browse files
committed
Allow attributes to appear as macro arguments
Fixes #8393
1 parent 8f65dbf commit 33c6d3f

File tree

7 files changed

+137
-59
lines changed

7 files changed

+137
-59
lines changed

src/libsyntax/ext/tt/macro_parser.rs

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use codemap;
1717
use parse::lexer::*; //resolve bug?
1818
use parse::ParseSess;
1919
use parse::parser::Parser;
20+
use parse::attr::parser_attr;
2021
use parse::token::{Token, EOF, to_str, nonterminal, get_ident_interner, ident_to_str};
2122
use parse::token;
2223

@@ -430,6 +431,7 @@ pub fn parse_nt(p: &Parser, name: &str) -> nonterminal {
430431
+ token::to_str(get_ident_interner(), p.token))
431432
},
432433
"path" => token::nt_path(p.parse_path_with_tps(false)),
434+
"attr" => token::nt_attr(@p.parse_attribute(false)),
433435
"tt" => {
434436
*p.quote_depth += 1u; //but in theory, non-quoted tts might be useful
435437
let res = token::nt_tt(@p.parse_token_tree());

src/libsyntax/parse/attr.rs

+74-57
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,17 @@
99
// except according to those terms.
1010

1111
use ast;
12-
use codemap::spanned;
12+
use codemap::{spanned, mk_sp};
1313
use codemap::BytePos;
1414
use parse::common::*; //resolve bug?
1515
use parse::token;
1616
use parse::parser::Parser;
17+
use parse::token::INTERPOLATED;
1718

1819
// a parser that can parse attributes.
1920
pub trait parser_attr {
2021
fn parse_outer_attributes(&self) -> ~[ast::Attribute];
21-
fn parse_attribute(&self, style: ast::AttrStyle) -> ast::Attribute;
22-
fn parse_attribute_naked(
23-
&self,
24-
style: ast::AttrStyle,
25-
lo: BytePos
26-
) -> ast::Attribute;
22+
fn parse_attribute(&self, permit_inner: bool) -> ast::Attribute;
2723
fn parse_inner_attrs_and_next(&self) ->
2824
(~[ast::Attribute], ~[ast::Attribute]);
2925
fn parse_meta_item(&self) -> @ast::MetaItem;
@@ -37,12 +33,17 @@ impl parser_attr for Parser {
3733
fn parse_outer_attributes(&self) -> ~[ast::Attribute] {
3834
let mut attrs: ~[ast::Attribute] = ~[];
3935
loop {
36+
debug!("parse_outer_attributes: self.token=%?",
37+
self.token);
4038
match *self.token {
39+
token::INTERPOLATED(token::nt_attr(*)) => {
40+
attrs.push(self.parse_attribute(false));
41+
}
4142
token::POUND => {
4243
if self.look_ahead(1, |t| *t != token::LBRACKET) {
4344
break;
4445
}
45-
attrs.push(self.parse_attribute(ast::AttrOuter));
46+
attrs.push(self.parse_attribute(false));
4647
}
4748
token::DOC_COMMENT(s) => {
4849
let attr = ::attr::mk_sugared_doc_attr(
@@ -62,23 +63,49 @@ impl parser_attr for Parser {
6263
return attrs;
6364
}
6465

65-
// matches attribute = # attribute_naked
66-
fn parse_attribute(&self, style: ast::AttrStyle) -> ast::Attribute {
67-
let lo = self.span.lo;
68-
self.expect(&token::POUND);
69-
return self.parse_attribute_naked(style, lo);
66+
// matches attribute = # [ meta_item ]
67+
//
68+
// if permit_inner is true, then a trailing `;` indicates an inner
69+
// attribute
70+
fn parse_attribute(&self, permit_inner: bool) -> ast::Attribute {
71+
debug!("parse_attributes: permit_inner=%? self.token=%?",
72+
permit_inner, self.token);
73+
let (span, value) = match *self.token {
74+
INTERPOLATED(token::nt_attr(attr)) => {
75+
assert!(attr.node.style == ast::AttrOuter);
76+
self.bump();
77+
(attr.span, attr.node.value)
78+
}
79+
token::POUND => {
80+
let lo = self.span.lo;
81+
self.bump();
82+
self.expect(&token::LBRACKET);
83+
let meta_item = self.parse_meta_item();
84+
self.expect(&token::RBRACKET);
85+
let hi = self.span.hi;
86+
(mk_sp(lo, hi), meta_item)
87+
}
88+
_ => {
89+
self.fatal(fmt!("expected `#` but found `%s`",
90+
self.this_token_to_str()));
91+
}
92+
};
93+
let style = if permit_inner && *self.token == token::SEMI {
94+
self.bump();
95+
ast::AttrInner
96+
} else {
97+
ast::AttrOuter
98+
};
99+
return spanned {
100+
span: span,
101+
node: ast::Attribute_ {
102+
style: style,
103+
value: value,
104+
is_sugared_doc: false
105+
}
106+
};
70107
}
71108

72-
// matches attribute_naked = [ meta_item ]
73-
fn parse_attribute_naked(&self, style: ast::AttrStyle, lo: BytePos) ->
74-
ast::Attribute {
75-
self.expect(&token::LBRACKET);
76-
let meta_item = self.parse_meta_item();
77-
self.expect(&token::RBRACKET);
78-
let hi = self.span.hi;
79-
return spanned(lo, hi, ast::Attribute_ { style: style,
80-
value: meta_item, is_sugared_doc: false }); }
81-
82109
// Parse attributes that appear after the opening of an item, each
83110
// terminated by a semicolon. In addition to a vector of inner attributes,
84111
// this function also returns a vector that may contain the first outer
@@ -89,47 +116,37 @@ impl parser_attr for Parser {
89116
// matches inner_attrs* outer_attr?
90117
// you can make the 'next' field an Option, but the result is going to be
91118
// more useful as a vector.
92-
fn parse_inner_attrs_and_next(&self) ->
93-
(~[ast::Attribute], ~[ast::Attribute]) {
119+
fn parse_inner_attrs_and_next(&self)
120+
-> (~[ast::Attribute], ~[ast::Attribute]) {
94121
let mut inner_attrs: ~[ast::Attribute] = ~[];
95122
let mut next_outer_attrs: ~[ast::Attribute] = ~[];
96123
loop {
97-
match *self.token {
98-
token::POUND => {
99-
if self.look_ahead(1, |t| *t != token::LBRACKET) {
100-
// This is an extension
101-
break;
124+
let attr = match *self.token {
125+
token::INTERPOLATED(token::nt_attr(*)) => {
126+
self.parse_attribute(true)
127+
}
128+
token::POUND => {
129+
if self.look_ahead(1, |t| *t != token::LBRACKET) {
130+
// This is an extension
131+
break;
132+
}
133+
self.parse_attribute(true)
102134
}
103-
let attr = self.parse_attribute(ast::AttrInner);
104-
if *self.token == token::SEMI {
135+
token::DOC_COMMENT(s) => {
105136
self.bump();
106-
inner_attrs.push(attr);
107-
} else {
108-
// It's not really an inner attribute
109-
let outer_attr =
110-
spanned(attr.span.lo, attr.span.hi,
111-
ast::Attribute_ { style: ast::AttrOuter,
112-
value: attr.node.value,
113-
is_sugared_doc: false });
114-
next_outer_attrs.push(outer_attr);
115-
break;
137+
::attr::mk_sugared_doc_attr(self.id_to_str(s),
138+
self.span.lo,
139+
self.span.hi)
116140
}
117-
}
118-
token::DOC_COMMENT(s) => {
119-
let attr = ::attr::mk_sugared_doc_attr(
120-
self.id_to_str(s),
121-
self.span.lo,
122-
self.span.hi
123-
);
124-
self.bump();
125-
if attr.node.style == ast::AttrInner {
126-
inner_attrs.push(attr);
127-
} else {
128-
next_outer_attrs.push(attr);
129-
break;
141+
_ => {
142+
break;
130143
}
131-
}
132-
_ => break
144+
};
145+
if attr.node.style == ast::AttrInner {
146+
inner_attrs.push(attr);
147+
} else {
148+
next_outer_attrs.push(attr);
149+
break;
133150
}
134151
}
135152
(inner_attrs, next_outer_attrs)

src/libsyntax/parse/lexer.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,11 @@ impl reader for StringReader {
129129

130130
impl reader for TtReader {
131131
fn is_eof(@mut self) -> bool { self.cur_tok == token::EOF }
132-
fn next_token(@mut self) -> TokenAndSpan { tt_next_token(self) }
132+
fn next_token(@mut self) -> TokenAndSpan {
133+
let r = tt_next_token(self);
134+
debug!("TtReader: r=%?", r);
135+
return r;
136+
}
133137
fn fatal(@mut self, m: ~str) -> ! {
134138
self.sp_diag.span_fatal(self.cur_span, m);
135139
}

src/libsyntax/parse/parser.rs

+13-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
#[macro_escape];
12+
1113
use abi;
1214
use abi::AbiSet;
1315
use ast::{Sigil, BorrowedSigil, ManagedSigil, OwnedSigil};
@@ -4452,7 +4454,17 @@ impl Parser {
44524454
attrs: ~[Attribute],
44534455
macros_allowed: bool)
44544456
-> item_or_view_item {
4455-
maybe_whole!(iovi self, nt_item);
4457+
match *self.token {
4458+
INTERPOLATED(token::nt_item(item)) => {
4459+
self.bump();
4460+
let new_attrs = vec::append(attrs, item.attrs);
4461+
return iovi_item(@ast::item {
4462+
attrs: new_attrs,
4463+
..(*item).clone()});
4464+
}
4465+
_ => {}
4466+
}
4467+
44564468
let lo = self.span.lo;
44574469

44584470
let visibility = self.parse_non_priv_visibility();

src/libsyntax/parse/token.rs

+3
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ pub enum nonterminal {
105105
nt_expr(@ast::expr),
106106
nt_ty( ast::Ty),
107107
nt_ident(ast::ident, bool),
108+
nt_attr(@ast::Attribute), // #[foo]
108109
nt_path( ast::Path),
109110
nt_tt( @ast::token_tree), //needs @ed to break a circularity
110111
nt_matchers(~[ast::matcher])
@@ -205,13 +206,15 @@ pub fn to_str(input: @ident_interner, t: &Token) -> ~str {
205206
INTERPOLATED(ref nt) => {
206207
match nt {
207208
&nt_expr(e) => ::print::pprust::expr_to_str(e, input),
209+
&nt_attr(e) => ::print::pprust::attribute_to_str(e, input),
208210
_ => {
209211
~"an interpolated " +
210212
match (*nt) {
211213
nt_item(*) => ~"item",
212214
nt_block(*) => ~"block",
213215
nt_stmt(*) => ~"statement",
214216
nt_pat(*) => ~"pattern",
217+
nt_attr(*) => fail!("should have been handled"),
215218
nt_expr(*) => fail!("should have been handled above"),
216219
nt_ty(*) => ~"type",
217220
nt_ident(*) => ~"identifier",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
macro_rules! test ( ($nm:ident,
2+
$a:attr,
3+
$i:item) => (mod $nm { $a; $i }); )
4+
5+
test!(a,
6+
#[cfg(qux)],
7+
pub fn bar() { })
8+
9+
test!(b,
10+
#[cfg(not(qux))],
11+
pub fn bar() { })
12+
13+
#[qux]
14+
fn main() {
15+
a::bar();
16+
//~^ ERROR use of undeclared module `a`
17+
//~^^ ERROR unresolved name
18+
//~^^^ ERROR unresolved name `a::bar`
19+
b::bar();
20+
}
21+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
macro_rules! test ( ($nm:ident,
2+
$a:attr,
3+
$i:item) => (mod $nm { $a $i }); )
4+
5+
test!(a,
6+
#[cfg(qux)],
7+
pub fn bar() { })
8+
9+
test!(b,
10+
#[cfg(not(qux))],
11+
pub fn bar() { })
12+
13+
// test1!(#[bar])
14+
#[qux]
15+
fn main() {
16+
a::bar(); //~ ERROR unresolved name `a::bar`
17+
b::bar();
18+
}
19+

0 commit comments

Comments
 (0)