@@ -6,17 +6,40 @@ use rustc_span::{BytePos, Span};
66use super :: DOC_BROKEN_LINK ;
77
88pub fn check ( cx : & LateContext < ' _ > , attrs : & [ Attribute ] ) {
9- for span in BrokenLinkLoader :: collect_spans_broken_link ( attrs) {
10- span_lint ( cx, DOC_BROKEN_LINK , span, "possible broken doc link" ) ;
9+ for broken_link in BrokenLinkLoader :: collect_spans_broken_link ( attrs) {
10+ let reason_msg = match broken_link. reason {
11+ BrokenLinkReason :: MultipleLines => "broken across multiple lines" ,
12+ BrokenLinkReason :: MissingCloseParenthesis => "missing close parenthesis" ,
13+ BrokenLinkReason :: WhiteSpace => "whitespace within url" ,
14+ } ;
15+
16+ span_lint (
17+ cx,
18+ DOC_BROKEN_LINK ,
19+ broken_link. span ,
20+ format ! ( "possible broken doc link: {reason_msg}" ) ,
21+ ) ;
1122 }
1223}
1324
25+ /// The reason why a link is considered broken.
26+ enum BrokenLinkReason {
27+ MultipleLines ,
28+ MissingCloseParenthesis ,
29+ WhiteSpace ,
30+ }
31+
32+ /// Broken link data.
33+ struct BrokenLink {
34+ reason : BrokenLinkReason ,
35+ span : Span ,
36+ }
37+
1438/// Scan AST attributes looking up in doc comments for broken links
1539/// which rustdoc won't be able to properly create link tags later.
16- #[ derive( Debug ) ]
1740struct BrokenLinkLoader {
1841 /// List of spans for detected broken links.
19- spans_broken_link : Vec < Span > ,
42+ broken_links : Vec < BrokenLink > ,
2043
2144 /// Mark it has detected a link and it is processing whether
2245 /// or not it is broken.
@@ -51,9 +74,9 @@ struct BrokenLinkLoader {
5174
5275impl BrokenLinkLoader {
5376 /// Return spans of broken links.
54- fn collect_spans_broken_link ( attrs : & [ Attribute ] ) -> Vec < Span > {
77+ fn collect_spans_broken_link ( attrs : & [ Attribute ] ) -> Vec < BrokenLink > {
5578 let mut loader = BrokenLinkLoader {
56- spans_broken_link : vec ! [ ] ,
79+ broken_links : vec ! [ ] ,
5780 active : false ,
5881 active_pos_start : 0 ,
5982 active_span : None ,
@@ -65,7 +88,7 @@ impl BrokenLinkLoader {
6588 start : 0_u32 ,
6689 } ;
6790 loader. scan_attrs ( attrs) ;
68- loader. spans_broken_link
91+ loader. broken_links
6992 }
7093
7194 fn scan_attrs ( & mut self , attrs : & [ Attribute ] ) {
@@ -79,15 +102,15 @@ impl BrokenLinkLoader {
79102 if idx > 0 && self . active && self . processing_link_url {
80103 let prev_attr = & attrs[ idx - 1 ] ;
81104 let prev_end_line = prev_attr. span . hi ( ) . 0 ;
82- self . record_broken_link ( prev_end_line) ;
105+ self . record_broken_link ( prev_end_line, BrokenLinkReason :: MissingCloseParenthesis ) ;
83106 }
84107 self . reset_lookup ( ) ;
85108 }
86109 }
87110
88111 if self . active && self . processing_link_url {
89112 let last_end_line = attrs. last ( ) . unwrap ( ) . span . hi ( ) . 0 ;
90- self . record_broken_link ( last_end_line) ;
113+ self . record_broken_link ( last_end_line, BrokenLinkReason :: MissingCloseParenthesis ) ;
91114 self . reset_lookup ( ) ;
92115 }
93116 }
@@ -139,7 +162,7 @@ impl BrokenLinkLoader {
139162 if self . url_multiple_lines {
140163 // +3 skips the opening delimiter and +1 to include the closing parethesis
141164 let pos_end = attr_span. lo ( ) . 0 + u32:: try_from ( pos) . unwrap ( ) + 4 ;
142- self . record_broken_link ( pos_end) ;
165+ self . record_broken_link ( pos_end, BrokenLinkReason :: MultipleLines ) ;
143166 self . reset_lookup ( ) ;
144167 }
145168 self . reset_lookup ( ) ;
@@ -148,7 +171,7 @@ impl BrokenLinkLoader {
148171
149172 if self . reading_link_url && c. is_whitespace ( ) {
150173 let pos_end = u32:: try_from ( pos) . unwrap ( ) ;
151- self . record_broken_link ( pos_end) ;
174+ self . record_broken_link ( pos_end, BrokenLinkReason :: WhiteSpace ) ;
152175 self . reset_lookup ( ) ;
153176 continue ;
154177 }
@@ -173,14 +196,14 @@ impl BrokenLinkLoader {
173196 self . url_multiple_lines = false ;
174197 }
175198
176- fn record_broken_link ( & mut self , pos_end : u32 ) {
199+ fn record_broken_link ( & mut self , pos_end : u32 , reason : BrokenLinkReason ) {
177200 if let Some ( attr_span) = self . active_span {
178201 let start = BytePos ( self . active_pos_start ) ;
179202 let end = BytePos ( pos_end) ;
180203
181- let com_span = Span :: new ( start, end, attr_span. ctxt ( ) , attr_span. parent ( ) ) ;
204+ let span = Span :: new ( start, end, attr_span. ctxt ( ) , attr_span. parent ( ) ) ;
182205
183- self . spans_broken_link . push ( com_span ) ;
206+ self . broken_links . push ( BrokenLink { reason , span } ) ;
184207 }
185208 }
186209}
0 commit comments