about summary refs log tree commit diff
path: root/src/libsyntax/parse
diff options
context:
space:
mode:
authorBrian Anderson <banderson@mozilla.com>2012-07-02 14:44:31 -0700
committerBrian Anderson <banderson@mozilla.com>2012-07-02 15:23:41 -0700
commit569467eb0d110dd299cb2efcce13e998d1d9c11f (patch)
treef9214ae504b0402df4e55ef6bf3a3e8bc6a831b8 /src/libsyntax/parse
parent3ced5b0da2d0bb0498858553acefd09e347c1665 (diff)
parent29eb788b1f957f84d1b19a6ddcb7d5f4852d4973 (diff)
downloadrust-569467eb0d110dd299cb2efcce13e998d1d9c11f.tar.gz
rust-569467eb0d110dd299cb2efcce13e998d1d9c11f.zip
Merge remote-tracking branch 'Dretch/prettydocs'
Conflicts:
	src/compiletest/errors.rs
	src/libsyntax/parse/attr.rs
	src/libsyntax/parse/comments.rs
	src/test/compile-fail/ambig_impl_unify.rs
	src/test/compile-fail/assign-super.rs
	src/test/compile-fail/bad-for-loop.rs
	src/test/compile-fail/bad-var-env-capture-in-block-arg.rs
	src/test/compile-fail/block-arg-as-stmt-with-value.rs
	src/test/compile-fail/borrowck-assign-comp-idx.rs
	src/test/compile-fail/borrowck-lend-flow.rs
	src/test/compile-fail/borrowck-loan-blocks-move-cc.rs
	src/test/compile-fail/borrowck-loan-blocks-mut-uniq.rs
	src/test/compile-fail/borrowck-loan-rcvr.rs
	src/test/compile-fail/borrowck-loan-vec-content.rs
	src/test/compile-fail/borrowck-mut-vec-as-imm-slice-bad.rs
	src/test/compile-fail/cap-clause-with-stack-closure.rs
	src/test/compile-fail/do1.rs
	src/test/compile-fail/do2.rs
	src/test/compile-fail/empty-vec-trailing-comma.rs
	src/test/compile-fail/evec-subtyping.rs
	src/test/compile-fail/issue-1896.rs
	src/test/compile-fail/issue-2149.rs
	src/test/compile-fail/issue-2150.rs
	src/test/compile-fail/issue-2487-b.rs
	src/test/compile-fail/kindck-implicit-close-over-mut-var.rs
	src/test/compile-fail/liveness-issue-2163.rs
	src/test/compile-fail/liveness-use-in-index-lvalue.rs
	src/test/compile-fail/no-reuse-move-arc.rs
	src/test/compile-fail/no-send-res-ports.rs
	src/test/compile-fail/non-const.rs
	src/test/compile-fail/pure-higher-order.rs
	src/test/compile-fail/pure-loop-body.rs
	src/test/compile-fail/regions-addr-of-upvar-self.rs
	src/test/compile-fail/regions-escape-loop-via-vec.rs
	src/test/compile-fail/regions-scoping.rs
	src/test/compile-fail/seq-args.rs
	src/test/compile-fail/tstate-unsat-in-called-fn-expr.rs
	src/test/compile-fail/tstate-unsat-in-fn-expr.rs
	src/test/compile-fail/vec-add.rs
	src/test/compile-fail/vec-concat-bug.rs
	src/test/compile-fail/vector-no-ann.rs
