@@ -4,37 +4,42 @@ use crate::core::DocContext;
44use crate :: fold:: DocFolder ;
55use crate :: html:: markdown:: opts;
66use core:: ops:: Range ;
7- use pulldown_cmark:: { Event , LinkType , Parser , Tag } ;
7+ use pulldown_cmark:: { Event , Parser , Tag } ;
88use regex:: Regex ;
99use rustc_errors:: Applicability ;
10+ use std:: lazy:: SyncLazy ;
11+ use std:: mem;
1012
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 " ,
1517} ;
1618
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+ } ) ;
2328
24- struct NonAutolinksLinter < ' a , ' tcx > {
29+ struct BareUrlsLinter < ' a , ' tcx > {
2530 cx : & ' a mut DocContext < ' tcx > ,
26- regex : Regex ,
2731}
2832
29- impl < ' a , ' tcx > NonAutolinksLinter < ' a , ' tcx > {
33+ impl < ' a , ' tcx > BareUrlsLinter < ' a , ' tcx > {
3034 fn find_raw_urls (
3135 & self ,
3236 text : & str ,
3337 range : Range < usize > ,
3438 f : & impl Fn ( & DocContext < ' _ > , & str , & str , Range < usize > ) ,
3539 ) {
40+ trace ! ( "looking for raw urls in {}" , text) ;
3641 // 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) {
3843 let url = match_. as_str ( ) ;
3944 let url_range = match_. range ( ) ;
4045 f (
@@ -47,18 +52,11 @@ impl<'a, 'tcx> NonAutolinksLinter<'a, 'tcx> {
4752 }
4853}
4954
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)
5957}
6058
61- impl < ' a , ' tcx > DocFolder for NonAutolinksLinter < ' a , ' tcx > {
59+ impl < ' a , ' tcx > DocFolder for BareUrlsLinter < ' a , ' tcx > {
6260 fn fold_item ( & mut self , item : Item ) -> Option < Item > {
6361 let hir_id = match DocContext :: as_local_hir_id ( self . cx . tcx , item. def_id ) {
6462 Some ( hir_id) => hir_id,
@@ -73,7 +71,7 @@ impl<'a, 'tcx> DocFolder for NonAutolinksLinter<'a, 'tcx> {
7371 let sp = super :: source_span_for_markdown_range ( cx. tcx , & dox, & range, & item. attrs )
7472 . or_else ( || span_of_attrs ( & item. attrs ) )
7573 . 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| {
7775 lint. build ( msg)
7876 . span_suggestion (
7977 sp,
@@ -89,37 +87,16 @@ impl<'a, 'tcx> DocFolder for NonAutolinksLinter<'a, 'tcx> {
8987
9088 while let Some ( ( event, range) ) = p. next ( ) {
9189 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- }
11790 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 ( .. ) ) ) => {
12093 while let Some ( ( event, _) ) = p. next ( ) {
12194 match event {
122- Event :: End ( Tag :: CodeBlock ( _) ) => break ,
95+ Event :: End ( end)
96+ if mem:: discriminant ( & end) == mem:: discriminant ( & tag) =>
97+ {
98+ break ;
99+ }
123100 _ => { }
124101 }
125102 }
0 commit comments