@@ -5,159 +5,149 @@ use std::ops::Range;
5
5
use std:: str:: CharIndices ;
6
6
7
7
use pulldown_cmark:: { BrokenLink , Event , LinkType , Parser , Tag , TagEnd } ;
8
+ use rustc_hir:: HirId ;
8
9
use rustc_resolve:: rustdoc:: source_span_for_markdown_range;
9
10
10
11
use crate :: clean:: * ;
11
12
use crate :: core:: DocContext ;
12
13
use crate :: html:: markdown:: main_body_opts;
13
14
14
- pub ( crate ) fn visit_item ( cx : & DocContext < ' _ > , item : & Item ) {
15
+ pub ( crate ) fn visit_item ( cx : & DocContext < ' _ > , item : & Item , hir_id : HirId , dox : & str ) {
15
16
let tcx = cx. tcx ;
16
- let Some ( hir_id) = DocContext :: as_local_hir_id ( tcx, item. item_id )
17
- // If non-local, no need to check anything.
18
- else {
19
- return ;
20
- } ;
21
- let dox = item. doc_value ( ) ;
22
- if !dox. is_empty ( ) {
23
- let report_diag = |msg : String , range : & Range < usize > , is_open_tag : bool | {
24
- let sp = match source_span_for_markdown_range ( tcx, & dox, range, & item. attrs . doc_strings )
25
- {
26
- Some ( sp) => sp,
27
- None => item. attr_span ( tcx) ,
28
- } ;
29
- tcx. node_span_lint ( crate :: lint:: INVALID_HTML_TAGS , hir_id, sp, |lint| {
30
- use rustc_lint_defs:: Applicability ;
17
+ let report_diag = |msg : String , range : & Range < usize > , is_open_tag : bool | {
18
+ let sp = match source_span_for_markdown_range ( tcx, & dox, range, & item. attrs . doc_strings ) {
19
+ Some ( sp) => sp,
20
+ None => item. attr_span ( tcx) ,
21
+ } ;
22
+ tcx. node_span_lint ( crate :: lint:: INVALID_HTML_TAGS , hir_id, sp, |lint| {
23
+ use rustc_lint_defs:: Applicability ;
31
24
32
- lint. primary_message ( msg) ;
25
+ lint. primary_message ( msg) ;
33
26
34
- // If a tag looks like `<this>`, it might actually be a generic.
35
- // We don't try to detect stuff `<like, this>` because that's not valid HTML,
36
- // and we don't try to detect stuff `<like this>` because that's not valid Rust.
37
- let mut generics_end = range. end ;
38
- if let Some ( Some ( mut generics_start) ) = ( is_open_tag
39
- && dox[ ..generics_end] . ends_with ( '>' ) )
40
- . then ( || extract_path_backwards ( & dox, range. start ) )
27
+ // If a tag looks like `<this>`, it might actually be a generic.
28
+ // We don't try to detect stuff `<like, this>` because that's not valid HTML,
29
+ // and we don't try to detect stuff `<like this>` because that's not valid Rust.
30
+ let mut generics_end = range. end ;
31
+ if let Some ( Some ( mut generics_start) ) = ( is_open_tag
32
+ && dox[ ..generics_end] . ends_with ( '>' ) )
33
+ . then ( || extract_path_backwards ( & dox, range. start ) )
34
+ {
35
+ while generics_start != 0
36
+ && generics_end < dox. len ( )
37
+ && dox. as_bytes ( ) [ generics_start - 1 ] == b'<'
38
+ && dox. as_bytes ( ) [ generics_end] == b'>'
41
39
{
42
- while generics_start != 0
43
- && generics_end < dox. len ( )
44
- && dox. as_bytes ( ) [ generics_start - 1 ] == b'<'
45
- && dox. as_bytes ( ) [ generics_end] == b'>'
46
- {
47
- generics_end += 1 ;
48
- generics_start -= 1 ;
49
- if let Some ( new_start) = extract_path_backwards ( & dox, generics_start) {
50
- generics_start = new_start;
51
- }
52
- if let Some ( new_end) = extract_path_forward ( & dox, generics_end) {
53
- generics_end = new_end;
54
- }
40
+ generics_end += 1 ;
41
+ generics_start -= 1 ;
42
+ if let Some ( new_start) = extract_path_backwards ( & dox, generics_start) {
43
+ generics_start = new_start;
55
44
}
56
45
if let Some ( new_end) = extract_path_forward ( & dox, generics_end) {
57
46
generics_end = new_end;
58
47
}
59
- let generics_sp = match source_span_for_markdown_range (
60
- tcx,
61
- & dox,
62
- & ( generics_start..generics_end) ,
63
- & item. attrs . doc_strings ,
64
- ) {
65
- Some ( sp) => sp,
66
- None => item. attr_span ( tcx) ,
67
- } ;
68
- // Sometimes, we only extract part of a path. For example, consider this:
69
- //
70
- // <[u32] as IntoIter<u32>>::Item
71
- // ^^^^^ unclosed HTML tag `u32`
72
- //
73
- // We don't have any code for parsing fully-qualified trait paths.
74
- // In theory, we could add it, but doing it correctly would require
75
- // parsing the entire path grammar, which is problematic because of
76
- // overlap between the path grammar and Markdown.
77
- //
78
- // The example above shows that ambiguity. Is `[u32]` intended to be an
79
- // intra-doc link to the u32 primitive, or is it intended to be a slice?
80
- //
81
- // If the below conditional were removed, we would suggest this, which is
82
- // not what the user probably wants.
83
- //
84
- // <[u32] as `IntoIter<u32>`>::Item
85
- //
86
- // We know that the user actually wants to wrap the whole thing in a code
87
- // block, but the only reason we know that is because `u32` does not, in
88
- // fact, implement IntoIter. If the example looks like this:
89
- //
90
- // <[Vec<i32>] as IntoIter<i32>::Item
91
- //
92
- // The ideal fix would be significantly different.
93
- if ( generics_start > 0 && dox. as_bytes ( ) [ generics_start - 1 ] == b'<' )
94
- || ( generics_end < dox. len ( ) && dox. as_bytes ( ) [ generics_end] == b'>' )
95
- {
96
- return ;
97
- }
98
- // multipart form is chosen here because ``Vec<i32>`` would be confusing.
99
- lint. multipart_suggestion (
100
- "try marking as source code" ,
101
- vec ! [
102
- ( generics_sp. shrink_to_lo( ) , String :: from( "`" ) ) ,
103
- ( generics_sp. shrink_to_hi( ) , String :: from( "`" ) ) ,
104
- ] ,
105
- Applicability :: MaybeIncorrect ,
106
- ) ;
107
48
}
108
- } ) ;
109
- } ;
49
+ if let Some ( new_end) = extract_path_forward ( & dox, generics_end) {
50
+ generics_end = new_end;
51
+ }
52
+ let generics_sp = match source_span_for_markdown_range (
53
+ tcx,
54
+ & dox,
55
+ & ( generics_start..generics_end) ,
56
+ & item. attrs . doc_strings ,
57
+ ) {
58
+ Some ( sp) => sp,
59
+ None => item. attr_span ( tcx) ,
60
+ } ;
61
+ // Sometimes, we only extract part of a path. For example, consider this:
62
+ //
63
+ // <[u32] as IntoIter<u32>>::Item
64
+ // ^^^^^ unclosed HTML tag `u32`
65
+ //
66
+ // We don't have any code for parsing fully-qualified trait paths.
67
+ // In theory, we could add it, but doing it correctly would require
68
+ // parsing the entire path grammar, which is problematic because of
69
+ // overlap between the path grammar and Markdown.
70
+ //
71
+ // The example above shows that ambiguity. Is `[u32]` intended to be an
72
+ // intra-doc link to the u32 primitive, or is it intended to be a slice?
73
+ //
74
+ // If the below conditional were removed, we would suggest this, which is
75
+ // not what the user probably wants.
76
+ //
77
+ // <[u32] as `IntoIter<u32>`>::Item
78
+ //
79
+ // We know that the user actually wants to wrap the whole thing in a code
80
+ // block, but the only reason we know that is because `u32` does not, in
81
+ // fact, implement IntoIter. If the example looks like this:
82
+ //
83
+ // <[Vec<i32>] as IntoIter<i32>::Item
84
+ //
85
+ // The ideal fix would be significantly different.
86
+ if ( generics_start > 0 && dox. as_bytes ( ) [ generics_start - 1 ] == b'<' )
87
+ || ( generics_end < dox. len ( ) && dox. as_bytes ( ) [ generics_end] == b'>' )
88
+ {
89
+ return ;
90
+ }
91
+ // multipart form is chosen here because ``Vec<i32>`` would be confusing.
92
+ lint. multipart_suggestion (
93
+ "try marking as source code" ,
94
+ vec ! [
95
+ ( generics_sp. shrink_to_lo( ) , String :: from( "`" ) ) ,
96
+ ( generics_sp. shrink_to_hi( ) , String :: from( "`" ) ) ,
97
+ ] ,
98
+ Applicability :: MaybeIncorrect ,
99
+ ) ;
100
+ }
101
+ } ) ;
102
+ } ;
110
103
111
- let mut tags = Vec :: new ( ) ;
112
- let mut is_in_comment = None ;
113
- let mut in_code_block = false ;
104
+ let mut tags = Vec :: new ( ) ;
105
+ let mut is_in_comment = None ;
106
+ let mut in_code_block = false ;
114
107
115
- let link_names = item. link_names ( & cx. cache ) ;
108
+ let link_names = item. link_names ( & cx. cache ) ;
116
109
117
- let mut replacer = |broken_link : BrokenLink < ' _ > | {
118
- if let Some ( link) =
119
- link_names. iter ( ) . find ( |link| * link. original_text == * broken_link. reference )
120
- {
121
- Some ( ( link. href . as_str ( ) . into ( ) , link. new_text . to_string ( ) . into ( ) ) )
122
- } else if matches ! (
123
- & broken_link. link_type,
124
- LinkType :: Reference | LinkType :: ReferenceUnknown
125
- ) {
126
- // If the link is shaped [like][this], suppress any broken HTML in the [this] part.
127
- // The `broken_intra_doc_links` will report typos in there anyway.
128
- Some ( (
129
- broken_link. reference . to_string ( ) . into ( ) ,
130
- broken_link. reference . to_string ( ) . into ( ) ,
131
- ) )
132
- } else {
133
- None
134
- }
135
- } ;
110
+ let mut replacer = |broken_link : BrokenLink < ' _ > | {
111
+ if let Some ( link) =
112
+ link_names. iter ( ) . find ( |link| * link. original_text == * broken_link. reference )
113
+ {
114
+ Some ( ( link. href . as_str ( ) . into ( ) , link. new_text . to_string ( ) . into ( ) ) )
115
+ } else if matches ! ( & broken_link. link_type, LinkType :: Reference | LinkType :: ReferenceUnknown )
116
+ {
117
+ // If the link is shaped [like][this], suppress any broken HTML in the [this] part.
118
+ // The `broken_intra_doc_links` will report typos in there anyway.
119
+ Some ( (
120
+ broken_link. reference . to_string ( ) . into ( ) ,
121
+ broken_link. reference . to_string ( ) . into ( ) ,
122
+ ) )
123
+ } else {
124
+ None
125
+ }
126
+ } ;
136
127
137
- let p = Parser :: new_with_broken_link_callback ( & dox, main_body_opts ( ) , Some ( & mut replacer) )
138
- . into_offset_iter ( ) ;
128
+ let p = Parser :: new_with_broken_link_callback ( & dox, main_body_opts ( ) , Some ( & mut replacer) )
129
+ . into_offset_iter ( ) ;
139
130
140
- for ( event, range) in p {
141
- match event {
142
- Event :: Start ( Tag :: CodeBlock ( _) ) => in_code_block = true ,
143
- Event :: Html ( text) | Event :: InlineHtml ( text) if !in_code_block => {
144
- extract_tags ( & mut tags, & text, range, & mut is_in_comment, & report_diag)
145
- }
146
- Event :: End ( TagEnd :: CodeBlock ) => in_code_block = false ,
147
- _ => { }
131
+ for ( event, range) in p {
132
+ match event {
133
+ Event :: Start ( Tag :: CodeBlock ( _) ) => in_code_block = true ,
134
+ Event :: Html ( text) | Event :: InlineHtml ( text) if !in_code_block => {
135
+ extract_tags ( & mut tags, & text, range, & mut is_in_comment, & report_diag)
148
136
}
137
+ Event :: End ( TagEnd :: CodeBlock ) => in_code_block = false ,
138
+ _ => { }
149
139
}
140
+ }
150
141
151
- for ( tag, range) in tags. iter ( ) . filter ( |( t, _) | {
152
- let t = t. to_lowercase ( ) ;
153
- !ALLOWED_UNCLOSED . contains ( & t. as_str ( ) )
154
- } ) {
155
- report_diag ( format ! ( "unclosed HTML tag `{tag}`" ) , range, true ) ;
156
- }
142
+ for ( tag, range) in tags. iter ( ) . filter ( |( t, _) | {
143
+ let t = t. to_lowercase ( ) ;
144
+ !ALLOWED_UNCLOSED . contains ( & t. as_str ( ) )
145
+ } ) {
146
+ report_diag ( format ! ( "unclosed HTML tag `{tag}`" ) , range, true ) ;
147
+ }
157
148
158
- if let Some ( range) = is_in_comment {
159
- report_diag ( "Unclosed HTML comment" . to_string ( ) , & range, false ) ;
160
- }
149
+ if let Some ( range) = is_in_comment {
150
+ report_diag ( "Unclosed HTML comment" . to_string ( ) , & range, false ) ;
161
151
}
162
152
}
163
153
0 commit comments