From 54bf8a681bededa6c7e09f4c1da3cb68efb885a3 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Sun, 11 Oct 2020 14:56:12 -0400 Subject: [PATCH 1/8] Don't link to nightly primitives on stable channel I am not sure how to test this. --- src/librustdoc/clean/types.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 1e07f8e2eac24..f81c6c3df76af 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -14,6 +14,7 @@ use rustc_ast::util::comments::beautify_doc_string; use rustc_ast::{self as ast, AttrStyle}; use rustc_ast::{FloatTy, IntTy, UintTy}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_feature::UnstableFeatures; use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; @@ -698,9 +699,13 @@ impl Attributes { "../".repeat(depth) } Some(&(_, _, ExternalLocation::Remote(ref s))) => s.to_string(), - Some(&(_, _, ExternalLocation::Unknown)) | None => { - String::from("https://doc.rust-lang.org/nightly") - } + Some(&(_, _, ExternalLocation::Unknown)) | None => String::from( + if UnstableFeatures::from_environment().is_nightly_build() { + "https://doc.rust-lang.org/nightly" + } else { + "https://doc.rust-lang.org" + }, + ), }; // This is a primitive so the url is done "by hand". let tail = fragment.find('#').unwrap_or_else(|| fragment.len()); From 7e4028f9e1d95558f035128072f8ae92454e11ea Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 12 Oct 2020 18:28:57 +0200 Subject: [PATCH 2/8] Add new lint for automatic_links improvements --- compiler/rustc_lint/src/lib.rs | 3 +- compiler/rustc_session/src/lint/builtin.rs | 12 +++ src/librustdoc/core.rs | 2 + src/librustdoc/passes/automatic_links.rs | 93 ++++++++++++++++++++++ src/librustdoc/passes/mod.rs | 5 ++ 5 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 src/librustdoc/passes/automatic_links.rs diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 1db59bfc39dce..7e97b20e8ac59 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -63,7 +63,7 @@ use rustc_hir::def_id::LocalDefId; use rustc_middle::ty::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_session::lint::builtin::{ - BARE_TRAIT_OBJECTS, BROKEN_INTRA_DOC_LINKS, ELIDED_LIFETIMES_IN_PATHS, + AUTOMATIC_LINKS, BARE_TRAIT_OBJECTS, BROKEN_INTRA_DOC_LINKS, ELIDED_LIFETIMES_IN_PATHS, EXPLICIT_OUTLIVES_REQUIREMENTS, INVALID_CODEBLOCK_ATTRIBUTES, INVALID_HTML_TAGS, MISSING_DOC_CODE_EXAMPLES, PRIVATE_DOC_TESTS, }; @@ -307,6 +307,7 @@ fn register_builtins(store: &mut LintStore, no_interleave_lints: bool) { add_lint_group!( "rustdoc", + AUTOMATIC_LINKS, BROKEN_INTRA_DOC_LINKS, PRIVATE_INTRA_DOC_LINKS, INVALID_CODEBLOCK_ATTRIBUTES, diff --git a/compiler/rustc_session/src/lint/builtin.rs b/compiler/rustc_session/src/lint/builtin.rs index fef3164de59be..9e4eadff254e4 100644 --- a/compiler/rustc_session/src/lint/builtin.rs +++ b/compiler/rustc_session/src/lint/builtin.rs @@ -1891,6 +1891,17 @@ declare_lint! { "detects invalid HTML tags in doc comments" } +declare_lint! { + /// The `automatic_links` lint detects when a URL/email address could be + /// written using only brackets. This is a `rustdoc` only lint, see the + /// documentation in the [rustdoc book]. + /// + /// [rustdoc book]: ../../../rustdoc/lints.html#automatic_links + pub AUTOMATIC_LINKS, + Allow, + "detects URLs/email adresses that could be written using only brackets" +} + declare_lint! { /// The `where_clauses_object_safety` lint detects for [object safety] of /// [where clauses]. @@ -2711,6 +2722,7 @@ declare_lint_pass! { MISSING_DOC_CODE_EXAMPLES, INVALID_HTML_TAGS, PRIVATE_DOC_TESTS, + AUTOMATIC_LINKS, WHERE_CLAUSES_OBJECT_SAFETY, PROC_MACRO_DERIVE_RESOLUTION_FALLBACK, MACRO_USE_EXTERN_CRATE, diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 45a84c4fb30d3..f834be84d4c59 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -330,11 +330,13 @@ pub fn run_core( let invalid_codeblock_attributes_name = rustc_lint::builtin::INVALID_CODEBLOCK_ATTRIBUTES.name; let invalid_html_tags = rustc_lint::builtin::INVALID_HTML_TAGS.name; let renamed_and_removed_lints = rustc_lint::builtin::RENAMED_AND_REMOVED_LINTS.name; + let automatic_links = rustc_lint::builtin::AUTOMATIC_LINKS.name; let unknown_lints = rustc_lint::builtin::UNKNOWN_LINTS.name; // In addition to those specific lints, we also need to allow those given through // command line, otherwise they'll get ignored and we don't want that. let lints_to_show = vec![ + automatic_links.to_owned(), intra_link_resolution_failure_name.to_owned(), missing_docs.to_owned(), missing_doc_example.to_owned(), diff --git a/src/librustdoc/passes/automatic_links.rs b/src/librustdoc/passes/automatic_links.rs new file mode 100644 index 0000000000000..79542241326da --- /dev/null +++ b/src/librustdoc/passes/automatic_links.rs @@ -0,0 +1,93 @@ +use super::{span_of_attrs, Pass}; +use crate::clean::*; +use crate::core::DocContext; +use crate::fold::DocFolder; +use crate::html::markdown::opts; +use pulldown_cmark::{Event, Parser, Tag}; +use rustc_feature::UnstableFeatures; +use rustc_session::lint; + +pub const CHECK_AUTOMATIC_LINKS: Pass = Pass { + name: "check-automatic-links", + run: check_automatic_links, + description: "detects URLS/email addresses that could be written using brackets", +}; + +struct AutomaticLinksLinter<'a, 'tcx> { + cx: &'a DocContext<'tcx>, +} + +impl<'a, 'tcx> AutomaticLinksLinter<'a, 'tcx> { + fn new(cx: &'a DocContext<'tcx>) -> Self { + AutomaticLinksLinter { cx } + } +} + +pub fn check_automatic_links(krate: Crate, cx: &DocContext<'_>) -> Crate { + if !UnstableFeatures::from_environment().is_nightly_build() { + krate + } else { + let mut coll = AutomaticLinksLinter::new(cx); + + coll.fold_crate(krate) + } +} + +impl<'a, 'tcx> DocFolder for AutomaticLinksLinter<'a, 'tcx> { + fn fold_item(&mut self, item: Item) -> Option { + let hir_id = match self.cx.as_local_hir_id(item.def_id) { + Some(hir_id) => hir_id, + None => { + // If non-local, no need to check anything. + return self.fold_item_recur(item); + } + }; + let dox = item.attrs.collapsed_doc_value().unwrap_or_default(); + if !dox.is_empty() { + let cx = &self.cx; + + let p = Parser::new_ext(&dox, opts()).into_offset_iter(); + + let mut title = String::new(); + let mut in_link = false; + + for (event, range) in p { + match event { + Event::Start(Tag::Link(..)) => in_link = true, + Event::End(Tag::Link(_, url, _)) => { + in_link = false; + if url.as_ref() != title { + continue; + } + let sp = match super::source_span_for_markdown_range( + cx, + &dox, + &range, + &item.attrs, + ) { + Some(sp) => sp, + None => span_of_attrs(&item.attrs).unwrap_or(item.source.span()), + }; + cx.tcx.struct_span_lint_hir( + lint::builtin::AUTOMATIC_LINKS, + hir_id, + sp, + |lint| { + lint.build("Unneeded long form for URL") + .help(&format!("Try with `<{}>` instead", url)) + .emit() + }, + ); + title.clear(); + } + Event::Text(s) if in_link => { + title.push_str(&s); + } + _ => {} + } + } + } + + self.fold_item_recur(item) + } +} diff --git a/src/librustdoc/passes/mod.rs b/src/librustdoc/passes/mod.rs index 39c61f7edbe94..0436a999ecb8c 100644 --- a/src/librustdoc/passes/mod.rs +++ b/src/librustdoc/passes/mod.rs @@ -12,6 +12,9 @@ use crate::clean::{self, DocFragmentKind, GetDefId, Item}; use crate::core::DocContext; use crate::fold::{DocFolder, StripItem}; +mod automatic_links; +pub use self::automatic_links::CHECK_AUTOMATIC_LINKS; + mod collapse_docs; pub use self::collapse_docs::COLLAPSE_DOCS; @@ -91,6 +94,7 @@ pub const PASSES: &[Pass] = &[ COLLECT_TRAIT_IMPLS, CALCULATE_DOC_COVERAGE, CHECK_INVALID_HTML_TAGS, + CHECK_AUTOMATIC_LINKS, ]; /// The list of passes run by default. @@ -106,6 +110,7 @@ pub const DEFAULT_PASSES: &[ConditionalPass] = &[ ConditionalPass::always(CHECK_CODE_BLOCK_SYNTAX), ConditionalPass::always(CHECK_INVALID_HTML_TAGS), ConditionalPass::always(PROPAGATE_DOC_CFG), + ConditionalPass::always(CHECK_AUTOMATIC_LINKS), ]; /// The list of default passes run when `--doc-coverage` is passed to rustdoc. From 2e832c92a3bbd07124dc6932037ed668be22c519 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 12 Oct 2020 18:29:38 +0200 Subject: [PATCH 3/8] Add documentation for automatic_links lint --- compiler/rustc_session/src/lint/builtin.rs | 6 ++-- src/doc/rustdoc/src/lints.md | 40 ++++++++++++++++++++++ 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_session/src/lint/builtin.rs b/compiler/rustc_session/src/lint/builtin.rs index 9e4eadff254e4..3fa9c288904df 100644 --- a/compiler/rustc_session/src/lint/builtin.rs +++ b/compiler/rustc_session/src/lint/builtin.rs @@ -1893,13 +1893,13 @@ declare_lint! { declare_lint! { /// The `automatic_links` lint detects when a URL/email address could be - /// written using only brackets. This is a `rustdoc` only lint, see the - /// documentation in the [rustdoc book]. + /// written using only angle brackets. This is a `rustdoc` only lint, see + /// the documentation in the [rustdoc book]. /// /// [rustdoc book]: ../../../rustdoc/lints.html#automatic_links pub AUTOMATIC_LINKS, Allow, - "detects URLs/email adresses that could be written using only brackets" + "detects URLs/email adresses that could be written using only angle brackets" } declare_lint! { diff --git a/src/doc/rustdoc/src/lints.md b/src/doc/rustdoc/src/lints.md index d8c0bab225943..490f7f237f1f0 100644 --- a/src/doc/rustdoc/src/lints.md +++ b/src/doc/rustdoc/src/lints.md @@ -285,3 +285,43 @@ warning: unclosed HTML tag `h1` warning: 2 warnings emitted ``` + +## automatic_links + +This link is **allowed by default** and is **nightly-only**. It detects links +which could use the "automatic" link syntax. For example: + +```rust +#![warn(automatic_links)] + +/// [http://a.com](http://a.com) +/// [http://b.com] +/// +/// [http://b.com]: http://b.com +pub fn foo() {} +``` + +Which will give: + +```text +error: Unneeded long form for URL + --> foo.rs:3:5 + | +3 | /// [http://a.com](http://a.com) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> foo.rs:1:9 + | +1 | #![deny(automatic_links)] + | ^^^^^^^^^^^^^^^ + = help: Try with `` instead + +error: Unneeded long form for URL + --> foo.rs:5:5 + | +5 | /// [http://b.com] + | ^^^^^^^^^^^^^^ + | + = help: Try with `` instead +``` From 3649620810cb02c1ee0bbce7bd0afaee38106d6d Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 12 Oct 2020 18:29:56 +0200 Subject: [PATCH 4/8] Add tests for automatic_links lint --- src/test/rustdoc-ui/automatic-links.rs | 17 ++++++++++++++++ src/test/rustdoc-ui/automatic-links.stderr | 23 ++++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 src/test/rustdoc-ui/automatic-links.rs create mode 100644 src/test/rustdoc-ui/automatic-links.stderr diff --git a/src/test/rustdoc-ui/automatic-links.rs b/src/test/rustdoc-ui/automatic-links.rs new file mode 100644 index 0000000000000..9273b854aee1d --- /dev/null +++ b/src/test/rustdoc-ui/automatic-links.rs @@ -0,0 +1,17 @@ +#![deny(automatic_links)] + +/// [http://a.com](http://a.com) +//~^ ERROR Unneeded long form for URL +/// [http://b.com] +//~^ ERROR Unneeded long form for URL +/// +/// [http://b.com]: http://b.com +/// +/// [http://c.com][http://c.com] +pub fn a() {} + +/// [a](http://a.com) +/// [b] +/// +/// [b]: http://b.com +pub fn everything_is_fine_here() {} diff --git a/src/test/rustdoc-ui/automatic-links.stderr b/src/test/rustdoc-ui/automatic-links.stderr new file mode 100644 index 0000000000000..2922fedb238cb --- /dev/null +++ b/src/test/rustdoc-ui/automatic-links.stderr @@ -0,0 +1,23 @@ +error: Unneeded long form for URL + --> $DIR/automatic-links.rs:3:5 + | +LL | /// [http://a.com](http://a.com) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/automatic-links.rs:1:9 + | +LL | #![deny(automatic_links)] + | ^^^^^^^^^^^^^^^ + = help: Try with `` instead + +error: Unneeded long form for URL + --> $DIR/automatic-links.rs:5:5 + | +LL | /// [http://b.com] + | ^^^^^^^^^^^^^^ + | + = help: Try with `` instead + +error: aborting due to 2 previous errors + From 6fdd98dd032cfce0e6993254bf06f9683535a32b Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 13 Oct 2020 15:46:34 +0200 Subject: [PATCH 5/8] Extend automatic_links lint to take into account URLs without link syntax --- Cargo.lock | 1 + src/doc/rustdoc/src/lints.md | 22 +++--- src/librustdoc/Cargo.toml | 1 + src/librustdoc/passes/automatic_links.rs | 89 +++++++++++++++------- src/test/rustdoc-ui/automatic-links.rs | 9 ++- src/test/rustdoc-ui/automatic-links.stderr | 17 +++-- 6 files changed, 94 insertions(+), 45 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 63c4fcf9e67b5..d875ba5a2ea2c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4203,6 +4203,7 @@ dependencies = [ "itertools 0.9.0", "minifier", "pulldown-cmark 0.8.0", + "regex", "rustc-rayon", "serde", "serde_json", diff --git a/src/doc/rustdoc/src/lints.md b/src/doc/rustdoc/src/lints.md index 490f7f237f1f0..a3bdf3201caf9 100644 --- a/src/doc/rustdoc/src/lints.md +++ b/src/doc/rustdoc/src/lints.md @@ -294,6 +294,7 @@ which could use the "automatic" link syntax. For example: ```rust #![warn(automatic_links)] +/// http://hello.rs /// [http://a.com](http://a.com) /// [http://b.com] /// @@ -304,24 +305,27 @@ pub fn foo() {} Which will give: ```text -error: Unneeded long form for URL +warning: won't be a link as is --> foo.rs:3:5 | -3 | /// [http://a.com](http://a.com) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +3 | /// http://hello.rs + | ^^^^^^^^^^^^^^^ help: use an automatic link instead: `` | note: the lint level is defined here --> foo.rs:1:9 | -1 | #![deny(automatic_links)] +1 | #![warn(automatic_links)] | ^^^^^^^^^^^^^^^ - = help: Try with `` instead -error: Unneeded long form for URL +warning: unneeded long form for URL + --> foo.rs:4:5 + | +4 | /// [http://a.com](http://a.com) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + +warning: unneeded long form for URL --> foo.rs:5:5 | 5 | /// [http://b.com] - | ^^^^^^^^^^^^^^ - | - = help: Try with `` instead + | ^^^^^^^^^^^^^^ help: use an automatic link instead: `` ``` diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index a40a44fe27da3..b0f5bac6abd0f 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -16,6 +16,7 @@ serde_json = "1.0" smallvec = "1.0" tempfile = "3" itertools = "0.9" +regex = "1" [dev-dependencies] expect-test = "1.0" diff --git a/src/librustdoc/passes/automatic_links.rs b/src/librustdoc/passes/automatic_links.rs index 79542241326da..11c1a4d0bfba8 100644 --- a/src/librustdoc/passes/automatic_links.rs +++ b/src/librustdoc/passes/automatic_links.rs @@ -3,23 +3,55 @@ use crate::clean::*; use crate::core::DocContext; use crate::fold::DocFolder; use crate::html::markdown::opts; -use pulldown_cmark::{Event, Parser, Tag}; +use core::ops::Range; +use pulldown_cmark::{Event, LinkType, Parser, Tag}; +use regex::Regex; +use rustc_errors::Applicability; use rustc_feature::UnstableFeatures; use rustc_session::lint; pub const CHECK_AUTOMATIC_LINKS: Pass = Pass { name: "check-automatic-links", run: check_automatic_links, - description: "detects URLS/email addresses that could be written using brackets", + description: "detects URLS/email addresses that could be written using angle brackets", }; +const URL_REGEX: &str = + r"https?://(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,4}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)"; + struct AutomaticLinksLinter<'a, 'tcx> { cx: &'a DocContext<'tcx>, + regex: Regex, } impl<'a, 'tcx> AutomaticLinksLinter<'a, 'tcx> { fn new(cx: &'a DocContext<'tcx>) -> Self { - AutomaticLinksLinter { cx } + AutomaticLinksLinter { cx, regex: Regex::new(URL_REGEX).expect("failed to build regex") } + } + + fn find_raw_urls( + &self, + text: &str, + range: Range, + f: &impl Fn(&DocContext<'_>, &str, &str, Range), + ) { + for (pos, c) in text.char_indices() { + // For now, we only check "full" URLs. + if c == 'h' { + let text = &text[pos..]; + if text.starts_with("http://") || text.starts_with("https://") { + if let Some(m) = self.regex.find(text) { + let url = &text[..m.end()]; + f( + self.cx, + "won't be a link as is", + url, + Range { start: range.start + pos, end: range.start + pos + m.end() }, + ) + } + } + } + } } } @@ -44,45 +76,48 @@ impl<'a, 'tcx> DocFolder for AutomaticLinksLinter<'a, 'tcx> { }; let dox = item.attrs.collapsed_doc_value().unwrap_or_default(); if !dox.is_empty() { - let cx = &self.cx; + let report_diag = |cx: &DocContext<'_>, msg: &str, url: &str, range: Range| { + let sp = super::source_span_for_markdown_range(cx, &dox, &range, &item.attrs) + .or_else(|| span_of_attrs(&item.attrs)) + .unwrap_or(item.source.span()); + cx.tcx.struct_span_lint_hir(lint::builtin::AUTOMATIC_LINKS, hir_id, sp, |lint| { + lint.build(msg) + .span_suggestion( + sp, + "use an automatic link instead", + format!("<{}>", url), + Applicability::MachineApplicable, + ) + .emit() + }); + }; let p = Parser::new_ext(&dox, opts()).into_offset_iter(); let mut title = String::new(); let mut in_link = false; + let mut ignore = false; for (event, range) in p { match event { - Event::Start(Tag::Link(..)) => in_link = true, + Event::Start(Tag::Link(kind, _, _)) => { + in_link = true; + ignore = matches!(kind, LinkType::Autolink | LinkType::Email); + } Event::End(Tag::Link(_, url, _)) => { in_link = false; - if url.as_ref() != title { - continue; + if url.as_ref() == title && !ignore { + report_diag(self.cx, "unneeded long form for URL", &url, range); } - let sp = match super::source_span_for_markdown_range( - cx, - &dox, - &range, - &item.attrs, - ) { - Some(sp) => sp, - None => span_of_attrs(&item.attrs).unwrap_or(item.source.span()), - }; - cx.tcx.struct_span_lint_hir( - lint::builtin::AUTOMATIC_LINKS, - hir_id, - sp, - |lint| { - lint.build("Unneeded long form for URL") - .help(&format!("Try with `<{}>` instead", url)) - .emit() - }, - ); title.clear(); + ignore = false; } Event::Text(s) if in_link => { - title.push_str(&s); + if !ignore { + title.push_str(&s); + } } + Event::Text(s) => self.find_raw_urls(&s, range, &report_diag), _ => {} } } diff --git a/src/test/rustdoc-ui/automatic-links.rs b/src/test/rustdoc-ui/automatic-links.rs index 9273b854aee1d..f9dbe67e5b1da 100644 --- a/src/test/rustdoc-ui/automatic-links.rs +++ b/src/test/rustdoc-ui/automatic-links.rs @@ -1,15 +1,20 @@ #![deny(automatic_links)] /// [http://a.com](http://a.com) -//~^ ERROR Unneeded long form for URL +//~^ ERROR unneeded long form for URL /// [http://b.com] -//~^ ERROR Unneeded long form for URL +//~^ ERROR unneeded long form for URL /// /// [http://b.com]: http://b.com /// /// [http://c.com][http://c.com] pub fn a() {} +/// https://somewhere.com?hello=12 +//~^ ERROR won't be a link as is +pub fn c() {} + +/// /// [a](http://a.com) /// [b] /// diff --git a/src/test/rustdoc-ui/automatic-links.stderr b/src/test/rustdoc-ui/automatic-links.stderr index 2922fedb238cb..d2c0c51d7a472 100644 --- a/src/test/rustdoc-ui/automatic-links.stderr +++ b/src/test/rustdoc-ui/automatic-links.stderr @@ -1,23 +1,26 @@ -error: Unneeded long form for URL +error: unneeded long form for URL --> $DIR/automatic-links.rs:3:5 | LL | /// [http://a.com](http://a.com) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` | note: the lint level is defined here --> $DIR/automatic-links.rs:1:9 | LL | #![deny(automatic_links)] | ^^^^^^^^^^^^^^^ - = help: Try with `` instead -error: Unneeded long form for URL +error: unneeded long form for URL --> $DIR/automatic-links.rs:5:5 | LL | /// [http://b.com] - | ^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^ help: use an automatic link instead: `` + +error: won't be a link as is + --> $DIR/automatic-links.rs:13:5 | - = help: Try with `` instead +LL | /// https://somewhere.com?hello=12 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors From e833cd302bb7f88dc47846b17f3ba2cc7d47d4ac Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 14 Oct 2020 15:11:55 +0200 Subject: [PATCH 6/8] Improve automatic_links globally --- compiler/rustc_session/src/lint/builtin.rs | 10 +-- src/doc/rustdoc/src/lints.md | 13 +-- src/librustdoc/passes/automatic_links.rs | 37 ++++---- src/test/rustdoc-ui/automatic-links.rs | 40 ++++++++- src/test/rustdoc-ui/automatic-links.stderr | 100 ++++++++++++++++++++- 5 files changed, 163 insertions(+), 37 deletions(-) diff --git a/compiler/rustc_session/src/lint/builtin.rs b/compiler/rustc_session/src/lint/builtin.rs index 3fa9c288904df..63f5766d50dde 100644 --- a/compiler/rustc_session/src/lint/builtin.rs +++ b/compiler/rustc_session/src/lint/builtin.rs @@ -1892,14 +1892,14 @@ declare_lint! { } declare_lint! { - /// The `automatic_links` lint detects when a URL/email address could be - /// written using only angle brackets. This is a `rustdoc` only lint, see - /// the documentation in the [rustdoc book]. + /// The `automatic_links` lint detects when a URL could be written using + /// only angle brackets. This is a `rustdoc` only lint, see the + /// documentation in the [rustdoc book]. /// /// [rustdoc book]: ../../../rustdoc/lints.html#automatic_links pub AUTOMATIC_LINKS, - Allow, - "detects URLs/email adresses that could be written using only angle brackets" + Warn, + "detects URLs that could be written using only angle brackets" } declare_lint! { diff --git a/src/doc/rustdoc/src/lints.md b/src/doc/rustdoc/src/lints.md index a3bdf3201caf9..c9e93384c2338 100644 --- a/src/doc/rustdoc/src/lints.md +++ b/src/doc/rustdoc/src/lints.md @@ -288,12 +288,10 @@ warning: 2 warnings emitted ## automatic_links -This link is **allowed by default** and is **nightly-only**. It detects links -which could use the "automatic" link syntax. For example: +This lint is **nightly-only** and **warns by default**. It detects links which +could use the "automatic" link syntax. For example: ```rust -#![warn(automatic_links)] - /// http://hello.rs /// [http://a.com](http://a.com) /// [http://b.com] @@ -305,17 +303,12 @@ pub fn foo() {} Which will give: ```text -warning: won't be a link as is +warning: this URL is not a hyperlink --> foo.rs:3:5 | 3 | /// http://hello.rs | ^^^^^^^^^^^^^^^ help: use an automatic link instead: `` | -note: the lint level is defined here - --> foo.rs:1:9 - | -1 | #![warn(automatic_links)] - | ^^^^^^^^^^^^^^^ warning: unneeded long form for URL --> foo.rs:4:5 diff --git a/src/librustdoc/passes/automatic_links.rs b/src/librustdoc/passes/automatic_links.rs index 11c1a4d0bfba8..816d2fd15eee5 100644 --- a/src/librustdoc/passes/automatic_links.rs +++ b/src/librustdoc/passes/automatic_links.rs @@ -13,11 +13,15 @@ use rustc_session::lint; pub const CHECK_AUTOMATIC_LINKS: Pass = Pass { name: "check-automatic-links", run: check_automatic_links, - description: "detects URLS/email addresses that could be written using angle brackets", + description: "detects URLS that could be written using angle brackets", }; -const URL_REGEX: &str = - r"https?://(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,4}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)"; +const URL_REGEX: &str = concat!( + r"https?://", // url scheme + r"([-a-zA-Z0-9@:%._\+~#=]{2,256}\.)+", // one or more subdomains + r"[a-zA-Z]{2,4}", // root domain + r"\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)" // optional query or url fragments +); struct AutomaticLinksLinter<'a, 'tcx> { cx: &'a DocContext<'tcx>, @@ -35,22 +39,16 @@ impl<'a, 'tcx> AutomaticLinksLinter<'a, 'tcx> { range: Range, f: &impl Fn(&DocContext<'_>, &str, &str, Range), ) { - for (pos, c) in text.char_indices() { - // For now, we only check "full" URLs. - if c == 'h' { - let text = &text[pos..]; - if text.starts_with("http://") || text.starts_with("https://") { - if let Some(m) = self.regex.find(text) { - let url = &text[..m.end()]; - f( - self.cx, - "won't be a link as is", - url, - Range { start: range.start + pos, end: range.start + pos + m.end() }, - ) - } - } - } + // For now, we only check "full" URLs (meaning, starting with "http://" or "https://"). + for match_ in self.regex.find_iter(&text) { + let url = match_.as_str(); + let url_range = match_.range(); + f( + self.cx, + "this URL is not a hyperlink", + url, + Range { start: range.start + url_range.start, end: range.start + url_range.end }, + ); } } } @@ -106,6 +104,7 @@ impl<'a, 'tcx> DocFolder for AutomaticLinksLinter<'a, 'tcx> { } Event::End(Tag::Link(_, url, _)) => { in_link = false; + // NOTE: links cannot be nested, so we don't need to check `kind` if url.as_ref() == title && !ignore { report_diag(self.cx, "unneeded long form for URL", &url, range); } diff --git a/src/test/rustdoc-ui/automatic-links.rs b/src/test/rustdoc-ui/automatic-links.rs index f9dbe67e5b1da..27eb4e4a646ce 100644 --- a/src/test/rustdoc-ui/automatic-links.rs +++ b/src/test/rustdoc-ui/automatic-links.rs @@ -10,8 +10,40 @@ /// [http://c.com][http://c.com] pub fn a() {} +/// https://somewhere.com +//~^ ERROR this URL is not a hyperlink +/// https://somewhere.com/a +//~^ ERROR this URL is not a hyperlink +/// https://www.somewhere.com +//~^ ERROR this URL is not a hyperlink +/// https://www.somewhere.com/a +//~^ ERROR this URL is not a hyperlink +/// https://subdomain.example.com +//~^ ERROR not a hyperlink +/// https://somewhere.com? +//~^ ERROR this URL is not a hyperlink +/// https://somewhere.com/a? +//~^ ERROR this URL is not a hyperlink /// https://somewhere.com?hello=12 -//~^ ERROR won't be a link as is +//~^ ERROR this URL is not a hyperlink +/// https://somewhere.com/a?hello=12 +//~^ ERROR this URL is not a hyperlink +/// https://example.com?hello=12#xyz +//~^ ERROR this URL is not a hyperlink +/// https://example.com/a?hello=12#xyz +//~^ ERROR this URL is not a hyperlink +/// https://example.com#xyz +//~^ ERROR this URL is not a hyperlink +/// https://example.com/a#xyz +//~^ ERROR this URL is not a hyperlink +/// https://somewhere.com?hello=12&bye=11 +//~^ ERROR this URL is not a hyperlink +/// https://somewhere.com/a?hello=12&bye=11 +//~^ ERROR this URL is not a hyperlink +/// https://somewhere.com?hello=12&bye=11#xyz +//~^ ERROR this URL is not a hyperlink +/// hey! https://somewhere.com/a?hello=12&bye=11#xyz +//~^ ERROR this URL is not a hyperlink pub fn c() {} /// @@ -20,3 +52,9 @@ pub fn c() {} /// /// [b]: http://b.com pub fn everything_is_fine_here() {} + +#[allow(automatic_links)] +pub mod foo { + /// https://somewhere.com/a?hello=12&bye=11#xyz + pub fn bar() {} +} diff --git a/src/test/rustdoc-ui/automatic-links.stderr b/src/test/rustdoc-ui/automatic-links.stderr index d2c0c51d7a472..00e210aaaa22d 100644 --- a/src/test/rustdoc-ui/automatic-links.stderr +++ b/src/test/rustdoc-ui/automatic-links.stderr @@ -16,11 +16,107 @@ error: unneeded long form for URL LL | /// [http://b.com] | ^^^^^^^^^^^^^^ help: use an automatic link instead: `` -error: won't be a link as is +error: this URL is not a hyperlink --> $DIR/automatic-links.rs:13:5 | +LL | /// https://somewhere.com + | ^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + +error: this URL is not a hyperlink + --> $DIR/automatic-links.rs:15:5 + | +LL | /// https://somewhere.com/a + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + +error: this URL is not a hyperlink + --> $DIR/automatic-links.rs:17:5 + | +LL | /// https://www.somewhere.com + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + +error: this URL is not a hyperlink + --> $DIR/automatic-links.rs:19:5 + | +LL | /// https://www.somewhere.com/a + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + +error: this URL is not a hyperlink + --> $DIR/automatic-links.rs:21:5 + | +LL | /// https://subdomain.example.com + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + +error: this URL is not a hyperlink + --> $DIR/automatic-links.rs:23:5 + | +LL | /// https://somewhere.com? + | ^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + +error: this URL is not a hyperlink + --> $DIR/automatic-links.rs:25:5 + | +LL | /// https://somewhere.com/a? + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + +error: this URL is not a hyperlink + --> $DIR/automatic-links.rs:27:5 + | LL | /// https://somewhere.com?hello=12 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` -error: aborting due to 3 previous errors +error: this URL is not a hyperlink + --> $DIR/automatic-links.rs:29:5 + | +LL | /// https://somewhere.com/a?hello=12 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + +error: this URL is not a hyperlink + --> $DIR/automatic-links.rs:31:5 + | +LL | /// https://example.com?hello=12#xyz + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + +error: this URL is not a hyperlink + --> $DIR/automatic-links.rs:33:5 + | +LL | /// https://example.com/a?hello=12#xyz + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + +error: this URL is not a hyperlink + --> $DIR/automatic-links.rs:35:5 + | +LL | /// https://example.com#xyz + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + +error: this URL is not a hyperlink + --> $DIR/automatic-links.rs:37:5 + | +LL | /// https://example.com/a#xyz + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + +error: this URL is not a hyperlink + --> $DIR/automatic-links.rs:39:5 + | +LL | /// https://somewhere.com?hello=12&bye=11 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + +error: this URL is not a hyperlink + --> $DIR/automatic-links.rs:41:5 + | +LL | /// https://somewhere.com/a?hello=12&bye=11 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + +error: this URL is not a hyperlink + --> $DIR/automatic-links.rs:43:5 + | +LL | /// https://somewhere.com?hello=12&bye=11#xyz + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + +error: this URL is not a hyperlink + --> $DIR/automatic-links.rs:45:10 + | +LL | /// hey! https://somewhere.com/a?hello=12&bye=11#xyz + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + +error: aborting due to 19 previous errors From d7272eadb2eed094169e7d19a4bf9ef4b96c3c58 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 14 Oct 2020 17:35:43 +0200 Subject: [PATCH 7/8] Fix automatic_links warnings --- library/core/src/intrinsics.rs | 2 +- library/core/src/lib.rs | 1 + library/core/src/num/dec2flt/mod.rs | 2 +- library/core/src/slice/sort.rs | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index b2798ea66250f..c13da79fff504 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -9,7 +9,7 @@ //! This includes changes in the stability of the constness. //! //! In order to make an intrinsic usable at compile-time, one needs to copy the implementation -//! from https://github.com/rust-lang/miri/blob/master/src/shims/intrinsics.rs to +//! from to //! `compiler/rustc_mir/src/interpret/intrinsics.rs` and add a //! `#[rustc_const_unstable(feature = "foo", issue = "01234")]` to the intrinsic. //! diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 960a26fd28329..00b9193b5f6bd 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -283,6 +283,7 @@ pub mod primitive; unused_imports, unsafe_op_in_unsafe_fn )] +#[cfg_attr(not(bootstrap), allow(automatic_links))] // FIXME: This annotation should be moved into rust-lang/stdarch after clashing_extern_declarations is // merged. It currently cannot because bootstrap fails as the lint hasn't been defined yet. #[allow(clashing_extern_declarations)] diff --git a/library/core/src/num/dec2flt/mod.rs b/library/core/src/num/dec2flt/mod.rs index 6f3a3a867450d..039112e9f3468 100644 --- a/library/core/src/num/dec2flt/mod.rs +++ b/library/core/src/num/dec2flt/mod.rs @@ -33,7 +33,7 @@ //! //! Primarily, this module and its children implement the algorithms described in: //! "How to Read Floating Point Numbers Accurately" by William D. Clinger, -//! available online: http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.45.4152 +//! available online: //! //! In addition, there are numerous helper functions that are used in the paper but not available //! in Rust (or at least in core). Our version is additionally complicated by the need to handle diff --git a/library/core/src/slice/sort.rs b/library/core/src/slice/sort.rs index 71d2c2c9b2f4c..2a7693d27efa2 100644 --- a/library/core/src/slice/sort.rs +++ b/library/core/src/slice/sort.rs @@ -1,7 +1,7 @@ //! Slice sorting //! //! This module contains a sorting algorithm based on Orson Peters' pattern-defeating quicksort, -//! published at: https://github.com/orlp/pdqsort +//! published at: //! //! Unstable sorting is compatible with libcore because it doesn't allocate memory, unlike our //! stable sorting implementation. From a577942a8f398a516a1156be8e3e672cd28edf3d Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 16 Oct 2020 15:01:17 +0200 Subject: [PATCH 8/8] Fix sidebar scroll on mobile devices --- src/librustdoc/html/static/main.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js index 1b3eb2011afdc..67e50bba1f2e0 100644 --- a/src/librustdoc/html/static/main.js +++ b/src/librustdoc/html/static/main.js @@ -2716,10 +2716,6 @@ function defocusSearchBar() { }; } - window.onresize = function() { - hideSidebar(); - }; - if (main) { onEachLazy(main.getElementsByClassName("loading-content"), function(e) { e.remove();