diff --git a/src/comp/syntax/parse/parser.rs b/src/comp/syntax/parse/parser.rs index b52b02a3323..3f5cae0a161 100644 --- a/src/comp/syntax/parse/parser.rs +++ b/src/comp/syntax/parse/parser.rs @@ -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>; -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, diff --git a/src/comp/syntax/print/pprust.rs b/src/comp/syntax/print/pprust.rs index ea2404e849c..e0c1acc4fcc 100644 --- a/src/comp/syntax/print/pprust.rs +++ b/src/comp/syntax/print/pprust.rs @@ -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); diff --git a/src/test/compile-fail/attr-before-ext.rs b/src/test/compile-fail/attr-before-ext.rs new file mode 100644 index 00000000000..8409ab8ef52 --- /dev/null +++ b/src/test/compile-fail/attr-before-ext.rs @@ -0,0 +1,4 @@ +fn main() { + #[attr] + #debug("hi"); //! ERROR expected item +} \ No newline at end of file diff --git a/src/test/compile-fail/attr-before-let.rs b/src/test/compile-fail/attr-before-let.rs new file mode 100644 index 00000000000..814ad400d6b --- /dev/null +++ b/src/test/compile-fail/attr-before-let.rs @@ -0,0 +1,4 @@ +fn main() { + #[attr] + let _i = 0; //! ERROR expected item +} \ No newline at end of file diff --git a/src/test/pretty/attr-fn-inner.rs b/src/test/pretty/attr-fn-inner.rs new file mode 100644 index 00000000000..87e62c004fe --- /dev/null +++ b/src/test/pretty/attr-fn-inner.rs @@ -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() { } +} diff --git a/src/test/run-pass/item-attributes.rs b/src/test/run-pass/item-attributes.rs index eba33e5e67f..ba00bb4a302 100644 --- a/src/test/run-pass/item-attributes.rs +++ b/src/test/run-pass/item-attributes.rs @@ -183,6 +183,10 @@ mod test_literals { mod m { } } +fn test_fn_inner() { + #[inner_fn_attr]; +} + fn main() { } //