@@ -17,7 +17,10 @@ use crate::core::DocContext;
17
17
use crate :: html:: markdown:: main_body_opts;
18
18
19
19
pub ( 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 > | {
20
+ let report_diag = |cx : & DocContext < ' _ > ,
21
+ msg : & ' static str ,
22
+ range : Range < usize > ,
23
+ without_brackets : Option < & str > | {
21
24
let maybe_sp = source_span_for_markdown_range ( cx. tcx , dox, & range, & item. attrs . doc_strings )
22
25
. map ( |( sp, _) | sp) ;
23
26
let sp = maybe_sp. unwrap_or_else ( || item. attr_span ( cx. tcx ) ) ;
@@ -27,14 +30,22 @@ pub(super) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId, dox: &
27
30
// The fallback of using the attribute span is suitable for
28
31
// highlighting where the error is, but not for placing the < and >
29
32
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
- ) ;
33
+ if let Some ( without_brackets) = without_brackets {
34
+ lint. multipart_suggestion (
35
+ "use an automatic link instead" ,
36
+ vec ! [ ( sp, format!( "<{without_brackets}>" ) ) ] ,
37
+ Applicability :: MachineApplicable ,
38
+ ) ;
39
+ } else {
40
+ lint. multipart_suggestion (
41
+ "use an automatic link instead" ,
42
+ vec ! [
43
+ ( sp. shrink_to_lo( ) , "<" . to_string( ) ) ,
44
+ ( sp. shrink_to_hi( ) , ">" . to_string( ) ) ,
45
+ ] ,
46
+ Applicability :: MachineApplicable ,
47
+ ) ;
48
+ }
38
49
}
39
50
} ) ;
40
51
} ;
@@ -43,7 +54,7 @@ pub(super) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId, dox: &
43
54
44
55
while let Some ( ( event, range) ) = p. next ( ) {
45
56
match event {
46
- Event :: Text ( s) => find_raw_urls ( cx, & s, range, & report_diag) ,
57
+ Event :: Text ( s) => find_raw_urls ( cx, dox , & s, range, & report_diag) ,
47
58
// We don't want to check the text inside code blocks or links.
48
59
Event :: Start ( tag @ ( Tag :: CodeBlock ( _) | Tag :: Link { .. } ) ) => {
49
60
for ( event, _) in p. by_ref ( ) {
@@ -67,25 +78,35 @@ static URL_REGEX: LazyLock<Regex> = LazyLock::new(|| {
67
78
r"https?://" , // url scheme
68
79
r"([-a-zA-Z0-9@:%._\+~#=]{2,256}\.)+" , // one or more subdomains
69
80
r"[a-zA-Z]{2,63}" , // root domain
70
- r"\b([-a-zA-Z0-9@:%_\+.~#?&/=]*)" // optional query or url fragments
81
+ r"\b([-a-zA-Z0-9@:%_\+.~#?&/=]*)" , // optional query or url fragments
71
82
) )
72
83
. expect ( "failed to build regex" )
73
84
} ) ;
74
85
75
86
fn find_raw_urls (
76
87
cx : & DocContext < ' _ > ,
88
+ dox : & str ,
77
89
text : & str ,
78
90
range : Range < usize > ,
79
- f : & impl Fn ( & DocContext < ' _ > , & ' static str , Range < usize > ) ,
91
+ f : & impl Fn ( & DocContext < ' _ > , & ' static str , Range < usize > , Option < & str > ) ,
80
92
) {
81
93
trace ! ( "looking for raw urls in {text}" ) ;
82
94
// For now, we only check "full" URLs (meaning, starting with "http://" or "https://").
83
95
for match_ in URL_REGEX . find_iter ( text) {
84
- let url_range = match_. range ( ) ;
85
- f (
86
- cx,
87
- "this URL is not a hyperlink" ,
88
- Range { start : range. start + url_range. start , end : range. start + url_range. end } ,
89
- ) ;
96
+ let mut url_range = match_. range ( ) ;
97
+ url_range. start += range. start ;
98
+ url_range. end += range. start ;
99
+ let mut without_brackets = None ;
100
+ // If the link is contained inside `[]`, then we need to replace the brackets and
101
+ // not just add `<>`.
102
+ if dox[ ..url_range. start ] . ends_with ( '[' )
103
+ && url_range. end <= dox. len ( )
104
+ && dox[ url_range. end ..] . starts_with ( ']' )
105
+ {
106
+ url_range. start -= 1 ;
107
+ url_range. end += 1 ;
108
+ without_brackets = Some ( match_. as_str ( ) ) ;
109
+ }
110
+ f ( cx, "this URL is not a hyperlink" , url_range, without_brackets) ;
90
111
}
91
112
}
0 commit comments