about summary refs log tree commit diff
diff options
context:
space:
mode:
authorBrian Anderson <banderson@mozilla.com>2012-01-15 17:23:59 -0800
committerBrian Anderson <banderson@mozilla.com>2012-01-15 17:48:58 -0800
commit3466c9b4befd03fd39b099466b97fea92bb5109f (patch)
tree77c3918572db83a837878cdd5af428b3a12e7dec
parent17585cc47ec7c4ffc9d14f33473c4238b6680e88 (diff)
downloadrust-3466c9b4befd03fd39b099466b97fea92bb5109f.tar.gz
rust-3466c9b4befd03fd39b099466b97fea92bb5109f.zip
rustc: Parse fn inner attributes. Closes #1506
-rw-r--r--src/comp/syntax/parse/parser.rs125
-rw-r--r--src/comp/syntax/print/pprust.rs14
-rw-r--r--src/test/compile-fail/attr-before-ext.rs4
-rw-r--r--src/test/compile-fail/attr-before-let.rs4
-rw-r--r--src/test/pretty/attr-fn-inner.rs13
-rw-r--r--src/test/run-pass/item-attributes.rs4
6 files changed, 134 insertions, 30 deletions
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<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,
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() { }
 
 //