Skip to content

Commit 9aed9c1

Browse files
authored
Rollup merge of #81764 - jyn514:lint-links, r=GuillaumeGomez
Stabilize `rustdoc::bare_urls` lint Closes #77501. Closes #83598.
2 parents 689978c + ff245da commit 9aed9c1

12 files changed

+129
-135
lines changed

compiler/rustc_lint/src/levels.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -464,15 +464,16 @@ impl<'s> LintLevelsBuilder<'s> {
464464
// we don't warn about the name change.
465465
if let CheckLintNameResult::Warning(_, Some(new_name)) = lint_result {
466466
// Ignore any errors or warnings that happen because the new name is inaccurate
467-
if let CheckLintNameResult::Ok(ids) =
468-
store.check_lint_name(&new_name, tool_name)
469-
{
467+
// NOTE: `new_name` already includes the tool name, so we don't have to add it again.
468+
if let CheckLintNameResult::Ok(ids) = store.check_lint_name(&new_name, None) {
470469
let src =
471470
LintLevelSource::Node(Symbol::intern(&new_name), li.span(), reason);
472471
for &id in ids {
473472
self.check_gated_lint(id, attr.span);
474473
self.insert_spec(&mut specs, id, (level, src));
475474
}
475+
} else {
476+
panic!("renamed lint does not exist: {}", new_name);
476477
}
477478
}
478479
}

library/core/src/lib.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,8 @@ pub mod primitive;
298298
unused_imports,
299299
unsafe_op_in_unsafe_fn
300300
)]
301-
#[allow(rustdoc::non_autolinks)]
301+
#[cfg_attr(bootstrap, allow(rustdoc::non_autolinks))]
302+
#[cfg_attr(not(bootstrap), allow(rustdoc::bare_urls))]
302303
// FIXME: This annotation should be moved into rust-lang/stdarch after clashing_extern_declarations is
303304
// merged. It currently cannot because bootstrap fails as the lint hasn't been defined yet.
304305
#[allow(clashing_extern_declarations)]

src/doc/rustdoc/src/lints.md

