@@ -17,33 +17,43 @@ use crate::core::DocContext;
1717use crate :: html:: markdown:: main_body_opts;
1818
1919pub ( super ) fn visit_item ( cx : & DocContext < ' _ > , item : & Item , hir_id : HirId , dox : & str ) {
20- let report_diag = |cx : & DocContext < ' _ > , msg : & ' static str , range : Range < usize > | {
21- let maybe_sp = source_span_for_markdown_range ( cx. tcx , dox, & range, & item. attrs . doc_strings )
22- . map ( |( sp, _) | sp) ;
23- let sp = maybe_sp. unwrap_or_else ( || item. attr_span ( cx. tcx ) ) ;
24- cx. tcx . node_span_lint ( crate :: lint:: BARE_URLS , hir_id, sp, |lint| {
25- lint. primary_message ( msg)
26- . note ( "bare URLs are not automatically turned into clickable links" ) ;
27- // The fallback of using the attribute span is suitable for
28- // highlighting where the error is, but not for placing the < and >
29- if let Some ( sp) = maybe_sp {
30- lint. multipart_suggestion (
31- "use an automatic link instead" ,
32- vec ! [
33- ( sp. shrink_to_lo( ) , "<" . to_string( ) ) ,
34- ( sp. shrink_to_hi( ) , ">" . to_string( ) ) ,
35- ] ,
36- Applicability :: MachineApplicable ,
37- ) ;
38- }
39- } ) ;
40- } ;
20+ let report_diag =
21+ |cx : & DocContext < ' _ > , msg : & ' static str , range : Range < usize > , no_brackets : Option < & str > | {
22+ let maybe_sp =
23+ source_span_for_markdown_range ( cx. tcx , dox, & range, & item. attrs . doc_strings )
24+ . map ( |( sp, _) | sp) ;
25+ let sp = maybe_sp. unwrap_or_else ( || item. attr_span ( cx. tcx ) ) ;
26+ cx. tcx . node_span_lint ( crate :: lint:: BARE_URLS , hir_id, sp, |lint| {
27+ lint. primary_message ( msg)
28+ . note ( "bare URLs are not automatically turned into clickable links" ) ;
29+ // The fallback of using the attribute span is suitable for
30+ // highlighting where the error is, but not for placing the < and >
31+ if let Some ( sp) = maybe_sp {
32+ if let Some ( no_brackets) = no_brackets {
33+ lint. multipart_suggestion (
34+ "use an automatic link instead" ,
35+ vec ! [ ( sp, format!( "<{no_brackets}>" ) ) ] ,
36+ Applicability :: MachineApplicable ,
37+ ) ;
38+ } else {
39+ lint. multipart_suggestion (
40+ "use an automatic link instead" ,
41+ vec ! [
42+ ( sp. shrink_to_lo( ) , "<" . to_string( ) ) ,
43+ ( sp. shrink_to_hi( ) , ">" . to_string( ) ) ,
44+ ] ,
45+ Applicability :: MachineApplicable ,
46+ ) ;
47+ }
48+ }
49+ } ) ;
50+ } ;
4151
4252 let mut p = Parser :: new_ext ( dox, main_body_opts ( ) ) . into_offset_iter ( ) ;
4353
4454 while let Some ( ( event, range) ) = p. next ( ) {
4555 match event {
46- Event :: Text ( s) => find_raw_urls ( cx, & s, range, & report_diag) ,
56+ Event :: Text ( s) => find_raw_urls ( cx, dox , & s, range, & report_diag) ,
4757 // We don't want to check the text inside code blocks or links.
4858 Event :: Start ( tag @ ( Tag :: CodeBlock ( _) | Tag :: Link { .. } ) ) => {
4959 for ( event, _) in p. by_ref ( ) {
@@ -67,25 +77,41 @@ static URL_REGEX: LazyLock<Regex> = LazyLock::new(|| {
6777 r"https?://" , // url scheme
6878 r"([-a-zA-Z0-9@:%._\+~#=]{2,256}\.)+" , // one or more subdomains
6979 r"[a-zA-Z]{2,63}" , // root domain
70- r"\b([-a-zA-Z0-9@:%_\+.~#?&/=]*)" // optional query or url fragments
80+ r"\b([-a-zA-Z0-9@:%_\+.~#?&/=]*)" , // optional query or url fragments
7181 ) )
7282 . expect ( "failed to build regex" )
7383} ) ;
7484
7585fn find_raw_urls (
7686 cx : & DocContext < ' _ > ,
87+ whole_text : & str ,
7788 text : & str ,
7889 range : Range < usize > ,
79- f : & impl Fn ( & DocContext < ' _ > , & ' static str , Range < usize > ) ,
90+ f : & impl Fn ( & DocContext < ' _ > , & ' static str , Range < usize > , Option < & str > ) ,
8091) {
8192 trace ! ( "looking for raw urls in {text}" ) ;
8293 // For now, we only check "full" URLs (meaning, starting with "http://" or "https://").
8394 for match_ in URL_REGEX . find_iter ( text) {
8495 let url_range = match_. range ( ) ;
96+ let mut no_brackets = None ;
97+ let mut extra_range = 0 ;
98+ // If the whole text is contained inside `[]`, then we need to replace the brackets and
99+ // not just add `<>`.
100+ if whole_text[ ..range. start + url_range. start ] . ends_with ( '[' )
101+ && range. start + url_range. end <= whole_text. len ( )
102+ && whole_text[ range. start + url_range. end ..] . starts_with ( ']' )
103+ {
104+ extra_range = 1 ;
105+ no_brackets = Some ( match_. as_str ( ) ) ;
106+ }
85107 f (
86108 cx,
87109 "this URL is not a hyperlink" ,
88- Range { start : range. start + url_range. start , end : range. start + url_range. end } ,
110+ Range {
111+ start : range. start + url_range. start - extra_range,
112+ end : range. start + url_range. end + extra_range,
113+ } ,
114+ no_brackets,
89115 ) ;
90116 }
91117}
0 commit comments