@@ -4,37 +4,42 @@ use crate::core::DocContext;
4
4
use crate :: fold:: DocFolder ;
5
5
use crate :: html:: markdown:: opts;
6
6
use core:: ops:: Range ;
7
- use pulldown_cmark:: { Event , LinkType , Parser , Tag } ;
7
+ use pulldown_cmark:: { Event , Parser , Tag } ;
8
8
use regex:: Regex ;
9
9
use rustc_errors:: Applicability ;
10
+ use std:: lazy:: SyncLazy ;
11
+ use std:: mem;
10
12
11
- crate const CHECK_NON_AUTOLINKS : Pass = Pass {
12
- name : "check-non-autolinks " ,
13
- run : check_non_autolinks ,
14
- description : "detects URLs that could be linkified " ,
13
+ crate const CHECK_BARE_URLS : Pass = Pass {
14
+ name : "check-bare-urls " ,
15
+ run : check_bare_urls ,
16
+ description : "detects URLs that are not hyperlinks " ,
15
17
} ;
16
18
17
- const URL_REGEX : & str = concat ! (
18
- r"https?://" , // url scheme
19
- r"([-a-zA-Z0-9@:%._\+~#=]{2,256}\.)+" , // one or more subdomains
20
- r"[a-zA-Z]{2,63}" , // root domain
21
- r"\b([-a-zA-Z0-9@:%_\+.~#?&/=]*)" // optional query or url fragments
22
- ) ;
19
+ const URL_REGEX : SyncLazy < Regex > = SyncLazy :: new ( || {
20
+ Regex :: new ( concat ! (
21
+ r"https?://" , // url scheme
22
+ r"([-a-zA-Z0-9@:%._\+~#=]{2,256}\.)+" , // one or more subdomains
23
+ r"[a-zA-Z]{2,63}" , // root domain
24
+ r"\b([-a-zA-Z0-9@:%_\+.~#?&/=]*)" // optional query or url fragments
25
+ ) )
26
+ . expect ( "failed to build regex" )
27
+ } ) ;
23
28
24
- struct NonAutolinksLinter < ' a , ' tcx > {
29
+ struct BareUrlsLinter < ' a , ' tcx > {
25
30
cx : & ' a mut DocContext < ' tcx > ,
26
- regex : Regex ,
27
31
}
28
32
29
- impl < ' a , ' tcx > NonAutolinksLinter < ' a , ' tcx > {
33
+ impl < ' a , ' tcx > BareUrlsLinter < ' a , ' tcx > {
30
34
fn find_raw_urls (
31
35
& self ,
32
36
text : & str ,
33
37
range : Range < usize > ,
34
38
f : & impl Fn ( & DocContext < ' _ > , & str , & str , Range < usize > ) ,
35
39
) {
40
+ trace ! ( "looking for raw urls in {}" , text) ;
36
41
// For now, we only check "full" URLs (meaning, starting with "http://" or "https://").
37
- for match_ in self . regex . find_iter ( & text) {
42
+ for match_ in URL_REGEX . find_iter ( & text) {
38
43
let url = match_. as_str ( ) ;
39
44
let url_range = match_. range ( ) ;
40
45
f (
@@ -47,18 +52,11 @@ impl<'a, 'tcx> NonAutolinksLinter<'a, 'tcx> {
47
52
}
48
53
}
49
54
50
- crate fn check_non_autolinks ( krate : Crate , cx : & mut DocContext < ' _ > ) -> Crate {
51
- if !cx. tcx . sess . is_nightly_build ( ) {
52
- krate
53
- } else {
54
- let mut coll =
55
- NonAutolinksLinter { cx, regex : Regex :: new ( URL_REGEX ) . expect ( "failed to build regex" ) } ;
56
-
57
- coll. fold_crate ( krate)
58
- }
55
+ crate fn check_bare_urls ( krate : Crate , cx : & mut DocContext < ' _ > ) -> Crate {
56
+ BareUrlsLinter { cx } . fold_crate ( krate)
59
57
}
60
58
61
- impl < ' a , ' tcx > DocFolder for NonAutolinksLinter < ' a , ' tcx > {
59
+ impl < ' a , ' tcx > DocFolder for BareUrlsLinter < ' a , ' tcx > {
62
60
fn fold_item ( & mut self , item : Item ) -> Option < Item > {
63
61
let hir_id = match DocContext :: as_local_hir_id ( self . cx . tcx , item. def_id ) {
64
62
Some ( hir_id) => hir_id,
@@ -73,7 +71,7 @@ impl<'a, 'tcx> DocFolder for NonAutolinksLinter<'a, 'tcx> {
73
71
let sp = super :: source_span_for_markdown_range ( cx. tcx , & dox, & range, & item. attrs )
74
72
. or_else ( || span_of_attrs ( & item. attrs ) )
75
73
. unwrap_or ( item. span . inner ( ) ) ;
76
- cx. tcx . struct_span_lint_hir ( crate :: lint:: NON_AUTOLINKS , hir_id, sp, |lint| {
74
+ cx. tcx . struct_span_lint_hir ( crate :: lint:: BARE_URLS , hir_id, sp, |lint| {
77
75
lint. build ( msg)
78
76
. span_suggestion (
79
77
sp,
@@ -89,37 +87,16 @@ impl<'a, 'tcx> DocFolder for NonAutolinksLinter<'a, 'tcx> {
89
87
90
88
while let Some ( ( event, range) ) = p. next ( ) {
91
89
match event {
92
- Event :: Start ( Tag :: Link ( kind, _, _) ) => {
93
- let ignore = matches ! ( kind, LinkType :: Autolink | LinkType :: Email ) ;
94
- let mut title = String :: new ( ) ;
95
-
96
- while let Some ( ( event, range) ) = p. next ( ) {
97
- match event {
98
- Event :: End ( Tag :: Link ( _, url, _) ) => {
99
- // NOTE: links cannot be nested, so we don't need to
100
- // check `kind`
101
- if url. as_ref ( ) == title && !ignore && self . regex . is_match ( & url)
102
- {
103
- report_diag (
104
- self . cx ,
105
- "unneeded long form for URL" ,
106
- & url,
107
- range,
108
- ) ;
109
- }
110
- break ;
111
- }
112
- Event :: Text ( s) if !ignore => title. push_str ( & s) ,
113
- _ => { }
114
- }
115
- }
116
- }
117
90
Event :: Text ( s) => self . find_raw_urls ( & s, range, & report_diag) ,
118
- Event :: Start ( Tag :: CodeBlock ( _ ) ) => {
119
- // We don't want to check the text inside the code blocks.
91
+ // We don't want to check the text inside code blocks or links.
92
+ Event :: Start ( tag @ ( Tag :: CodeBlock ( _ ) | Tag :: Link ( .. ) ) ) => {
120
93
while let Some ( ( event, _) ) = p. next ( ) {
121
94
match event {
122
- Event :: End ( Tag :: CodeBlock ( _) ) => break ,
95
+ Event :: End ( end)
96
+ if mem:: discriminant ( & end) == mem:: discriminant ( & tag) =>
97
+ {
98
+ break ;
99
+ }
123
100
_ => { }
124
101
}
125
102
}
0 commit comments