+8-15
Original file line numberDiff line numberDiff line change
@@ -294,40 +294,33 @@ warning: unclosed HTML tag `h1`
294294
warning: 2 warnings emitted
295295
```
296296

297-
## non_autolinks
297+
## bare_urls
298298

299-
This lint is **nightly-only** and **warns by default**. It detects links which
300-
could use the "automatic" link syntax. For example:
299+
This lint is **warn-by-default**. It detects URLs which are not links.
300+
For example:
301301

302302
```rust
303303
/// http://example.org
304-
/// [http://example.com](http://example.com)
305304
/// [http://example.net]
306-
///
307-
/// [http://example.com]: http://example.com
308305
pub fn foo() {}
309306
```
310307

311308
Which will give:
312309

313310
```text
314311
warning: this URL is not a hyperlink
315-
--> foo.rs:1:5
312+
--> links.rs:1:5
316313
|
317314
1 | /// http://example.org
318315
| ^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `<http://example.org>`
319316
|
320-
= note: `#[warn(rustdoc::non_autolinks)]` on by default
321-
322-
warning: unneeded long form for URL
323-
--> foo.rs:2:5
324-
|
325-
2 | /// [http://example.com](http://example.com)
326-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `<http://example.com>`
317+
= note: `#[warn(rustdoc::bare_urls)]` on by default
327318
328319
warning: this URL is not a hyperlink
329-
--> foo.rs:3:6
320+
--> links.rs:3:6
330321
|
331322
3 | /// [http://example.net]
332323
| ^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `<http://example.net>`
324+
325+
warning: 2 warnings emitted
333326
```

src/librustdoc/lint.rs

+8-7
Original file line numberDiff line numberDiff line change
@@ -148,14 +148,13 @@ declare_rustdoc_lint! {
148148
}
149149

150150
declare_rustdoc_lint! {
151-
/// The `non_autolinks` lint detects when a URL could be written using
152-
/// only angle brackets. This is a `rustdoc` only lint, see the
153-
/// documentation in the [rustdoc book].
151+
/// The `bare_urls` lint detects when a URL is not a hyperlink.
152+
/// This is a `rustdoc` only lint, see the documentation in the [rustdoc book].
154153
///
155-
/// [rustdoc book]: ../../../rustdoc/lints.html#non_autolinks
156-
NON_AUTOLINKS,
154+
/// [rustdoc book]: ../../../rustdoc/lints.html#bare_urls
155+
BARE_URLS,
157156
Warn,
158-
"detects URLs that could be written using only angle brackets"
157+
"detects URLs that are not hyperlinks"
159158
}
160159

161160
crate static RUSTDOC_LINTS: Lazy<Vec<&'static Lint>> = Lazy::new(|| {
@@ -166,7 +165,7 @@ crate static RUSTDOC_LINTS: Lazy<Vec<&'static Lint>> = Lazy::new(|| {
166165
PRIVATE_DOC_TESTS,
167166
INVALID_CODEBLOCK_ATTRIBUTES,
168167
INVALID_HTML_TAGS,
169-
NON_AUTOLINKS,
168+
BARE_URLS,
170169
MISSING_CRATE_LEVEL_DOCS,
171170
]
172171
});
@@ -185,4 +184,6 @@ crate fn register_lints(_sess: &Session, lint_store: &mut LintStore) {
185184
}
186185
lint_store
187186
.register_renamed("intra_doc_link_resolution_failure", "rustdoc::broken_intra_doc_links");
187+
lint_store.register_renamed("non_autolinks", "rustdoc::bare_urls");
188+
lint_store.register_renamed("rustdoc::non_autolinks", "rustdoc::bare_urls");
188189
}

src/librustdoc/passes/non_autolinks.rs src/librustdoc/passes/bare_urls.rs

+31-54
Original file line numberDiff line numberDiff line change
@@ -4,37 +4,42 @@ use crate::core::DocContext;
44
use crate::fold::DocFolder;
55
use crate::html::markdown::opts;
66
use core::ops::Range;
7-
use pulldown_cmark::{Event, LinkType, Parser, Tag};
7+
use pulldown_cmark::{Event, Parser, Tag};
88
use regex::Regex;
99
use 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
}

src/librustdoc/passes/mod.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ use crate::core::DocContext;
1212
mod stripper;
1313
crate use stripper::*;
1414

15-
mod non_autolinks;
16-
crate use self::non_autolinks::CHECK_NON_AUTOLINKS;
15+
mod bare_urls;
16+
crate use self::bare_urls::CHECK_BARE_URLS;
1717

1818
mod strip_hidden;
1919
crate use self::strip_hidden::STRIP_HIDDEN;
@@ -90,7 +90,7 @@ crate const PASSES: &[Pass] = &[
9090
COLLECT_TRAIT_IMPLS,
9191
CALCULATE_DOC_COVERAGE,
9292
CHECK_INVALID_HTML_TAGS,
93-
CHECK_NON_AUTOLINKS,
93+
CHECK_BARE_URLS,
9494
];
9595

9696
/// The list of passes run by default.
@@ -105,7 +105,7 @@ crate const DEFAULT_PASSES: &[ConditionalPass] = &[
105105
ConditionalPass::always(CHECK_CODE_BLOCK_SYNTAX),
106106
ConditionalPass::always(CHECK_INVALID_HTML_TAGS),
107107
ConditionalPass::always(PROPAGATE_DOC_CFG),
108-
ConditionalPass::always(CHECK_NON_AUTOLINKS),
108+
ConditionalPass::always(CHECK_BARE_URLS),
109109
];
110110

111111
/// The list of default passes run when `--doc-coverage` is passed to rustdoc.

src/test/rustdoc-ui/renamed-lint-still-applies.rs

+5
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,8 @@
44
// stable channel.
55
//! [x]
66
//~^ ERROR unresolved link
7+
8+
#![deny(rustdoc::non_autolinks)]
9+
//~^ WARNING renamed to `rustdoc::bare_urls`
10+
//! http://example.com
11+
//~^ ERROR not a hyperlink

src/test/rustdoc-ui/renamed-lint-still-applies.stderr

+21-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
warning: lint `rustdoc::non_autolinks` has been renamed to `rustdoc::bare_urls`
2+
--> $DIR/renamed-lint-still-applies.rs:8:9
3+
|
4+
LL | #![deny(rustdoc::non_autolinks)]
5+
| ^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `rustdoc::bare_urls`
6+
|
7+
= note: `#[warn(renamed_and_removed_lints)]` on by default
8+
19
error: unresolved link to `x`
210
--> $DIR/renamed-lint-still-applies.rs:5:6
311
|
@@ -12,5 +20,17 @@ LL | #![deny(broken_intra_doc_links)]
1220
= note: `#[deny(rustdoc::broken_intra_doc_links)]` implied by `#[deny(broken_intra_doc_links)]`
1321
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
1422