Diffstat (limited to 'src/libsyntax/parse')
-rw-r--r--src/libsyntax/parse/attr.rs86
-rw-r--r--src/libsyntax/parse/comments.rs128
-rw-r--r--src/libsyntax/parse/lexer.rs66
-rw-r--r--src/libsyntax/parse/token.rs5
4 files changed, 243 insertions, 42 deletions
diff --git a/src/libsyntax/parse/attr.rs b/src/libsyntax/parse/attr.rs
index 13c72d1e315..d804a927edb 100644
--- a/src/libsyntax/parse/attr.rs
+++ b/src/libsyntax/parse/attr.rs
@@ -15,7 +15,8 @@ impl parser_attr for parser {
         -> attr_or_ext
     {
         let expect_item_next = vec::is_not_empty(first_item_attrs);
-        if self.token == token::POUND {
+        alt self.token {
+          token::POUND {
             let lo = self.span.lo;
             if self.look_ahead(1u) == token::LBRACKET {
                 self.bump();
@@ -30,15 +31,40 @@ impl parser_attr for parser {
                 self.bump();
                 ret some(right(self.parse_syntax_ext_naked(lo)));
             } else { ret none; }
-        } else { ret none; }
+        }
+        token::DOC_COMMENT(_) {
+          ret some(left(self.parse_outer_attributes()));
+        }
+        _ {
+          ret none;
+        }
+      }
     }
 
     // Parse attributes that appear before an item
     fn parse_outer_attributes() -> ~[ast::attribute] {
         let mut attrs: ~[ast::attribute] = ~[];
-        while self.token == token::POUND
-            && self.look_ahead(1u) == token::LBRACKET {
-            vec::push(attrs, self.parse_attribute(ast::attr_outer));
+        loop {
+            alt copy self.token {
+              token::POUND {
+                if self.look_ahead(1u) != token::LBRACKET {
+                    break;
+                }
+                attrs += [self.parse_attribute(ast::attr_outer)]/~;
+              }
+              token::DOC_COMMENT(s) {
+                let attr = ::attr::mk_sugared_doc_attr(
+                        *self.get_str(s), self.span.lo, self.span.hi);
+                if attr.node.style != ast::attr_outer {
+                  self.fatal("expected outer comment");
+                }
+                attrs += [attr]/~;
+                self.bump();
+              }
+              _ {
+                break;
+              }
+            }
         }
         ret attrs;
     }
@@ -55,7 +81,8 @@ impl parser_attr for parser {
         let meta_item = self.parse_meta_item();
         self.expect(token::RBRACKET);
         let mut hi = self.span.hi;
-        ret spanned(lo, hi, {style: style, value: *meta_item});
+        ret spanned(lo, hi, {style: style, value: *meta_item,
+                             is_sugared_doc: false});
     }
 
     // Parse attributes that appear after the opening of an item, each
@@ -68,22 +95,41 @@ impl parser_attr for parser {
         {inner: ~[ast::attribute], next: ~[ast::attribute]} {
         let mut inner_attrs: ~[ast::attribute] = ~[];
         let mut next_outer_attrs: ~[ast::attribute] = ~[];
-        while self.token == token::POUND {
-            if self.look_ahead(1u) != token::LBRACKET {
-                // This is an extension
-                break;
-            }
-            let attr = self.parse_attribute(ast::attr_inner);
-            if self.token == token::SEMI {
+        loop {
+            alt copy self.token {
+              token::POUND {
+                if self.look_ahead(1u) != token::LBRACKET {
+                    // This is an extension
+                    break;
+                }
+                let attr = self.parse_attribute(ast::attr_inner);
+                if self.token == token::SEMI {
+                    self.bump();
+                    inner_attrs += [attr]/~;
+                } else {
+                    // It's not really an inner attribute
+                    let outer_attr =
+                        spanned(attr.span.lo, attr.span.hi,
+                            {style: ast::attr_outer, value: attr.node.value,
+                             is_sugared_doc: false});
+                    next_outer_attrs += [outer_attr]/~;
+                    break;
+                }
+              }
+              token::DOC_COMMENT(s) {
+                let attr = ::attr::mk_sugared_doc_attr(
+                        *self.get_str(s), self.span.lo, self.span.hi);
                 self.bump();
-                vec::push(inner_attrs, attr);
-            } else {
-                // It's not really an inner attribute
-                let outer_attr =
-                    spanned(attr.span.lo, attr.span.hi,
-                            {style: ast::attr_outer, value: attr.node.value});
-                vec::push(next_outer_attrs, outer_attr);
+                if attr.node.style == ast::attr_inner {
+                  inner_attrs += [attr]/~;
+                } else {
+                  next_outer_attrs += [attr]/~;
+                  break;
+                }
+              }
+              _ {
                 break;
+              }
             }
         }
         ret {inner: inner_attrs, next: next_outer_attrs};
diff --git a/src/libsyntax/parse/comments.rs b/src/libsyntax/parse/comments.rs
index 3fbb16b5574..e188331dd24 100644
--- a/src/libsyntax/parse/comments.rs
+++ b/src/libsyntax/parse/comments.rs
@@ -8,6 +8,7 @@ export cmnt;
 export lit;
 export cmnt_style;
 export gather_comments_and_literals;
+export is_doc_comment, doc_comment_style, strip_doc_comment_decoration;
 
 enum cmnt_style {
     isolated, // No code on either side of each line of the comment
@@ -18,6 +19,81 @@ enum cmnt_style {
 
 type cmnt = {style: cmnt_style, lines: ~[str], pos: uint};
 
+fn is_doc_comment(s: str) -> bool {
+    s.starts_with("///") ||
+    s.starts_with("//!") ||
+    s.starts_with("/**") ||
+    s.starts_with("/*!")
+}
+
+fn doc_comment_style(comment: str) -> ast::attr_style {
+    assert is_doc_comment(comment);
+    if comment.starts_with("//!") || comment.starts_with("/*!") {
+        ast::attr_inner
+    } else {
+        ast::attr_outer
+    }
+}
+
+fn strip_doc_comment_decoration(comment: str) -> str {
+
+    /// remove whitespace-only lines from the start/end of lines
+    fn vertical_trim(lines: [str]/~) -> [str]/~ {
+        let mut i = 0u, j = lines.len();
+        while i < j && lines[i].trim().is_empty() {
+            i += 1u;
+        }
+        while j > i && lines[j - 1u].trim().is_empty() {
+            j -= 1u;
+        }
+        ret lines.slice(i, j);
+    }
+
+    // drop leftmost columns that contain only values in chars
+    fn block_trim(lines: [str]/~, chars: str, max: option<uint>) -> [str]/~ {
+
+        let mut i = max.get_default(uint::max_value);
+        for lines.each |line| {
+            if line.trim().is_empty() {
+                cont;
+            }
+            for line.each_chari |j, c| {
+                if j >= i {
+                    break;
+                }
+                if !chars.contains_char(c) {
+                    i = j;
+                    break;
+                }
+            }
+        }
+
+        ret do lines.map |line| {
+            let chars = str::chars(line);
+            if i > chars.len() {
+                ""
+            } else {
+                str::from_chars(chars.slice(i, chars.len()))
+            }
+        };
+    }
+
+    if comment.starts_with("//") {
+        ret comment.slice(3u, comment.len()).trim();
+    }
+
+    if comment.starts_with("/*") {
+        let lines = str::lines_any(comment.slice(3u, comment.len() - 2u));
+        let lines = vertical_trim(lines);
+        let lines = block_trim(lines, "\t ", none);
+        let lines = block_trim(lines, "*", some(1u));
+        let lines = block_trim(lines, "\t ", none);
+        ret str::connect(lines, "\n");
+    }
+
+    fail "not a doc-comment: " + comment;
+}
+
 fn read_to_eol(rdr: string_reader) -> str {
     let mut val = "";
     while rdr.curr != '\n' && !is_eof(rdr) {
@@ -57,29 +133,41 @@ fn consume_whitespace_counting_blank_lines(rdr: string_reader,
     }
 }
 
-fn read_shebang_comment(rdr: string_reader, code_to_the_left: bool) -> cmnt {
+
+fn read_shebang_comment(rdr: string_reader, code_to_the_left: bool,
+                                                        &comments: [cmnt]/~) {
     #debug(">>> shebang comment");
     let p = rdr.chpos;
     #debug("<<< shebang comment");
-    ret {style: if code_to_the_left { trailing } else { isolated },
-         lines: ~[read_one_line_comment(rdr)],
-         pos: p};
+    vec::push(comments, {
+        style: if code_to_the_left { trailing } else { isolated },
+        lines: ~[read_one_line_comment(rdr)],
+        pos: p
+    });
 }
 
-fn read_line_comments(rdr: string_reader, code_to_the_left: bool) -> cmnt {
+fn read_line_comments(rdr: string_reader, code_to_the_left: bool,
+                                                        &comments: [cmnt]/~) {
     #debug(">>> line comments");
     let p = rdr.chpos;
     let mut lines: ~[str] = ~[];
     while rdr.curr == '/' && nextch(rdr) == '/' {
         let line = read_one_line_comment(rdr);
         log(debug, line);
+        if is_doc_comment(line) { // doc-comments are not put in comments
+            break;
+        }
         vec::push(lines, line);
         consume_non_eol_whitespace(rdr);
     }
     #debug("<<< line comments");
-    ret {style: if code_to_the_left { trailing } else { isolated },
-         lines: lines,
-         pos: p};
+    if !lines.is_empty() {
+        vec::push(comments, {
+            style: if code_to_the_left { trailing } else { isolated },
+            lines: lines,
+            pos: p
+        });
+    }
 }
 
 fn all_whitespace(s: str, begin: uint, end: uint) -> bool {
@@ -101,13 +189,27 @@ fn trim_whitespace_prefix_and_push_line(&lines: ~[str],
     vec::push(lines, s1);
 }
 
-fn read_block_comment(rdr: string_reader, code_to_the_left: bool) -> cmnt {
+fn read_block_comment(rdr: string_reader, code_to_the_left: bool,
+                                                        &comments: [cmnt]/~) {
     #debug(">>> block comment");
     let p = rdr.chpos;
     let mut lines: ~[str] = ~[];
     let mut col: uint = rdr.col;
     bump(rdr);
     bump(rdr);
+
+    // doc-comments are not really comments, they are attributes
+    if rdr.curr == '*' || rdr.curr == '!' {
+        while !(rdr.curr == '*' && nextch(rdr) == '/') && !is_eof(rdr) {
+            bump(rdr);
+        }
+        if !is_eof(rdr) {
+            bump(rdr);
+            bump(rdr);
+        }
+        ret;
+    }
+
     let mut curr_line = "/*";
     let mut level: int = 1;
     while level > 0 {
@@ -143,7 +245,7 @@ fn read_block_comment(rdr: string_reader, code_to_the_left: bool) -> cmnt {
         style = mixed;
     }
     #debug("<<< block comment");
-    ret {style: style, lines: lines, pos: p};
+    vec::push(comments, {style: style, lines: lines, pos: p});
 }
 
 fn peeking_at_comment(rdr: string_reader) -> bool {
@@ -156,11 +258,11 @@ fn consume_comment(rdr: string_reader, code_to_the_left: bool,
                    &comments: ~[cmnt]) {
     #debug(">>> consume comment");
     if rdr.curr == '/' && nextch(rdr) == '/' {
-        vec::push(comments, read_line_comments(rdr, code_to_the_left));
+        read_line_comments(rdr, code_to_the_left, comments);
     } else if rdr.curr == '/' && nextch(rdr) == '*' {
-        vec::push(comments, read_block_comment(rdr, code_to_the_left));
+        read_block_comment(rdr, code_to_the_left, comments);
     } else if rdr.curr == '#' && nextch(rdr) == '!' {
-        vec::push(comments, read_shebang_comment(rdr, code_to_the_left));
+        read_shebang_comment(rdr, code_to_the_left, comments);
     } else { fail; }
     #debug("<<< consume comment");
 }
diff --git a/src/libsyntax/parse/lexer.rs b/src/libsyntax/parse/lexer.rs
index 9da63508b12..8a32ecdac64 100644
--- a/src/libsyntax/parse/lexer.rs
+++ b/src/libsyntax/parse/lexer.rs
@@ -161,7 +161,11 @@ impl tt_reader_as_reader of reader for tt_reader {
 }
 
 fn string_advance_token(&&r: string_reader) {
-    consume_whitespace_and_comments(r);
+    for consume_whitespace_and_comments(r).each |comment| {
+        r.peek_tok = comment.tok;
+        r.peek_span = comment.sp;
+        ret;
+    }
 
     if is_eof(r) {
         r.peek_tok = token::EOF;
@@ -277,22 +281,41 @@ fn is_hex_digit(c: char) -> bool {
 
 fn is_bin_digit(c: char) -> bool { ret c == '0' || c == '1'; }
 
-fn consume_whitespace_and_comments(rdr: string_reader) {
+// might return a sugared-doc-attr
+fn consume_whitespace_and_comments(rdr: string_reader)
+                                -> option<{tok: token::token, sp: span}> {
     while is_whitespace(rdr.curr) { bump(rdr); }
     ret consume_any_line_comment(rdr);
 }
 
-fn consume_any_line_comment(rdr: string_reader) {
+// might return a sugared-doc-attr
+fn consume_any_line_comment(rdr: string_reader)
+                                -> option<{tok: token::token, sp: span}> {
     if rdr.curr == '/' {
         alt nextch(rdr) {
           '/' {
-            while rdr.curr != '\n' && !is_eof(rdr) { bump(rdr); }
-            // Restart whitespace munch.
-
-            ret consume_whitespace_and_comments(rdr);
+            bump(rdr);
+            bump(rdr);
+            // line comments starting with "///" or "//!" are doc-comments
+            if rdr.curr == '/' || rdr.curr == '!' {
+                let start_chpos = rdr.chpos - 2u;
+                let mut acc = "//";
+                while rdr.curr != '\n' && !is_eof(rdr) {
+                    str::push_char(acc, rdr.curr);
+                    bump(rdr);
+                }
+                ret some({
+                    tok: token::DOC_COMMENT(intern(*rdr.interner, @acc)),
+                    sp: ast_util::mk_sp(start_chpos, rdr.chpos)
+                });
+            } else {
+                while rdr.curr != '\n' && !is_eof(rdr) { bump(rdr); }
+                // Restart whitespace munch.
+                ret consume_whitespace_and_comments(rdr);
+            }
           }
           '*' { bump(rdr); bump(rdr); ret consume_block_comment(rdr); }
-          _ { ret; }
+          _ {}
         }
     } else if rdr.curr == '#' {
         if nextch(rdr) == '!' {
@@ -305,9 +328,34 @@ fn consume_any_line_comment(rdr: string_reader) {
             }
         }
     }
+    ret none;
 }
 
-fn consume_block_comment(rdr: string_reader) {
+// might return a sugared-doc-attr
+fn consume_block_comment(rdr: string_reader)
+                                -> option<{tok: token::token, sp: span}> {
+
+    // block comments starting with "/**" or "/*!" are doc-comments
+    if rdr.curr == '*' || rdr.curr == '!' {
+        let start_chpos = rdr.chpos - 2u;
+        let mut acc = "/*";
+        while !(rdr.curr == '*' && nextch(rdr) == '/') && !is_eof(rdr) {
+            str::push_char(acc, rdr.curr);
+            bump(rdr);
+        }
+        if is_eof(rdr) {
+            rdr.fatal("unterminated block doc-comment");
+        } else {
+            acc += "*/";
+            bump(rdr);
+            bump(rdr);
+            ret some({
+                tok: token::DOC_COMMENT(intern(*rdr.interner, @acc)),
+                sp: ast_util::mk_sp(start_chpos, rdr.chpos)
+            });
+        }
+    }
+
     let mut level: int = 1;
     while level > 0 {
         if is_eof(rdr) { rdr.fatal("unterminated block comment"); }
diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs
index a5d119d8986..a2bd503ffca 100644
--- a/src/libsyntax/parse/token.rs
+++ b/src/libsyntax/parse/token.rs
@@ -80,6 +80,7 @@ enum token {
 
     //ACTUALLY(whole_nonterminal),
 
+    DOC_COMMENT(str_num),
     EOF,
 }
 
@@ -170,11 +171,15 @@ fn to_str(in: interner<@str>, t: token) -> str {
             + str::escape_default(*interner::get(in, s))
             + "\""
       }
+
       /* Name components */
       IDENT(s, _) {
         *interner::get(in, s)
       }
       UNDERSCORE { "_" }
+
+      /* Other */
+      DOC_COMMENT(s) { *interner::get(in, s) }
       EOF { "<eof>" }
     }
 }