Allow generic parameters in intra-doc links

The contents of the generics will be mostly ignored (except for warning
if fully-qualified syntax is used, which is currently unsupported in
intra-doc links - see issue #74563).

* Allow links like `Vec<T>`, `Result<T, E>`, and `Option<Box<T>>`
* Allow links like `Vec::<T>::new()`
* Warn on
  * Unbalanced angle brackets (e.g. `Vec<T` or `Vec<T>>`)
  * Missing type to apply generics to (`<T>` or `<Box<T>>`)
  * Use of fully-qualified syntax (`<Vec as IntoIterator>::into_iter`)
  * Invalid path separator (`Vec:<T>:new`)
  * Too many angle brackets (`Vec<<T>>`)
  * Empty angle brackets (`Vec<>`)

Note that this implementation *does* allow some constructs that aren't
valid in the actual Rust syntax, for example `Box::<T>new()`. That may
not be supported in rustdoc in the future; it is an implementation
detail.
This commit is contained in:
Camelid 2020-10-08 22:24:34 -07:00
parent 9ba1d21868
commit 4c765f66a4
6 changed files with 381 additions and 2 deletions

View file

@ -2,7 +2,7 @@
//~^ NOTE lint level is defined
// FIXME: this should say that it was skipped (maybe an allowed by default lint?)
/// [<invalid syntax>]
/// [invalid intra-doc syntax!!]
/// [path::to::nonexistent::module]
//~^ ERROR unresolved link

View file

@ -0,0 +1,19 @@
#![deny(broken_intra_doc_links)]
//! [Vec<] //~ ERROR
//! [Vec<Box<T] //~ ERROR
//! [Vec<Box<T>] //~ ERROR
//! [Vec<Box<T>>>] //~ ERROR
//! [Vec<T>>>] //~ ERROR
//! [<Vec] //~ ERROR
//! [Vec::<] //~ ERROR
//! [<T>] //~ ERROR
//! [<invalid syntax>] //~ ERROR
//! [Vec:<T>:new()] //~ ERROR
//! [Vec<<T>>] //~ ERROR
//! [Vec<>] //~ ERROR
//! [Vec<<>>] //~ ERROR
// FIXME(#74563) support UFCS
//! [<Vec as IntoIterator>::into_iter] //~ ERROR
//! [<Vec<T> as IntoIterator>::iter] //~ ERROR

View file

@ -0,0 +1,102 @@
error: unresolved link to `Vec<`
--> $DIR/intra-link-malformed-generics.rs:3:6
|
LL | //! [Vec<]
| ^^^^ unbalanced angle brackets
|
note: the lint level is defined here
--> $DIR/intra-link-malformed-generics.rs:1:9
|
LL | #![deny(broken_intra_doc_links)]
| ^^^^^^^^^^^^^^^^^^^^^^
error: unresolved link to `Vec<Box<T`
--> $DIR/intra-link-malformed-generics.rs:4:6
|
LL | //! [Vec<Box<T]
| ^^^^^^^^^ unbalanced angle brackets
error: unresolved link to `Vec<Box<T>`
--> $DIR/intra-link-malformed-generics.rs:5:6
|
LL | //! [Vec<Box<T>]
| ^^^^^^^^^^ unbalanced angle brackets
error: unresolved link to `Vec<Box<T>>>`
--> $DIR/intra-link-malformed-generics.rs:6:6
|
LL | //! [Vec<Box<T>>>]
| ^^^^^^^^^^^^ unbalanced angle brackets
error: unresolved link to `Vec<T>>>`
--> $DIR/intra-link-malformed-generics.rs:7:6
|
LL | //! [Vec<T>>>]
| ^^^^^^^^ unbalanced angle brackets
error: unresolved link to `<Vec`
--> $DIR/intra-link-malformed-generics.rs:8:6
|
LL | //! [<Vec]
| ^^^^ unbalanced angle brackets
error: unresolved link to `Vec::<`
--> $DIR/intra-link-malformed-generics.rs:9:6
|
LL | //! [Vec::<]
| ^^^^^^ unbalanced angle brackets
error: unresolved link to `<T>`
--> $DIR/intra-link-malformed-generics.rs:10:6
|
LL | //! [<T>]
| ^^^ missing type for generic parameters
error: unresolved link to `<invalid syntax>`
--> $DIR/intra-link-malformed-generics.rs:11:6
|
LL | //! [<invalid syntax>]
| ^^^^^^^^^^^^^^^^ missing type for generic parameters
error: unresolved link to `Vec:<T>:new`
--> $DIR/intra-link-malformed-generics.rs:12:6
|
LL | //! [Vec:<T>:new()]
| ^^^^^^^^^^^^^ has invalid path separator
error: unresolved link to `Vec<<T>>`
--> $DIR/intra-link-malformed-generics.rs:13:6
|
LL | //! [Vec<<T>>]
| ^^^^^^^^ too many angle brackets
error: unresolved link to `Vec<>`
--> $DIR/intra-link-malformed-generics.rs:14:6
|
LL | //! [Vec<>]
| ^^^^^ empty angle brackets
error: unresolved link to `Vec<<>>`
--> $DIR/intra-link-malformed-generics.rs:15:6
|
LL | //! [Vec<<>>]
| ^^^^^^^ too many angle brackets
error: unresolved link to `<Vec as IntoIterator>::into_iter`
--> $DIR/intra-link-malformed-generics.rs:18:6
|
LL | //! [<Vec as IntoIterator>::into_iter]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ fully-qualified syntax is unsupported
|
= note: see https://github.com/rust-lang/rust/issues/74563 for more information
error: unresolved link to `<Vec<T> as IntoIterator>::iter`
--> $DIR/intra-link-malformed-generics.rs:19:6
|
LL | //! [<Vec<T> as IntoIterator>::iter]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ fully-qualified syntax is unsupported
|
= note: see https://github.com/rust-lang/rust/issues/74563 for more information
error: aborting due to 15 previous errors

View file

@ -0,0 +1,55 @@
// ignore-tidy-linelength
#![crate_name = "foo"]
//! Here's a link to [`Vec<T>`] and one to [`Box<Vec<Option<T>>>`].
//! Here's a link to [`Iterator<Box<T>>::Item`].
//!
//! And what about a link to [just `Option`](Option) and, [with the generic, `Option<T>`](Option<T>)?
//!
//! We should also try linking to [`Result<T, E>`]; it has *two* generics!
//!
//! Now let's test a trickier case: [`Vec::<T>::new`], or you could write it
//! [with parentheses as `Vec::<T>::new()`][Vec::<T>::new()].
//! And what about something even harder? That would be [`Vec::<Box<T>>::new()`].
//!
//! This is also pretty tricky: [`TypeId::of::<String>()`].
//! And this too: [`Vec::<std::error::Error>::len`].
//!
//! We unofficially and implicitly support things that aren't valid in the actual Rust syntax, like
//! [`Box::<T>new()`]. We may not support them in the future!
//!
//! These will be resolved as regular links:
//! - [`this is <invalid syntax> first`](https://www.rust-lang.org)
//! - [`this is <invalid syntax> twice`]
//! - [`<invalid syntax> thrice`](https://www.rust-lang.org)
//! - [`<invalid syntax> four times`][rlo]
//! - [a < b][rlo]
//! - [c > d]
//!
//! [`this is <invalid syntax> twice`]: https://www.rust-lang.org
//! [rlo]: https://www.rust-lang.org
//! [c > d]: https://www.rust-lang.org
// @has foo/index.html '//a[@href="https://doc.rust-lang.org/nightly/alloc/vec/struct.Vec.html"]' 'Vec<T>'
// @has foo/index.html '//a[@href="https://doc.rust-lang.org/nightly/alloc/boxed/struct.Box.html"]' 'Box<Vec<Option<T>>>'
// @has foo/index.html '//a[@href="https://doc.rust-lang.org/nightly/core/iter/traits/iterator/trait.Iterator.html#associatedtype.Item"]' 'Iterator<Box<T>>::Item'
// @has foo/index.html '//a[@href="https://doc.rust-lang.org/nightly/core/option/enum.Option.html"]' 'just Option'
// @has foo/index.html '//a[@href="https://doc.rust-lang.org/nightly/core/option/enum.Option.html"]' 'with the generic, Option<T>'
// @has foo/index.html '//a[@href="https://doc.rust-lang.org/nightly/core/result/enum.Result.html"]' 'Result<T, E>'
// @has foo/index.html '//a[@href="https://doc.rust-lang.org/nightly/alloc/vec/struct.Vec.html#method.new"]' 'Vec::<T>::new'
// @has foo/index.html '//a[@href="https://doc.rust-lang.org/nightly/alloc/vec/struct.Vec.html#method.new"]' 'with parentheses as Vec::<T>::new()'
// @has foo/index.html '//a[@href="https://doc.rust-lang.org/nightly/alloc/vec/struct.Vec.html#method.new"]' 'Vec::<Box<T>>::new()'
// @has foo/index.html '//a[@href="https://doc.rust-lang.org/nightly/core/any/struct.TypeId.html#method.of"]' 'TypeId::of::<String>()'
// @has foo/index.html '//a[@href="https://doc.rust-lang.org/nightly/alloc/vec/struct.Vec.html#method.len"]' 'Vec::<std::error::Error>::len'
// @has foo/index.html '//a[@href="https://doc.rust-lang.org/nightly/alloc/boxed/struct.Box.html#method.new"]' 'Box::<T>new()'
// @has foo/index.html '//a[@href="https://www.rust-lang.org"]' 'this is <invalid syntax> first'
// @has foo/index.html '//a[@href="https://www.rust-lang.org"]' 'this is <invalid syntax> twice'
// @has foo/index.html '//a[@href="https://www.rust-lang.org"]' '<invalid syntax> thrice'
// @has foo/index.html '//a[@href="https://www.rust-lang.org"]' '<invalid syntax> four times'
// @has foo/index.html '//a[@href="https://www.rust-lang.org"]' 'a < b'
// @has foo/index.html '//a[@href="https://www.rust-lang.org"]' 'c > d'
use std::any::TypeId;