Skip to content

Commit 47f43da

Browse files
committed
Merge branch 'doc-comments'
2 parents 7a4ebce + 569467e commit 47f43da

File tree

268 files changed

+1051
-656
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

268 files changed

+1051
-656
lines changed

src/compiletest/errors.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,14 @@ fn load_errors(testfile: str) -> ~[expected_error] {
2121
}
2222

2323
fn parse_expected(line_num: uint, line: str) -> ~[expected_error] unsafe {
24-
let error_tag = "//!";
24+
let error_tag = "//~";
2525
let mut idx;
2626
alt str::find_str(line, error_tag) {
2727
option::none { ret ~[]; }
2828
option::some(nn) { idx = (nn as uint) + str::len(error_tag); }
2929
}
3030

31-
// "//!^^^ kind msg" denotes a message expected
31+
// "//~^^^ kind msg" denotes a message expected
3232
// three lines above current line:
3333
let mut adjust_line = 0u;
3434
let len = str::len(line);

src/etc/sugarise-doc-comments.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
#!/usr/bin/python
2+
3+
#
4+
# this script attempts to turn doc comment attributes (#[doc = "..."])
5+
# into sugared-doc-comments (/** ... */ and /// ...)
6+
#
7+
# it sugarises all .rs/.rc files underneath the working directory
8+
#
9+
10+
import sys, os, fnmatch, re
11+
12+
13+
DOC_PATTERN = '^(?P<indent>[\\t ]*)#\\[(\\s*)doc(\\s*)=' + \
14+
'(\\s*)"(?P<text>(\\"|[^"])*?)"(\\s*)\\]' + \
15+
'(?P<semi>;)?'
16+
17+
ESCAPES = [("\\'", "'"),
18+
('\\"', '"'),
19+
("\\n", "\n"),
20+
("\\r", "\r"),
21+
("\\t", "\t")]
22+
23+
24+
def unescape(s):
25+
for (find, repl) in ESCAPES:
26+
s = s.replace(find, repl)
27+
return s
28+
29+
30+
def block_trim(s):
31+
lns = s.splitlines()
32+
33+
# remove leading/trailing whitespace-lines
34+
while lns and not lns[0].strip():
35+
lns = lns[1:]
36+
while lns and not lns[-1].strip():
37+
lns = lns[:-1]
38+
39+
# remove leading horizontal whitespace
40+
n = sys.maxint
41+
for ln in lns:
42+
if ln.strip():
43+
n = min(n, len(re.search('^\s*', ln).group()))
44+
if n != sys.maxint:
45+
lns = [ln[n:] for ln in lns]
46+
47+
# strip trailing whitespace
48+
lns = [ln.rstrip() for ln in lns]
49+
50+
return lns
51+
52+
53+
def replace_doc(m):
54+
indent = m.group('indent')
55+
text = block_trim(unescape(m.group('text')))
56+
57+
if len(text) > 1:
58+
inner = '!' if m.group('semi') else '*'
59+
starify = lambda s: indent + ' *' + (' ' + s if s else '')
60+
text = '\n'.join(map(starify, text))
61+
repl = indent + '/*' + inner + '\n' + text + '\n' + indent + ' */'
62+
else:
63+
inner = '!' if m.group('semi') else '/'
64+
repl = indent + '//' + inner + ' ' + text[0]
65+
66+
return repl
67+
68+
69+
def sugarise_file(path):
70+
s = open(path).read()
71+
72+
r = re.compile(DOC_PATTERN, re.MULTILINE | re.DOTALL)
73+
ns = re.sub(r, replace_doc, s)
74+
75+
if s != ns:
76+
open(path, 'w').write(ns)
77+
78+
79+
for (dirpath, dirnames, filenames) in os.walk('.'):
80+
for name in fnmatch.filter(filenames, '*.r[sc]'):
81+
sugarise_file(os.path.join(dirpath, name))
82+

src/libcore/str.rs

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ export
5858
all, any,
5959
all_between, any_between,
6060
map,
61-
each,
62-
each_char,
61+
each, eachi,
62+
each_char, each_chari,
6363
bytes_iter,
6464
chars_iter,
6565
split_char_iter,
@@ -73,7 +73,7 @@ export
7373
find_char, find_char_from, find_char_between,
7474
rfind_char, rfind_char_from, rfind_char_between,
7575
find_str, find_str_from, find_str_between,
76-
contains,
76+
contains, contains_char,
7777
starts_with,
7878
ends_with,
7979

@@ -672,22 +672,35 @@ pure fn bytes_iter(ss: str/&, it: fn(u8)) {
672672
#[doc = "Iterate over the bytes in a string"]
673673
#[inline(always)]
674674
pure fn each(s: str/&, it: fn(u8) -> bool) {
675+
eachi(s, |_i, b| it(b) )
676+
}
677+
678+
#[doc = "Iterate over the bytes in a string, with indices"]
679+
#[inline(always)]
680+
pure fn eachi(s: str/&, it: fn(uint, u8) -> bool) {
675681
let mut i = 0u, l = len(s);
676682
while (i < l) {
677-
if !it(s[i]) { break; }
683+
if !it(i, s[i]) { break; }
678684
i += 1u;
679685
}
680686
}
681687

682688
#[doc = "Iterates over the chars in a string"]
683689
#[inline(always)]
684690
pure fn each_char(s: str/&, it: fn(char) -> bool) {
685-
let mut pos = 0u;
691+
each_chari(s, |_i, c| it(c))
692+
}
693+
694+
#[doc = "Iterates over the chars in a string, with indices"]
695+
#[inline(always)]
696+
pure fn each_chari(s: str/&, it: fn(uint, char) -> bool) {
697+
let mut pos = 0u, ch_pos = 0u;
686698
let len = len(s);
687699
while pos < len {
688700
let {ch, next} = char_range_at(s, pos);
689701
pos = next;
690-
if !it(ch) { break; }
702+
if !it(ch_pos, ch) { break; }
703+
ch_pos += 1u;
691704
}
692705
}
693706

@@ -1146,6 +1159,18 @@ pure fn contains(haystack: str/&a, needle: str/&b) -> bool {
11461159
option::is_some(find_str(haystack, needle))
11471160
}
11481161

1162+
#[doc = "
1163+
Returns true if a string contains a char.
1164+
1165+
# Arguments
1166+
1167+
* haystack - The string to look in
1168+
* needle - The char to look for
1169+
"]
1170+
pure fn contains_char(haystack: str/&, needle: char) -> bool {
1171+
option::is_some(find_char(haystack, needle))
1172+
}
1173+
11491174
#[doc = "
11501175
Returns true if one string starts with another
11511176
@@ -1879,12 +1904,21 @@ impl extensions/& for str/& {
18791904
#[doc = "Returns true if one string contains another"]
18801905
#[inline]
18811906
fn contains(needle: str/&a) -> bool { contains(self, needle) }
1907+
#[doc = "Returns true if a string contains a char"]
1908+
#[inline]
1909+
fn contains_char(needle: char) -> bool { contains_char(self, needle) }
18821910
#[doc = "Iterate over the bytes in a string"]
18831911
#[inline]
18841912
fn each(it: fn(u8) -> bool) { each(self, it) }
1913+
#[doc = "Iterate over the bytes in a string, with indices"]
1914+
#[inline]
1915+
fn eachi(it: fn(uint, u8) -> bool) { eachi(self, it) }
18851916
#[doc = "Iterate over the chars in a string"]
18861917
#[inline]
18871918
fn each_char(it: fn(char) -> bool) { each_char(self, it) }
1919+
#[doc = "Iterate over the chars in a string, with indices"]
1920+
#[inline]
1921+
fn each_chari(it: fn(uint, char) -> bool) { each_chari(self, it) }
18881922
#[doc = "Returns true if one string ends with another"]
18891923
#[inline]
18901924
fn ends_with(needle: str/&) -> bool { ends_with(self, needle) }
@@ -2644,6 +2678,14 @@ mod tests {
26442678
assert !contains(data, "ไท华");
26452679
}
26462680

2681+
#[test]
2682+
fn test_contains_char() {
2683+
assert contains_char("abc", 'b');
2684+
assert contains_char("a", 'a');
2685+
assert !contains_char("abc", 'd');
2686+
assert !contains_char("", 'a');
2687+
}
2688+
26472689
#[test]
26482690
fn test_chars_iter() {
26492691
let mut i = 0;

src/libsyntax/ast.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -652,8 +652,9 @@ type attribute = spanned<attribute_>;
652652
#[auto_serialize]
653653
enum attr_style { attr_outer, attr_inner, }
654654

655+
// doc-comments are promoted to attributes that have is_sugared_doc = true
655656
#[auto_serialize]
656-
type attribute_ = {style: attr_style, value: meta_item};
657+
type attribute_ = {style: attr_style, value: meta_item, is_sugared_doc: bool};
657658

658659
/*
659660
iface_refs appear in both impls and in classes that implement ifaces.

src/libsyntax/attr.rs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,21 @@ import std::map;
44
import std::map::hashmap;
55
import either::either;
66
import diagnostic::span_handler;
7-
import ast_util::dummy_spanned;
7+
import ast_util::{spanned, dummy_spanned};
8+
import parse::comments::{doc_comment_style, strip_doc_comment_decoration};
89

910
// Constructors
1011
export mk_name_value_item_str;
1112
export mk_name_value_item;
1213
export mk_list_item;
1314
export mk_word_item;
1415
export mk_attr;
16+
export mk_sugared_doc_attr;
1517

1618
// Conversion
1719
export attr_meta;
1820
export attr_metas;
21+
export desugar_doc_attr;
1922

2023
// Accessors
2124
export get_attr_name;
@@ -66,9 +69,19 @@ fn mk_word_item(+name: ast::ident) -> @ast::meta_item {
6669
}
6770

6871
fn mk_attr(item: @ast::meta_item) -> ast::attribute {
69-
ret dummy_spanned({style: ast::attr_inner, value: *item});
72+
ret dummy_spanned({style: ast::attr_inner, value: *item,
73+
is_sugared_doc: false});
7074
}
7175

76+
fn mk_sugared_doc_attr(text: str, lo: uint, hi: uint) -> ast::attribute {
77+
let lit = spanned(lo, hi, ast::lit_str(@text));
78+
let attr = {
79+
style: doc_comment_style(text),
80+
value: spanned(lo, hi, ast::meta_name_value(@"doc", lit)),
81+
is_sugared_doc: true
82+
};
83+
ret spanned(lo, hi, attr);
84+
}
7285

7386
/* Conversion */
7487

@@ -81,6 +94,16 @@ fn attr_metas(attrs: ~[ast::attribute]) -> ~[@ast::meta_item] {
8194
ret mitems;
8295
}
8396

97+
fn desugar_doc_attr(attr: ast::attribute) -> ast::attribute {
98+
if attr.node.is_sugared_doc {
99+
let comment = get_meta_item_value_str(@attr.node.value).get();
100+
let meta = mk_name_value_item_str(@"doc",
101+
strip_doc_comment_decoration(*comment));
102+
ret mk_attr(meta);
103+
} else {
104+
attr
105+
}
106+
}
84107

85108
/* Accessors */
86109

src/libsyntax/fold.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,8 @@ fn fold_meta_item_(&&mi: @meta_item, fld: ast_fold) -> @meta_item {
102102
fn fold_attribute_(at: attribute, fld: ast_fold) ->
103103
attribute {
104104
ret {node: {style: at.node.style,
105-
value: *fold_meta_item_(@at.node.value, fld)},
105+
value: *fold_meta_item_(@at.node.value, fld),
106+
is_sugared_doc: at.node.is_sugared_doc },
106107
span: fld.new_span(at.span)};
107108
}
108109
//used in noop_fold_foreign_item and noop_fold_fn_decl

0 commit comments

Comments
 (0)