@@ -50,25 +50,50 @@ impl EarlyLintPass for Doc {
50
50
}
51
51
}
52
52
53
+ /// Cleanup documentation decoration (`///` and such).
54
+ ///
55
+ /// We can't use `syntax::attr::AttributeMethods::with_desugared_doc` or
56
+ /// `syntax::parse::lexer::comments::strip_doc_comment_decoration` because we need to keep track of
57
+ /// the span but this function is inspired from the later.
58
+ #[ allow( cast_possible_truncation) ]
59
+ pub fn strip_doc_comment_decoration ( ( comment, span) : ( & str , Span ) ) -> Vec < ( & str , Span ) > {
60
+ // one-line comments lose their prefix
61
+ const ONELINERS : & ' static [ & ' static str ] = & [ "///!" , "///" , "//!" , "//" ] ;
62
+ for prefix in ONELINERS {
63
+ if comment. starts_with ( * prefix) {
64
+ return vec ! [ (
65
+ & comment[ prefix. len( ) ..] ,
66
+ Span { lo: span. lo + BytePos ( prefix. len( ) as u32 ) , ..span }
67
+ ) ] ;
68
+ }
69
+ }
70
+
71
+ if comment. starts_with ( "/*" ) {
72
+ return comment[ 3 ..comment. len ( ) - 2 ] . lines ( ) . map ( |line| {
73
+ let offset = line. as_ptr ( ) as usize - comment. as_ptr ( ) as usize ;
74
+ debug_assert_eq ! ( offset as u32 as usize , offset) ;
75
+
76
+ (
77
+ line,
78
+ Span {
79
+ lo : span. lo + BytePos ( offset as u32 ) ,
80
+ ..span
81
+ }
82
+ )
83
+ } ) . collect ( ) ;
84
+ }
85
+
86
+ panic ! ( "not a doc-comment: {}" , comment) ;
87
+ }
88
+
53
89
pub fn check_attrs < ' a > ( cx : & EarlyContext , valid_idents : & [ String ] , attrs : & ' a [ ast:: Attribute ] ) {
54
90
let mut docs = vec ! [ ] ;
55
91
56
- let mut in_multiline = false ;
57
92
for attr in attrs {
58
93
if attr. node . is_sugared_doc {
59
94
if let ast:: MetaItemKind :: NameValue ( _, ref doc) = attr. node . value . node {
60
95
if let ast:: LitKind :: Str ( ref doc, _) = doc. node {
61
- // doc comments start with `///` or `//!`
62
- let real_doc = & doc[ 3 ..] ;
63
- let mut span = attr. span ;
64
- span. lo = span. lo + BytePos ( 3 ) ;
65
-
66
- // check for multiline code blocks
67
- if real_doc. trim_left ( ) . starts_with ( "```" ) {
68
- in_multiline = !in_multiline;
69
- } else if !in_multiline {
70
- docs. push ( ( real_doc, span) ) ;
71
- }
96
+ docs. extend_from_slice ( & strip_doc_comment_decoration ( ( doc, attr. span ) ) ) ;
72
97
}
73
98
}
74
99
}
@@ -135,11 +160,11 @@ fn check_doc(cx: &EarlyContext, valid_idents: &[String], docs: &[(&str, Span)])
135
160
}
136
161
137
162
#[ allow( while_let_on_iterator) ] // borrowck complains about for
138
- fn jump_to ( & mut self , n : char ) -> Result < ( ) , ( ) > {
139
- while let Some ( ( _ , c) ) = self . next ( ) {
163
+ fn jump_to ( & mut self , n : char ) -> Result < bool , ( ) > {
164
+ while let Some ( ( new_line , c) ) = self . next ( ) {
140
165
if c == n {
141
166
self . advance_begin ( ) ;
142
- return Ok ( ( ) ) ;
167
+ return Ok ( new_line ) ;
143
168
}
144
169
}
145
170
@@ -217,6 +242,54 @@ fn check_doc(cx: &EarlyContext, valid_idents: &[String], docs: &[(&str, Span)])
217
242
pos : 0 ,
218
243
} ;
219
244
245
+ /// Check for fanced code block.
246
+ macro_rules! check_block {
247
+ ( $parser: expr, $c: tt, $new_line: expr) => { {
248
+ check_block!( $parser, $c, $c, $new_line)
249
+ } } ;
250
+
251
+ ( $parser: expr, $c: pat, $c_expr: expr, $new_line: expr) => { {
252
+ fn check_block( parser: & mut Parser , new_line: bool ) -> Result <bool , ( ) > {
253
+ if new_line {
254
+ let mut lookup_parser = parser. clone( ) ;
255
+ if let ( Some ( ( false , $c) ) , Some ( ( false , $c) ) ) = ( lookup_parser. next( ) , lookup_parser. next( ) ) {
256
+ * parser = lookup_parser;
257
+ // 3 or more ` or ~ open a code block to be closed with the same number of ` or ~
258
+ let mut open_count = 3 ;
259
+ while let Some ( ( false , $c) ) = parser. next( ) {
260
+ open_count += 1 ;
261
+ }
262
+
263
+ loop {
264
+ loop {
265
+ if try!( parser. jump_to( $c_expr) ) {
266
+ break ;
267
+ }
268
+ }
269
+
270
+ lookup_parser = parser. clone( ) ;
271
+ if let ( Some ( ( false , $c) ) , Some ( ( false , $c) ) ) = ( lookup_parser. next( ) , lookup_parser. next( ) ) {
272
+ let mut close_count = 3 ;
273
+ while let Some ( ( false , $c) ) = lookup_parser. next( ) {
274
+ close_count += 1 ;
275
+ }
276
+
277
+ if close_count == open_count {
278
+ * parser = lookup_parser;
279
+ return Ok ( true ) ;
280
+ }
281
+ }
282
+ }
283
+ }
284
+ }
285
+
286
+ Ok ( false )
287
+ }
288
+
289
+ check_block( & mut $parser, $new_line)
290
+ } } ;
291
+ }
292
+
220
293
loop {
221
294
match parser. next ( ) {
222
295
Some ( ( new_line, c) ) => {
@@ -225,7 +298,20 @@ fn check_doc(cx: &EarlyContext, valid_idents: &[String], docs: &[(&str, Span)])
225
298
parser. next_line ( ) ;
226
299
}
227
300
'`' => {
228
- try!( parser. jump_to ( '`' ) ) ;
301
+ if try!( check_block ! ( parser, '`' , new_line) ) {
302
+ continue ;
303
+ }
304
+
305
+ try!( parser. jump_to ( '`' ) ) ; // not a code block, just inline code
306
+ }
307
+ '~' => {
308
+ if try!( check_block ! ( parser, '~' , new_line) ) {
309
+ continue ;
310
+ }
311
+
312
+ // ~ does not introduce inline code, but two of them introduce
313
+ // strikethrough. Too bad for the consistency but we don't care about
314
+ // strikethrough.
229
315
}
230
316
'[' => {
231
317
// Check for a reference definition `[foo]:` at the beginning of a line
@@ -249,8 +335,12 @@ fn check_doc(cx: &EarlyContext, valid_idents: &[String], docs: &[(&str, Span)])
249
335
parser. link = false ;
250
336
251
337
match parser. peek ( ) {
252
- Some ( '(' ) => try!( parser. jump_to ( ')' ) ) ,
253
- Some ( '[' ) => try!( parser. jump_to ( ']' ) ) ,
338
+ Some ( '(' ) => {
339
+ try!( parser. jump_to ( ')' ) ) ;
340
+ }
341
+ Some ( '[' ) => {
342
+ try!( parser. jump_to ( ']' ) ) ;
343
+ }
254
344
Some ( _) => continue ,
255
345
None => return Err ( ( ) ) ,
256
346
}
0 commit comments