Skip to content

Commit 60d8bbf

Browse files
committed
WIP: refactor invalid_html lint to remove edge cases, passing main test
1 parent d47c875 commit 60d8bbf

File tree

3 files changed

+56
-43
lines changed

3 files changed

+56
-43
lines changed

src/librustdoc/passes/lint/html_tags.rs

Lines changed: 42 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,8 @@ struct TagParser {
249249
tag_name: String,
250250
tag_start_pos: usize,
251251
is_closing: bool,
252+
/// `true` if we are within a tag, but not within its name.
253+
in_attrs: bool,
252254
}
253255

254256
impl TagParser {
@@ -258,6 +260,7 @@ impl TagParser {
258260
tag_name: String::with_capacity(8),
259261
is_closing: false,
260262
tag_start_pos: 0,
263+
in_attrs: false,
261264
}
262265
}
263266

@@ -322,7 +325,7 @@ impl TagParser {
322325
) {
323326
let mut prev_pos = start_pos;
324327

325-
loop {
328+
'outer_loop: loop {
326329
let (pos, c) = match iter.peek() {
327330
Some((pos, c)) => (*pos, *c),
328331
// In case we reached the of the doc comment, we want to check that it's an
@@ -334,10 +337,11 @@ impl TagParser {
334337
// Checking if this is a closing tag (like `</a>` for `<a>`).
335338
if c == '/' && self.tag_name.is_empty() {
336339
self.is_closing = true;
337-
} else if is_valid_for_html_tag_name(c, self.tag_name.is_empty()) {
340+
} else if !self.in_attrs && is_valid_for_html_tag_name(c, self.tag_name.is_empty()) {
338341
self.tag_name.push(c);
339342
} else {
340343
if !self.tag_name.is_empty() {
344+
self.in_attrs = true;
341345
let mut r = Range { start: range.start + start_pos, end: range.start + pos };
342346
if c == '>' {
343347
// In case we have a tag without attribute, we can consider the span to
@@ -365,7 +369,7 @@ impl TagParser {
365369
}
366370
}
367371
if !found {
368-
break;
372+
break 'outer_loop;
369373
}
370374
}
371375
self.drop_tag(r, dox, f);
@@ -375,34 +379,42 @@ impl TagParser {
375379
if c != '>' {
376380
let mut quote = None;
377381
let mut after_eq = false;
378-
for (i, c) in text[pos..].char_indices() {
379-
if !c.is_whitespace() {
380-
if let Some(q) = quote {
381-
if c == q {
382-
quote = None;
383-
quote_pos = None;
384-
after_eq = false;
385-
}
386-
} else if c == '>' {
387-
break;
388-
} else if c == '<' {
389-
self.handle_lt_in_tag(range.clone(), pos + i, f);
390-
} else if c == '/' && !after_eq {
391-
is_self_closing = true;
392-
} else {
393-
if is_self_closing {
394-
is_self_closing = false;
395-
}
396-
if (c == '"' || c == '\'') && after_eq {
397-
quote = Some(c);
398-
quote_pos = Some(pos + i);
399-
} else if c == '=' {
400-
after_eq = true;
382+
'parse_til_gt: {
383+
for (i, c) in text[pos..].char_indices() {
384+
if !c.is_whitespace() {
385+
if let Some(q) = quote {
386+
if c == q {
387+
quote = None;
388+
quote_pos = None;
389+
after_eq = false;
390+
}
391+
} else if c == '>' {
392+
// fall through and call `tag_parsed`.
393+
break 'parse_til_gt;
394+
} else if c == '<' {
395+
self.handle_lt_in_tag(range.clone(), pos + i, f);
396+
} else if c == '/' && !after_eq {
397+
is_self_closing = true;
398+
} else {
399+
if is_self_closing {
400+
is_self_closing = false;
401+
}
402+
if (c == '"' || c == '\'') && after_eq {
403+
quote = Some(c);
404+
quote_pos = Some(pos + i);
405+
} else if c == '=' {
406+
after_eq = true;
407+
}
401408
}
409+
} else if quote.is_none() {
410+
after_eq = false;
402411
}
403-
} else if quote.is_none() {
404-
after_eq = false;
405412
}
413+
// if we've run out of text but still haven't found a `>`,
414+
// break out of the outer loop to skip over `tag_parsed`.
415+
// this allows us to either find the `>` in a later event
416+
// or emit a lint about it being missing.
417+
break 'outer_loop;
406418
}
407419
}
408420
if let Some(quote_pos) = quote_pos {
@@ -423,7 +435,7 @@ impl TagParser {
423435
if !valid {
424436
f(format!("invalid self-closing HTML tag `{}`", self.tag_name), &r, false);
425437
}
426-
} else {
438+
} else if !self.tag_name.is_empty() {
427439
self.tags.push((std::mem::take(&mut self.tag_name), r));
428440
}
429441
}
@@ -439,6 +451,7 @@ impl TagParser {
439451
fn tag_parsed(&mut self) {
440452
self.tag_name.clear();
441453
self.is_closing = false;
454+
self.in_attrs = false;
442455
}
443456

444457
fn extract_tags(

tests/rustdoc-ui/lints/invalid-html-tags.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ pub fn j() {}
106106
/// uiapp.run(&env::args().collect::<Vec<_>>());
107107
/// ```
108108
///
109-
/// <Vec<_> shouldn't warn!
109+
// <Vec<_> shouldn't warn!
110110
/// ``````
111111
pub fn k() {}
112112

@@ -161,8 +161,8 @@ pub fn r() {}
161161

162162
/// > <br>
163163
/// > <img
164-
/// > href="#broken"
165164
//~^ ERROR incomplete HTML tag `img`
165+
/// > href="#broken"
166166
pub fn s() {}
167167

168168
/// <br>
@@ -174,3 +174,6 @@ pub fn t() {}
174174
/// <br
175175
//~^ ERROR incomplete HTML tag `br`
176176
pub fn u() {}
177+
178+
/// <a href=">" alt="<">html5 allows this</a>
179+
pub fn no_error_5() {}

tests/rustdoc-ui/lints/invalid-html-tags.stderr

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ error: Unclosed HTML comment
8686
--> $DIR/invalid-html-tags.rs:88:5
8787
|
8888
LL | /// <!--
89-
| ^^^
89+
| ^^^^
9090

9191
error: unopened HTML tag `unopened-tag`
9292
--> $DIR/invalid-html-tags.rs:115:26
@@ -118,29 +118,26 @@ error: incomplete HTML tag `img`
118118
LL | /// <p><img</p>
119119
| ^^^^
120120

121-
error: unclosed HTML tag ``
122-
--> $DIR/invalid-html-tags.rs:158:8
121+
error: incomplete HTML tag `img`
122+
--> $DIR/invalid-html-tags.rs:163:7
123123
|
124-
LL | /// <p><img</p>
125-
| ^^^^
124+
LL | /// > <img
125+
| _______^
126+
LL | |
127+
LL | | /// > href="#broken"
128+
| |____________________^
126129

127130
error: incomplete HTML tag `br`
128131
--> $DIR/invalid-html-tags.rs:169:5
129132
|
130133
LL | /// <br<br>
131134
| ^^^
132135

133-
error: unclosed HTML tag ``
134-
--> $DIR/invalid-html-tags.rs:169:5
135-
|
136-
LL | /// <br<br>
137-
| ^^^
138-
139136
error: incomplete HTML tag `br`
140137
--> $DIR/invalid-html-tags.rs:174:5
141138
|
142139
LL | /// <br
143140
| ^^^
144141

145-
error: aborting due to 23 previous errors
142+
error: aborting due to 22 previous errors
146143

0 commit comments

Comments
 (0)