15-
error: aborting due to previous error
23+
error: this URL is not a hyperlink
24+
--> $DIR/renamed-lint-still-applies.rs:10:5
25+
|
26+
LL | //! http://example.com
27+
| ^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `<http://example.com>`
28+
|
29+
note: the lint level is defined here
30+
--> $DIR/renamed-lint-still-applies.rs:8:9
31+
|
32+
LL | #![deny(rustdoc::non_autolinks)]
33+
| ^^^^^^^^^^^^^^^^^^^^^^
34+
35+
error: aborting due to 2 previous errors; 1 warning emitted
1636

src/test/rustdoc-ui/unknown-renamed-lints.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,12 @@
88
//~^ ERROR unknown lint: `rustdoc::x`
99
#![deny(intra_doc_link_resolution_failure)]
1010
//~^ ERROR renamed to `rustdoc::broken_intra_doc_links`
11-
1211
#![deny(non_autolinks)]
12+
//~^ ERROR renamed to `rustdoc::bare_urls`
13+
#![deny(rustdoc::non_autolinks)]
14+
//~^ ERROR renamed to `rustdoc::bare_urls`
15+
16+
#![deny(private_doc_tests)]
1317
// FIXME: the old names for rustdoc lints should warn by default once `rustdoc::` makes it to the
1418
// stable channel.
1519

src/test/rustdoc-ui/unknown-renamed-lints.stderr

+15-3
Original file line numberDiff line numberDiff line change
@@ -28,19 +28,31 @@ note: the lint level is defined here
2828
LL | #![deny(renamed_and_removed_lints)]
2929
| ^^^^^^^^^^^^^^^^^^^^^^^^^
3030

31+
error: lint `non_autolinks` has been renamed to `rustdoc::bare_urls`
32+
--> $DIR/unknown-renamed-lints.rs:11:9
33+
|
34+
LL | #![deny(non_autolinks)]
35+
| ^^^^^^^^^^^^^ help: use the new name: `rustdoc::bare_urls`
36+
37+
error: lint `rustdoc::non_autolinks` has been renamed to `rustdoc::bare_urls`
38+
--> $DIR/unknown-renamed-lints.rs:13:9
39+
|
40+
LL | #![deny(rustdoc::non_autolinks)]
41+
| ^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `rustdoc::bare_urls`
42+
3143
error: lint `rustdoc` has been removed: use `rustdoc::all` instead
32-
--> $DIR/unknown-renamed-lints.rs:16:9
44+
--> $DIR/unknown-renamed-lints.rs:20:9
3345
|
3446
LL | #![deny(rustdoc)]
3547
| ^^^^^^^
3648

3749
error: unknown lint: `rustdoc::intra_doc_link_resolution_failure`
38-
--> $DIR/unknown-renamed-lints.rs:20:9
50+
--> $DIR/unknown-renamed-lints.rs:24:9
3951
|
4052
LL | #![deny(rustdoc::intra_doc_link_resolution_failure)]
4153
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4254

4355
error: Compilation failed, aborting rustdoc
4456

45-
error: aborting due to 6 previous errors
57+
error: aborting due to 8 previous errors
4658

src/test/rustdoc-ui/url-improvements.rs

+4-12
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,4 @@
1-
#![deny(rustdoc::non_autolinks)]
2-
3-
/// [http://aa.com](http://aa.com)
4-
//~^ ERROR unneeded long form for URL
5-
/// [http://bb.com]
6-
//~^ ERROR unneeded long form for URL
7-
///
8-
/// [http://bb.com]: http://bb.com
9-
///
10-
/// [http://c.com][http://c.com]
11-
pub fn a() {}
1+
#![deny(rustdoc::bare_urls)]
122

133
/// https://somewhere.com
144
//~^ ERROR this URL is not a hyperlink
@@ -54,12 +44,14 @@ pub fn c() {}
5444
///
5545
/// ```
5646
/// This link should not be linted: http://example.com
47+
///
48+
/// Nor this one: <http://example.com> or this one: [x](http://example.com)
5749
/// ```
5850
///
5951
/// [should_not.lint](should_not.lint)
6052
pub fn everything_is_fine_here() {}
6153

62-
#[allow(rustdoc::non_autolinks)]
54+
#[allow(rustdoc::bare_urls)]
6355
pub mod foo {
6456
/// https://somewhere.com/a?hello=12&bye=11#xyz
6557
pub fn bar() {}

0 commit comments

Comments
 (0)