rustc: Parse fn inner attributes. Closes #1506
This commit is contained in:
parent
17585cc47e
commit
3466c9b4be
6 changed files with 134 additions and 30 deletions
|
@ -1531,14 +1531,23 @@ fn parse_let(p: parser) -> @ast::decl {
|
|||
ret @spanned(lo, p.last_span.hi, ast::decl_local(locals));
|
||||
}
|
||||
|
||||
fn parse_stmt(p: parser) -> @ast::stmt {
|
||||
fn parse_stmt(p: parser, first_item_attrs: [ast::attribute]) -> @ast::stmt {
|
||||
fn check_expected_item(p: parser, current_attrs: [ast::attribute]) {
|
||||
// If we have attributes then we should have an item
|
||||
if vec::is_not_empty(current_attrs) {
|
||||
p.fatal("expected item");
|
||||
}
|
||||
}
|
||||
|
||||
let lo = p.span.lo;
|
||||
if eat_word(p, "let") {
|
||||
if is_word(p, "let") {
|
||||
check_expected_item(p, first_item_attrs);
|
||||
expect_word(p, "let");
|
||||
let decl = parse_let(p);
|
||||
ret @spanned(lo, decl.span.hi, ast::stmt_decl(decl, p.get_id()));
|
||||
} else {
|
||||
let item_attrs;
|
||||
alt parse_outer_attrs_or_ext(p) {
|
||||
alt parse_outer_attrs_or_ext(p, first_item_attrs) {
|
||||
none. { item_attrs = []; }
|
||||
some(left(attrs)) { item_attrs = attrs; }
|
||||
some(right(ext)) {
|
||||
|
@ -1546,6 +1555,8 @@ fn parse_stmt(p: parser) -> @ast::stmt {
|
|||
}
|
||||
}
|
||||
|
||||
let item_attrs = first_item_attrs + item_attrs;
|
||||
|
||||
alt parse_item(p, item_attrs) {
|
||||
some(i) {
|
||||
let hi = i.span.hi;
|
||||
|
@ -1555,10 +1566,7 @@ fn parse_stmt(p: parser) -> @ast::stmt {
|
|||
none() { /* fallthrough */ }
|
||||
}
|
||||
|
||||
// If we have attributes then we should have an item
|
||||
if vec::len(item_attrs) > 0u {
|
||||
ret p.fatal("expected item");
|
||||
}
|
||||
check_expected_item(p, item_attrs);
|
||||
|
||||
// Remainder are line-expr stmts.
|
||||
let e = parse_expr_res(p, RESTRICT_STMT_EXPR);
|
||||
|
@ -1605,16 +1613,37 @@ fn stmt_ends_with_semi(stmt: ast::stmt) -> bool {
|
|||
}
|
||||
|
||||
fn parse_block(p: parser) -> ast::blk {
|
||||
let (attrs, blk) = parse_inner_attrs_and_block(p, false);
|
||||
assert vec::is_empty(attrs);
|
||||
ret blk;
|
||||
}
|
||||
|
||||
fn parse_inner_attrs_and_block(
|
||||
p: parser, parse_attrs: bool) -> ([ast::attribute], ast::blk) {
|
||||
|
||||
fn maybe_parse_inner_attrs_and_next(
|
||||
p: parser, parse_attrs: bool) ->
|
||||
{inner: [ast::attribute], next: [ast::attribute]} {
|
||||
if parse_attrs {
|
||||
parse_inner_attrs_and_next(p)
|
||||
} else {
|
||||
{inner: [], next: []}
|
||||
}
|
||||
}
|
||||
|
||||
let lo = p.span.lo;
|
||||
if eat_word(p, "unchecked") {
|
||||
expect(p, token::LBRACE);
|
||||
be parse_block_tail(p, lo, ast::unchecked_blk);
|
||||
let {inner, next} = maybe_parse_inner_attrs_and_next(p, parse_attrs);
|
||||
ret (inner, parse_block_tail_(p, lo, ast::unchecked_blk, next));
|
||||
} else if eat_word(p, "unsafe") {
|
||||
expect(p, token::LBRACE);
|
||||
be parse_block_tail(p, lo, ast::unsafe_blk);
|
||||
let {inner, next} = maybe_parse_inner_attrs_and_next(p, parse_attrs);
|
||||
ret (inner, parse_block_tail_(p, lo, ast::unsafe_blk, next));
|
||||
} else {
|
||||
expect(p, token::LBRACE);
|
||||
be parse_block_tail(p, lo, ast::default_blk);
|
||||
let {inner, next} = maybe_parse_inner_attrs_and_next(p, parse_attrs);
|
||||
ret (inner, parse_block_tail_(p, lo, ast::default_blk, next));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1630,15 +1659,28 @@ fn parse_block_no_value(p: parser) -> ast::blk {
|
|||
// necessary, and this should take a qualifier.
|
||||
// some blocks start with "#{"...
|
||||
fn parse_block_tail(p: parser, lo: uint, s: ast::blk_check_mode) -> ast::blk {
|
||||
let stmts = [], expr = none;
|
||||
let view_items = parse_view_import_only(p);
|
||||
parse_block_tail_(p, lo, s, [])
|
||||
}
|
||||
|
||||
fn parse_block_tail_(p: parser, lo: uint, s: ast::blk_check_mode,
|
||||
first_item_attrs: [ast::attribute]) -> ast::blk {
|
||||
let stmts = [];
|
||||
let expr = none;
|
||||
let view_items = maybe_parse_view_import_only(p, first_item_attrs);
|
||||
let initial_attrs = first_item_attrs;
|
||||
|
||||
if p.token == token::RBRACE && !vec::is_empty(initial_attrs) {
|
||||
p.fatal("expected item");
|
||||
}
|
||||
|
||||
while p.token != token::RBRACE {
|
||||
alt p.token {
|
||||
token::SEMI. {
|
||||
p.bump(); // empty
|
||||
}
|
||||
_ {
|
||||
let stmt = parse_stmt(p);
|
||||
let stmt = parse_stmt(p, initial_attrs);
|
||||
initial_attrs = [];
|
||||
alt stmt.node {
|
||||
ast::stmt_expr(e, stmt_id) { // Expression without semicolon:
|
||||
alt p.token {
|
||||
|
@ -1751,7 +1793,8 @@ fn parse_item_fn(p: parser, purity: ast::purity,
|
|||
let lo = p.last_span.lo;
|
||||
let t = parse_fn_header(p);
|
||||
let decl = parse_fn_decl(p, purity);
|
||||
let body = parse_block(p);
|
||||
let (inner_attrs, body) = parse_inner_attrs_and_block(p, true);
|
||||
let attrs = attrs + inner_attrs;
|
||||
ret mk_item(p, lo, body.span.hi, t.ident,
|
||||
ast::item_fn(decl, t.tps, body), attrs);
|
||||
}
|
||||
|
@ -1832,8 +1875,7 @@ fn parse_item_res(p: parser, attrs: [ast::attribute]) -> @ast::item {
|
|||
fn parse_mod_items(p: parser, term: token::token,
|
||||
first_item_attrs: [ast::attribute]) -> ast::_mod {
|
||||
// Shouldn't be any view items since we've already parsed an item attr
|
||||
let view_items =
|
||||
if vec::len(first_item_attrs) == 0u { parse_view(p) } else { [] };
|
||||
let view_items = maybe_parse_view(p, first_item_attrs);
|
||||
let items: [@ast::item] = [];
|
||||
let initial_attrs = first_item_attrs;
|
||||
while p.token != term {
|
||||
|
@ -2134,14 +2176,20 @@ fn parse_item(p: parser, attrs: [ast::attribute]) -> option::t<@ast::item> {
|
|||
// extensions, which both begin with token.POUND
|
||||
type attr_or_ext = option::t<either::t<[ast::attribute], @ast::expr>>;
|
||||
|
||||
fn parse_outer_attrs_or_ext(p: parser) -> attr_or_ext {
|
||||
fn parse_outer_attrs_or_ext(
|
||||
p: parser,
|
||||
first_item_attrs: [ast::attribute]) -> attr_or_ext {
|
||||
let expect_item_next = vec::is_not_empty(first_item_attrs);
|
||||
if p.token == token::POUND {
|
||||
let lo = p.span.lo;
|
||||
p.bump();
|
||||
if p.token == token::LBRACKET {
|
||||
if p.look_ahead(1u) == token::LBRACKET {
|
||||
p.bump();
|
||||
let first_attr = parse_attribute_naked(p, ast::attr_outer, lo);
|
||||
ret some(left([first_attr] + parse_outer_attributes(p)));
|
||||
} else if !(p.token == token::LT || p.token == token::LBRACKET) {
|
||||
} else if !(p.look_ahead(1u) == token::LT
|
||||
|| p.look_ahead(1u) == token::LBRACKET
|
||||
|| expect_item_next) {
|
||||
p.bump();
|
||||
ret some(right(parse_syntax_ext_naked(p, lo)));
|
||||
} else { ret none; }
|
||||
} else { ret none; }
|
||||
|
@ -2182,6 +2230,10 @@ fn parse_inner_attrs_and_next(p: parser) ->
|
|||
let inner_attrs: [ast::attribute] = [];
|
||||
let next_outer_attrs: [ast::attribute] = [];
|
||||
while p.token == token::POUND {
|
||||
if p.look_ahead(1u) != token::LBRACKET {
|
||||
// This is an extension
|
||||
break;
|
||||
}
|
||||
let attr = parse_attribute(p, ast::attr_inner);
|
||||
if p.token == token::SEMI {
|
||||
p.bump();
|
||||
|
@ -2377,22 +2429,37 @@ fn is_view_item(p: parser) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
fn parse_view(p: parser) -> [@ast::view_item] {
|
||||
parse_view_while(p, is_view_item)
|
||||
fn maybe_parse_view(
|
||||
p: parser,
|
||||
first_item_attrs: [ast::attribute]) -> [@ast::view_item] {
|
||||
|
||||
maybe_parse_view_while(p, first_item_attrs, is_view_item)
|
||||
}
|
||||
|
||||
fn parse_view_import_only(p: parser) -> [@ast::view_item] {
|
||||
parse_view_while(p, bind is_word(_, "import"))
|
||||
fn maybe_parse_view_import_only(
|
||||
p: parser,
|
||||
first_item_attrs: [ast::attribute]) -> [@ast::view_item] {
|
||||
|
||||
maybe_parse_view_while(p, first_item_attrs, bind is_word(_, "import"))
|
||||
}
|
||||
|
||||
fn parse_view_while(p: parser, f: fn@(parser) -> bool) -> [@ast::view_item] {
|
||||
let items = [];
|
||||
while f(p) { items += [parse_view_item(p)]; }
|
||||
ret items;
|
||||
fn maybe_parse_view_while(
|
||||
p: parser,
|
||||
first_item_attrs: [ast::attribute],
|
||||
f: fn@(parser) -> bool) -> [@ast::view_item] {
|
||||
|
||||
if vec::len(first_item_attrs) == 0u {
|
||||
let items = [];
|
||||
while f(p) { items += [parse_view_item(p)]; }
|
||||
ret items;
|
||||
} else {
|
||||
// Shouldn't be any view items since we've already parsed an item attr
|
||||
ret [];
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_native_view(p: parser) -> [@ast::view_item] {
|
||||
parse_view_while(p, is_view_item)
|
||||
maybe_parse_view_while(p, [], is_view_item)
|
||||
}
|
||||
|
||||
fn parse_crate_from_source_file(input: str, cfg: ast::crate_cfg,
|
||||
|
|
|
@ -366,7 +366,7 @@ fn print_item(s: ps, &&item: @ast::item) {
|
|||
ast::item_fn(decl, typarams, body) {
|
||||
print_fn(s, decl, item.ident, typarams);
|
||||
word(s.s, " ");
|
||||
print_block(s, body);
|
||||
print_block_with_attrs(s, body, item.attrs);
|
||||
}
|
||||
ast::item_mod(_mod) {
|
||||
head(s, "mod");
|
||||
|
@ -551,10 +551,20 @@ fn print_block(s: ps, blk: ast::blk) {
|
|||
print_possibly_embedded_block(s, blk, block_normal, indent_unit);
|
||||
}
|
||||
|
||||
fn print_block_with_attrs(s: ps, blk: ast::blk, attrs: [ast::attribute]) {
|
||||
print_possibly_embedded_block_(s, blk, block_normal, indent_unit, attrs);
|
||||
}
|
||||
|
||||
tag embed_type { block_macro; block_block_fn; block_normal; }
|
||||
|
||||
fn print_possibly_embedded_block(s: ps, blk: ast::blk, embedded: embed_type,
|
||||
indented: uint) {
|
||||
print_possibly_embedded_block_(
|
||||
s, blk, embedded, indented, []);
|
||||
}
|
||||
|
||||
fn print_possibly_embedded_block_(s: ps, blk: ast::blk, embedded: embed_type,
|
||||
indented: uint, attrs: [ast::attribute]) {
|
||||
alt blk.node.rules {
|
||||
ast::unchecked_blk. { word(s.s, "unchecked"); }
|
||||
ast::unsafe_blk. { word(s.s, "unsafe"); }
|
||||
|
@ -570,6 +580,8 @@ fn print_possibly_embedded_block(s: ps, blk: ast::blk, embedded: embed_type,
|
|||
block_normal. { bopen(s); }
|
||||
}
|
||||
|
||||
print_inner_attributes(s, attrs);
|
||||
|
||||
for vi in blk.node.view_items { print_view_item(s, vi); }
|
||||
for st: @ast::stmt in blk.node.stmts {
|
||||
print_stmt(s, *st);
|
||||
|
|
4
src/test/compile-fail/attr-before-ext.rs
Normal file
4
src/test/compile-fail/attr-before-ext.rs
Normal file
|
@ -0,0 +1,4 @@
|
|||
fn main() {
|
||||
#[attr]
|
||||
#debug("hi"); //! ERROR expected item
|
||||
}
|
4
src/test/compile-fail/attr-before-let.rs
Normal file
4
src/test/compile-fail/attr-before-let.rs
Normal file
|
@ -0,0 +1,4 @@
|
|||
fn main() {
|
||||
#[attr]
|
||||
let _i = 0; //! ERROR expected item
|
||||
}
|
13
src/test/pretty/attr-fn-inner.rs
Normal file
13
src/test/pretty/attr-fn-inner.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
// pp-exact
|
||||
// Testing that both the inner item and next outer item are
|
||||
// preserved, and that the first outer item parsed in main is not
|
||||
// accidentally carried over to each inner function
|
||||
|
||||
fn main() {
|
||||
#[inner_attr];
|
||||
#[outer_attr]
|
||||
fn f() { }
|
||||
|
||||
#[outer_attr]
|
||||
fn g() { }
|
||||
}
|
|
@ -183,6 +183,10 @@ mod test_literals {
|
|||
mod m { }
|
||||
}
|
||||
|
||||
fn test_fn_inner() {
|
||||
#[inner_fn_attr];
|
||||
}
|
||||
|
||||
fn main() { }
|
||||
|
||||
//
|
||||
|
|
Loading…
Reference in a new issue