diff --git a/Cargo.lock b/Cargo.lock index 3d40ded19dd5a..3a1dae971cc19 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4297,6 +4297,7 @@ dependencies = [ "itertools 0.9.0", "minifier", "pulldown-cmark 0.8.0", + "regex", "rustc-rayon", "serde", "serde_json", diff --git a/compiler/rustc_codegen_cranelift/src/discriminant.rs b/compiler/rustc_codegen_cranelift/src/discriminant.rs index 6c9fb8e051b3c..1e8e86add1a59 100644 --- a/compiler/rustc_codegen_cranelift/src/discriminant.rs +++ b/compiler/rustc_codegen_cranelift/src/discriminant.rs @@ -1,6 +1,6 @@ //! Handling of enum discriminants //! -//! Adapted from https://github.com/rust-lang/rust/blob/d760df5aea483aae041c9a241e7acacf48f75035/src/librustc_codegen_ssa/mir/place.rs +//! Adapted from use rustc_target::abi::{Int, TagEncoding, Variants}; diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 7297a6de42046..24bfdad970a1c 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -69,7 +69,7 @@ use rustc_middle::ty::TyCtxt; use rustc_session::lint::builtin::{ 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, + MISSING_DOC_CODE_EXAMPLES, NON_AUTOLINKS, PRIVATE_DOC_TESTS, }; use rustc_span::symbol::{Ident, Symbol}; use rustc_span::Span; @@ -313,6 +313,7 @@ fn register_builtins(store: &mut LintStore, no_interleave_lints: bool) { add_lint_group!( "rustdoc", + NON_AUTOLINKS, BROKEN_INTRA_DOC_LINKS, PRIVATE_INTRA_DOC_LINKS, INVALID_CODEBLOCK_ATTRIBUTES, diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index a1b7c13e4c0f0..ff7a145c2668d 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -1890,6 +1890,17 @@ declare_lint! { "detects invalid HTML tags in doc comments" } +declare_lint! { + /// The `non_autolinks` 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#non_autolinks + pub NON_AUTOLINKS, + Warn, + "detects URLs that could be written using only angle brackets" +} + declare_lint! { /// The `where_clauses_object_safety` lint detects for [object safety] of /// [where clauses]. @@ -2795,6 +2806,7 @@ declare_lint_pass! { MISSING_DOC_CODE_EXAMPLES, INVALID_HTML_TAGS, PRIVATE_DOC_TESTS, + NON_AUTOLINKS, WHERE_CLAUSES_OBJECT_SAFETY, PROC_MACRO_DERIVE_RESOLUTION_FALLBACK, MACRO_USE_EXTERN_CRATE, diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 25a7bfcabb728..af9926400ca44 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -99,13 +99,13 @@ pub struct Lint { /// The name is written with underscores, e.g., "unused_imports". /// On the command line, underscores become dashes. /// - /// See https://rustc-dev-guide.rust-lang.org/diagnostics.html#lint-naming + /// See /// for naming guidelines. pub name: &'static str, /// Default level for the lint. /// - /// See https://rustc-dev-guide.rust-lang.org/diagnostics.html#diagnostic-levels + /// See /// for guidelines on choosing a default level. pub default_level: Level, @@ -330,8 +330,8 @@ impl LintBuffer { /// Declares a static item of type `&'static Lint`. /// -/// See https://rustc-dev-guide.rust-lang.org/diagnostics.html for documentation -/// and guidelines on writing lints. +/// See for +/// documentation and guidelines on writing lints. /// /// The macro call should start with a doc comment explaining the lint /// which will be embedded in the rustc user documentation book. It should diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs index 0421eabc2dc05..22c36b928781b 100644 --- a/compiler/rustc_middle/src/mir/coverage.rs +++ b/compiler/rustc_middle/src/mir/coverage.rs @@ -17,9 +17,9 @@ rustc_index::newtype_index! { impl ExpressionOperandId { /// An expression operand for a "zero counter", as described in the following references: /// - /// * https://github.com/rust-lang/llvm-project/blob/llvmorg-8.0.0/llvm/docs/CoverageMappingFormat.rst#counter - /// * https://github.com/rust-lang/llvm-project/blob/llvmorg-8.0.0/llvm/docs/CoverageMappingFormat.rst#tag - /// * https://github.com/rust-lang/llvm-project/blob/llvmorg-8.0.0/llvm/docs/CoverageMappingFormat.rst#counter-expressions + /// * + /// * + /// * /// /// This operand can be used to count two or more separate code regions with a single counter, /// if they run sequentially with no branches, by injecting the `Counter` in a `BasicBlock` for diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index 79e2c5aac2385..1e70f7605045e 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -228,7 +228,7 @@ pub struct CodegenUnit<'tcx> { /// Specifies the linkage type for a `MonoItem`. /// -/// See https://llvm.org/docs/LangRef.html#linkage-types for more details about these variants. +/// See for more details about these variants. #[derive(Copy, Clone, PartialEq, Debug, TyEncodable, TyDecodable, HashStable)] pub enum Linkage { External, diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 216451f268f4e..1c6937e685c65 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -368,7 +368,7 @@ pub struct TypeckResults<'tcx> { /// leads to a `vec![&&Option, &Option]`. Empty vectors are not stored. /// /// See: - /// https://github.com/rust-lang/rfcs/blob/master/text/2005-match-ergonomics.md#definitions + /// pat_adjustments: ItemLocalMap>>, /// Borrows diff --git a/compiler/rustc_mir/src/borrow_check/region_infer/mod.rs b/compiler/rustc_mir/src/borrow_check/region_infer/mod.rs index 47726632727d0..ac8ab71a1dc96 100644 --- a/compiler/rustc_mir/src/borrow_check/region_infer/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/region_infer/mod.rs @@ -1364,7 +1364,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// terms that the "longer free region" `'a` outlived the "shorter free region" `'b`. /// /// More details can be found in this blog post by Niko: - /// http://smallcultfollowing.com/babysteps/blog/2019/01/17/polonius-and-region-errors/ + /// /// /// In the canonical example /// diff --git a/compiler/rustc_mir/src/transform/dest_prop.rs b/compiler/rustc_mir/src/transform/dest_prop.rs index 410f462ed469f..46de5dba6e0ed 100644 --- a/compiler/rustc_mir/src/transform/dest_prop.rs +++ b/compiler/rustc_mir/src/transform/dest_prop.rs @@ -8,7 +8,7 @@ //! inside a single block to shuffle a value around unnecessarily. //! //! LLVM by itself is not good enough at eliminating these redundant copies (eg. see -//! https://github.com/rust-lang/rust/issues/32966), so this leaves some performance on the table +//! ), so this leaves some performance on the table //! that we can regain by implementing an optimization for removing these assign statements in rustc //! itself. When this optimization runs fast enough, it can also speed up the constant evaluation //! and code generation phases of rustc due to the reduced number of statements and locals. diff --git a/compiler/rustc_mir_build/src/thir/pattern/_match.rs b/compiler/rustc_mir_build/src/thir/pattern/_match.rs index 9e096f9ad6847..bc85d45d86773 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/_match.rs @@ -8,7 +8,7 @@ //! (b) each pattern is necessary (usefulness) //! //! The algorithm implemented here is a modified version of the one described in: -//! http://moscova.inria.fr/~maranget/papers/warn/index.html +//! //! However, to save future implementors from reading the original paper, we //! summarise the algorithm here to hopefully save time and be a little clearer //! (without being so rigorous). @@ -2040,7 +2040,7 @@ impl<'tcx> MissingConstructors<'tcx> { } } -/// Algorithm from http://moscova.inria.fr/~maranget/papers/warn/index.html. +/// Algorithm from . /// The algorithm from the paper has been modified to correctly handle empty /// types. The changes are: /// (0) We don't exit early if the pattern matrix has zero rows. We just diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 32b124970cf7c..0dfacd78908ba 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -511,7 +511,7 @@ impl<'a> StringReader<'a> { } /// Note: It was decided to not add a test case, because it would be to big. - /// https://github.com/rust-lang/rust/pull/50296#issuecomment-392135180 + /// fn report_too_many_hashes(&self, start: BytePos, found: usize) -> ! { self.fatal_span_( start, diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 4c0941120a6fc..75d75433f1bf1 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -38,7 +38,7 @@ use std::{cmp, fmt, mem}; /// Implemented to visit all `DefId`s in a type. /// Visiting `DefId`s is useful because visibilities and reachabilities are attached to them. /// The idea is to visit "all components of a type", as documented in -/// https://github.com/rust-lang/rfcs/blob/master/text/2145-type-privacy.md#how-to-determine-visibility-of-a-type. +/// . /// The default type visitor (`TypeVisitor`) does most of the job, but it has some shortcomings. /// First, it doesn't have overridable `fn visit_trait_ref`, so we have to catch trait `DefId`s /// manually. Second, it doesn't visit some type components like signatures of fn types, or traits diff --git a/compiler/rustc_target/src/spec/crt_objects.rs b/compiler/rustc_target/src/spec/crt_objects.rs index 8991691a9a30c..76c0bf419e8c4 100644 --- a/compiler/rustc_target/src/spec/crt_objects.rs +++ b/compiler/rustc_target/src/spec/crt_objects.rs @@ -3,7 +3,7 @@ //! //! Table of CRT objects for popular toolchains. //! The `crtx` ones are generally distributed with libc and the `begin/end` ones with gcc. -//! See https://dev.gentoo.org/~vapier/crt.txt for some more details. +//! See for some more details. //! //! | Pre-link CRT objects | glibc | musl | bionic | mingw | wasi | //! |----------------------|------------------------|------------------------|------------------|-------------------|------| diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index ba9e2f7fa0648..d3c5a9433d08e 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -950,7 +950,7 @@ pub struct TargetOptions { /// The MergeFunctions pass is generally useful, but some targets may need /// to opt out. The default is "aliases". /// - /// Workaround for: https://github.com/rust-lang/rust/issues/57356 + /// Workaround for: pub merge_functions: MergeFunctions, /// Use platform dependent mcount function diff --git a/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs b/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs index 19609b0d496f2..1ef0a81937849 100644 --- a/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs +++ b/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs @@ -8,7 +8,7 @@ //! (e.g. trying to create a TCP stream or something like that). //! //! This target is more or less managed by the Rust and WebAssembly Working -//! Group nowadays at https://github.com/rustwasm. +//! Group nowadays at . use super::wasm32_base; use super::{LinkerFlavor, LldFlavor, Target}; diff --git a/compiler/rustc_target/src/spec/wasm32_wasi.rs b/compiler/rustc_target/src/spec/wasm32_wasi.rs index 26e0722fcf0c2..8c2bb9208291d 100644 --- a/compiler/rustc_target/src/spec/wasm32_wasi.rs +++ b/compiler/rustc_target/src/spec/wasm32_wasi.rs @@ -7,7 +7,7 @@ //! intended to empower WebAssembly binaries with native capabilities such as //! filesystem access, network access, etc. //! -//! You can see more about the proposal at https://wasi.dev +//! You can see more about the proposal at . //! //! The Rust target definition here is interesting in a few ways. We want to //! serve two use cases here with this target: diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index 50efbbbe0fd76..32e0991733bd9 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -621,7 +621,7 @@ fn object_ty_for_trait<'tcx>( /// /// In practice, we cannot use `dyn Trait` explicitly in the obligation because it would result /// in a new check that `Trait` is object safe, creating a cycle (until object_safe_for_dispatch -/// is stabilized, see tracking issue https://github.com/rust-lang/rust/issues/43561). +/// is stabilized, see tracking issue ). /// Instead, we fudge a little by introducing a new type parameter `U` such that /// `Self: Unsize` and `U: Trait + ?Sized`, and use `U` in place of `dyn Trait`. /// Written as a chalk-style query: diff --git a/library/core/src/future/mod.rs b/library/core/src/future/mod.rs index fa5655ca35f41..cdde094147012 100644 --- a/library/core/src/future/mod.rs +++ b/library/core/src/future/mod.rs @@ -32,7 +32,7 @@ pub use poll_fn::{poll_fn, PollFn}; /// This type is needed because: /// /// a) Generators cannot implement `for<'a, 'b> Generator<&'a mut Context<'b>>`, so we need to pass -/// a raw pointer (see https://github.com/rust-lang/rust/issues/68923). +/// a raw pointer (see ). /// b) Raw pointers and `NonNull` aren't `Send` or `Sync`, so that would make every single future /// non-Send/Sync as well, and we don't want that. /// diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 3e5d7caa2fe5d..3570cab0022e8 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 b89ec93834fcc..069e6e7e71881 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -287,6 +287,7 @@ pub mod primitive; unused_imports, unsafe_op_in_unsafe_fn )] +#[cfg_attr(not(bootstrap), allow(non_autolinks))] // 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. diff --git a/library/panic_unwind/src/dwarf/eh.rs b/library/panic_unwind/src/dwarf/eh.rs index 8ce4dcd2acd9c..6dbf7c11b4c4e 100644 --- a/library/panic_unwind/src/dwarf/eh.rs +++ b/library/panic_unwind/src/dwarf/eh.rs @@ -1,9 +1,9 @@ //! Parsing of GCC-style Language-Specific Data Area (LSDA) //! For details see: -//! http://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-PDA/LSB-PDA/ehframechpt.html -//! http://mentorembedded.github.io/cxx-abi/exceptions.pdf -//! http://www.airs.com/blog/archives/460 -//! http://www.airs.com/blog/archives/464 +//! * +//! * +//! * +//! * //! //! A reference implementation may be found in the GCC source tree //! (`/libgcc/unwind-c.c` as of this writing). diff --git a/library/panic_unwind/src/gcc.rs b/library/panic_unwind/src/gcc.rs index 6b88bab8277ee..14f49bbf48337 100644 --- a/library/panic_unwind/src/gcc.rs +++ b/library/panic_unwind/src/gcc.rs @@ -4,9 +4,9 @@ //! "Exception Handling in LLVM" (llvm.org/docs/ExceptionHandling.html) and //! documents linked from it. //! These are also good reads: -//! https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html -//! http://monoinfinito.wordpress.com/series/exception-handling-in-c/ -//! http://www.airs.com/blog/index.php?s=exception+frames +//! * +//! * +//! * //! //! ## A brief summary //! diff --git a/src/bootstrap/toolstate.rs b/src/bootstrap/toolstate.rs index 8740393288c48..205524ad84fb7 100644 --- a/src/bootstrap/toolstate.rs +++ b/src/bootstrap/toolstate.rs @@ -152,7 +152,7 @@ impl Step for ToolStateCheck { /// error if there are any. /// /// This also handles publishing the results to the `history` directory of - /// the toolstate repo https://github.com/rust-lang-nursery/rust-toolstate + /// the toolstate repo /// if the env var `TOOLSTATE_PUBLISH` is set. Note that there is a /// *separate* step of updating the `latest.json` file and creating GitHub /// issues and comments in `src/ci/publish_toolstate.sh`, which is only @@ -162,7 +162,7 @@ impl Step for ToolStateCheck { /// The rules for failure are: /// * If the PR modifies a tool, the status must be test-pass. /// NOTE: There is intent to change this, see - /// https://github.com/rust-lang/rust/issues/65000. + /// . /// * All "stable" tools must be test-pass on the stable or beta branches. /// * During beta promotion week, a PR is not allowed to "regress" a /// stable tool. That is, the status is not allowed to get worse diff --git a/src/build_helper/lib.rs b/src/build_helper/lib.rs index e30da8d56e10f..80f804174ed08 100644 --- a/src/build_helper/lib.rs +++ b/src/build_helper/lib.rs @@ -32,7 +32,7 @@ macro_rules! t { /// Reads an environment variable and adds it to dependencies. /// Supposed to be used for all variables except those set for build scripts by cargo -/// https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-build-scripts +/// pub fn tracked_env_var_os + Display>(key: K) -> Option { println!("cargo:rerun-if-env-changed={}", key); env::var_os(key) diff --git a/src/doc/rustdoc/src/lints.md b/src/doc/rustdoc/src/lints.md index cb9099cd50bee..41292b3d83841 100644 --- a/src/doc/rustdoc/src/lints.md +++ b/src/doc/rustdoc/src/lints.md @@ -285,3 +285,41 @@ warning: unclosed HTML tag `h1` warning: 2 warnings emitted ``` + +## non_autolinks + +This lint is **nightly-only** and **warns by default**. It detects links which +could use the "automatic" link syntax. For example: + +```rust +/// http://example.org +/// [http://example.com](http://example.com) +/// [http://example.net] +/// +/// [http://example.com]: http://example.com +pub fn foo() {} +``` + +Which will give: + +```text +warning: this URL is not a hyperlink + --> foo.rs:1:5 + | +1 | /// http://example.org + | ^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + | + = note: `#[warn(non_autolinks)]` on by default + +warning: unneeded long form for URL + --> foo.rs:2:5 + | +2 | /// [http://example.com](http://example.com) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + +warning: this URL is not a hyperlink + --> foo.rs:3:6 + | +3 | /// [http://example.net] + | ^^^^^^^^^^^^^^^^^^ 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/core.rs b/src/librustdoc/core.rs index 45a84c4fb30d3..5eca54199d6c8 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -330,6 +330,7 @@ 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 non_autolinks = rustc_lint::builtin::NON_AUTOLINKS.name; let unknown_lints = rustc_lint::builtin::UNKNOWN_LINTS.name; // In addition to those specific lints, we also need to allow those given through @@ -344,6 +345,7 @@ pub fn run_core( invalid_html_tags.to_owned(), renamed_and_removed_lints.to_owned(), unknown_lints.to_owned(), + non_autolinks.to_owned(), ]; let (lint_opts, lint_caps) = init_lints(lints_to_show, lint_opts, |lint| { @@ -663,7 +665,7 @@ fn run_global_ctxt( (krate, ctxt.renderinfo.into_inner(), ctxt.render_options) } -/// Due to https://github.com/rust-lang/rust/pull/73566, +/// Due to , /// the name resolution pass may find errors that are never emitted. /// If typeck is called after this happens, then we'll get an ICE: /// 'Res::Error found but not reported'. To avoid this, emit the errors now. diff --git a/src/librustdoc/passes/mod.rs b/src/librustdoc/passes/mod.rs index 2591650e3f97f..047a73835c8eb 100644 --- a/src/librustdoc/passes/mod.rs +++ b/src/librustdoc/passes/mod.rs @@ -11,6 +11,9 @@ use crate::core::DocContext; mod stripper; pub use stripper::*; +mod non_autolinks; +pub use self::non_autolinks::CHECK_NON_AUTOLINKS; + mod collapse_docs; pub use self::collapse_docs::COLLAPSE_DOCS; @@ -90,6 +93,7 @@ pub const PASSES: &[Pass] = &[ COLLECT_TRAIT_IMPLS, CALCULATE_DOC_COVERAGE, CHECK_INVALID_HTML_TAGS, + CHECK_NON_AUTOLINKS, ]; /// The list of passes run by default. @@ -105,6 +109,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_NON_AUTOLINKS), ]; /// The list of default passes run when `--doc-coverage` is passed to rustdoc. diff --git a/src/librustdoc/passes/non_autolinks.rs b/src/librustdoc/passes/non_autolinks.rs new file mode 100644 index 0000000000000..4a8fc7fc6181e --- /dev/null +++ b/src/librustdoc/passes/non_autolinks.rs @@ -0,0 +1,139 @@ +use super::{span_of_attrs, Pass}; +use crate::clean::*; +use crate::core::DocContext; +use crate::fold::DocFolder; +use crate::html::markdown::opts; +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_NON_AUTOLINKS: Pass = Pass { + name: "check-non-autolinks", + run: check_non_autolinks, + description: "detects URLS that could be written using angle brackets", +}; + +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,63}", // root domain + r"\b([-a-zA-Z0-9@:%_\+.~#?&/=]*)" // optional query or url fragments +); + +struct NonAutolinksLinter<'a, 'tcx> { + cx: &'a DocContext<'tcx>, + regex: Regex, +} + +impl<'a, 'tcx> NonAutolinksLinter<'a, 'tcx> { + fn new(cx: &'a DocContext<'tcx>) -> Self { + Self { 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 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 }, + ); + } + } +} + +pub fn check_non_autolinks(krate: Crate, cx: &DocContext<'_>) -> Crate { + if !UnstableFeatures::from_environment().is_nightly_build() { + krate + } else { + let mut coll = NonAutolinksLinter::new(cx); + + coll.fold_crate(krate) + } +} + +impl<'a, 'tcx> DocFolder for NonAutolinksLinter<'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 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::NON_AUTOLINKS, hir_id, sp, |lint| { + lint.build(msg) + .span_suggestion( + sp, + "use an automatic link instead", + format!("<{}>", url), + Applicability::MachineApplicable, + ) + .emit() + }); + }; + + let mut p = Parser::new_ext(&dox, opts()).into_offset_iter(); + + while let Some((event, range)) = p.next() { + match event { + Event::Start(Tag::Link(kind, _, _)) => { + let ignore = matches!(kind, LinkType::Autolink | LinkType::Email); + let mut title = String::new(); + + while let Some((event, range)) = p.next() { + match event { + Event::End(Tag::Link(_, url, _)) => { + // NOTE: links cannot be nested, so we don't need to + // check `kind` + if url.as_ref() == title && !ignore && self.regex.is_match(&url) + { + report_diag( + self.cx, + "unneeded long form for URL", + &url, + range, + ); + } + break; + } + Event::Text(s) if !ignore => title.push_str(&s), + _ => {} + } + } + } + Event::Text(s) => self.find_raw_urls(&s, range, &report_diag), + Event::Start(Tag::CodeBlock(_)) => { + // We don't want to check the text inside the code blocks. + while let Some((event, _)) = p.next() { + match event { + Event::End(Tag::CodeBlock(_)) => break, + _ => {} + } + } + } + _ => {} + } + } + } + + self.fold_item_recur(item) + } +} diff --git a/src/test/rustdoc-ui/url-improvements.rs b/src/test/rustdoc-ui/url-improvements.rs new file mode 100644 index 0000000000000..8531583d38a65 --- /dev/null +++ b/src/test/rustdoc-ui/url-improvements.rs @@ -0,0 +1,66 @@ +#![deny(non_autolinks)] + +/// [http://aa.com](http://aa.com) +//~^ ERROR unneeded long form for URL +/// [http://bb.com] +//~^ ERROR unneeded long form for URL +/// +/// [http://bb.com]: http://bb.com +/// +/// [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 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() {} + +/// +/// [a](http://a.com) +/// [b] +/// +/// [b]: http://b.com +/// +/// ``` +/// This link should not be linted: http://example.com +/// ``` +/// +/// [should_not.lint](should_not.lint) +pub fn everything_is_fine_here() {} + +#[allow(non_autolinks)] +pub mod foo { + /// https://somewhere.com/a?hello=12&bye=11#xyz + pub fn bar() {} +} diff --git a/src/test/rustdoc-ui/url-improvements.stderr b/src/test/rustdoc-ui/url-improvements.stderr new file mode 100644 index 0000000000000..70ad4b06a515d --- /dev/null +++ b/src/test/rustdoc-ui/url-improvements.stderr @@ -0,0 +1,122 @@ +error: unneeded long form for URL + --> $DIR/url-improvements.rs:3:5 + | +LL | /// [http://aa.com](http://aa.com) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + | +note: the lint level is defined here + --> $DIR/url-improvements.rs:1:9 + | +LL | #![deny(non_autolinks)] + | ^^^^^^^^^^^^^ + +error: unneeded long form for URL + --> $DIR/url-improvements.rs:5:5 + | +LL | /// [http://bb.com] + | ^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + +error: this URL is not a hyperlink + --> $DIR/url-improvements.rs:13:5 + | +LL | /// https://somewhere.com + | ^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + +error: this URL is not a hyperlink + --> $DIR/url-improvements.rs:15:5 + | +LL | /// https://somewhere.com/a + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + +error: this URL is not a hyperlink + --> $DIR/url-improvements.rs:17:5 + | +LL | /// https://www.somewhere.com + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + +error: this URL is not a hyperlink + --> $DIR/url-improvements.rs:19:5 + | +LL | /// https://www.somewhere.com/a + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + +error: this URL is not a hyperlink + --> $DIR/url-improvements.rs:21:5 + | +LL | /// https://subdomain.example.com + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + +error: this URL is not a hyperlink + --> $DIR/url-improvements.rs:23:5 + | +LL | /// https://somewhere.com? + | ^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + +error: this URL is not a hyperlink + --> $DIR/url-improvements.rs:25:5 + | +LL | /// https://somewhere.com/a? + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + +error: this URL is not a hyperlink + --> $DIR/url-improvements.rs:27:5 + | +LL | /// https://somewhere.com?hello=12 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + +error: this URL is not a hyperlink + --> $DIR/url-improvements.rs:29:5 + | +LL | /// https://somewhere.com/a?hello=12 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + +error: this URL is not a hyperlink + --> $DIR/url-improvements.rs:31:5 + | +LL | /// https://example.com?hello=12#xyz + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + +error: this URL is not a hyperlink + --> $DIR/url-improvements.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/url-improvements.rs:35:5 + | +LL | /// https://example.com#xyz + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + +error: this URL is not a hyperlink + --> $DIR/url-improvements.rs:37:5 + | +LL | /// https://example.com/a#xyz + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + +error: this URL is not a hyperlink + --> $DIR/url-improvements.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/url-improvements.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/url-improvements.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/url-improvements.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 +