From 285b9d1cd4e170f6a6c98cbdf5f5059b52735d01 Mon Sep 17 00:00:00 2001
From: Michael Goulet
Date: Sun, 10 Apr 2022 20:55:10 -0700
Subject: [PATCH 01/23] Delay a bug when we see SelfCtor in ref pattern
---
compiler/rustc_resolve/src/late.rs | 9 +++++++++
src/test/ui/pattern/issue-95878.rs | 12 ++++++++++++
src/test/ui/pattern/issue-95878.stderr | 8 ++++++++
3 files changed, 29 insertions(+)
create mode 100644 src/test/ui/pattern/issue-95878.rs
create mode 100644 src/test/ui/pattern/issue-95878.stderr
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index 6fedabc816cff..0fdcc164f0296 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -1870,6 +1870,15 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
// These entities are explicitly allowed to be shadowed by fresh bindings.
None
}
+ Res::SelfCtor(_) => {
+ // We resolve `Self` in pattern position as an ident sometimes during recovery,
+ // so delay a bug instead of ICEing.
+ self.r.session.delay_span_bug(
+ ident.span,
+ "unexpected `SelfCtor` in pattern, expected identifier"
+ );
+ None
+ }
_ => span_bug!(
ident.span,
"unexpected resolution for an identifier in pattern: {:?}",
diff --git a/src/test/ui/pattern/issue-95878.rs b/src/test/ui/pattern/issue-95878.rs
new file mode 100644
index 0000000000000..f59814468b2f1
--- /dev/null
+++ b/src/test/ui/pattern/issue-95878.rs
@@ -0,0 +1,12 @@
+struct Foo<'a>(&'a ());
+
+impl<'a> Foo<'a> {
+ fn spam(&mut self, baz: &mut Vec) {
+ match 15 {
+ ref Self => (),
+ //~^ ERROR expected identifier, found keyword `Self`
+ }
+ }
+}
+
+fn main() {}
diff --git a/src/test/ui/pattern/issue-95878.stderr b/src/test/ui/pattern/issue-95878.stderr
new file mode 100644
index 0000000000000..e0eea06e0a3c3
--- /dev/null
+++ b/src/test/ui/pattern/issue-95878.stderr
@@ -0,0 +1,8 @@
+error: expected identifier, found keyword `Self`
+ --> $DIR/issue-95878.rs:6:17
+ |
+LL | ref Self => (),
+ | ^^^^ expected identifier, found keyword
+
+error: aborting due to previous error
+
From 7d5bbf55f22c91f966a0feb130b5ff476da4f017 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?R=C3=A9my=20Rakic?=
Date: Thu, 22 Jul 2021 20:21:48 +0200
Subject: [PATCH 02/23] prevent opaque types from appearing in impl headers
---
compiler/rustc_typeck/src/coherence/orphan.rs | 23 +++++++++++------
src/test/ui/impl-trait/auto-trait.rs | 1 +
src/test/ui/impl-trait/auto-trait.stderr | 14 ++++++++++-
src/test/ui/impl-trait/negative-reasoning.rs | 1 +
.../ui/impl-trait/negative-reasoning.stderr | 14 ++++++++++-
...ias-impl-trait-declaration-too-subtle-2.rs | 3 ++-
...impl-trait-declaration-too-subtle-2.stderr | 16 ++++++------
...alias-impl-trait-declaration-too-subtle.rs | 4 +--
...s-impl-trait-declaration-too-subtle.stderr | 25 ++++++-------------
.../nested-tait-inference3.rs | 2 +-
.../nested-tait-inference3.stderr | 10 +++++---
11 files changed, 71 insertions(+), 42 deletions(-)
diff --git a/compiler/rustc_typeck/src/coherence/orphan.rs b/compiler/rustc_typeck/src/coherence/orphan.rs
index 4b23cc4db85ba..564c3d31368b6 100644
--- a/compiler/rustc_typeck/src/coherence/orphan.rs
+++ b/compiler/rustc_typeck/src/coherence/orphan.rs
@@ -141,13 +141,22 @@ fn orphan_check_impl(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), ErrorGua
}
}
- if let ty::Opaque(def_id, _) = *trait_ref.self_ty().kind() {
- let reported = tcx
- .sess
- .struct_span_err(sp, "cannot implement trait on type alias impl trait")
- .span_note(tcx.def_span(def_id), "type alias impl trait defined here")
- .emit();
- return Err(reported);
+ // Ensure no opaque types are present in this impl header. See issues #76202 and #86411 for examples,
+ // and #84660 where it would otherwise allow unsoundness.
+ if trait_ref.has_opaque_types() {
+ for ty in trait_ref.substs {
+ for ty in ty.walk() {
+ let ty::subst::GenericArgKind::Type(ty) = ty.unpack() else { continue };
+ let ty::Opaque(def_id, _) = ty.kind() else { continue };
+ let reported = tcx
+ .sess
+ .struct_span_err(sp, "cannot implement trait on type alias impl trait")
+ .span_note(tcx.def_span(def_id), "type alias impl trait defined here")
+ .emit();
+ return Err(reported);
+ }
+ }
+ span_bug!(sp, "opque type not found, but `has_opaque_types` is set")
}
Ok(())
diff --git a/src/test/ui/impl-trait/auto-trait.rs b/src/test/ui/impl-trait/auto-trait.rs
index 35994e4a5ba3f..afa95645a2786 100644
--- a/src/test/ui/impl-trait/auto-trait.rs
+++ b/src/test/ui/impl-trait/auto-trait.rs
@@ -20,6 +20,7 @@ impl AnotherTrait for T {}
// in the future.)
impl AnotherTrait for D {
//~^ ERROR conflicting implementations of trait `AnotherTrait` for type `D`
+ //~| ERROR cannot implement trait on type alias impl trait
}
fn main() {}
diff --git a/src/test/ui/impl-trait/auto-trait.stderr b/src/test/ui/impl-trait/auto-trait.stderr
index 81009413c9a26..9171cfca5b146 100644
--- a/src/test/ui/impl-trait/auto-trait.stderr
+++ b/src/test/ui/impl-trait/auto-trait.stderr
@@ -1,3 +1,15 @@
+error: cannot implement trait on type alias impl trait
+ --> $DIR/auto-trait.rs:21:1
+ |
+LL | impl AnotherTrait for D {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: type alias impl trait defined here
+ --> $DIR/auto-trait.rs:7:19
+ |
+LL | type OpaqueType = impl OpaqueTrait;
+ | ^^^^^^^^^^^^^^^^
+
error[E0119]: conflicting implementations of trait `AnotherTrait` for type `D`
--> $DIR/auto-trait.rs:21:1
|
@@ -7,6 +19,6 @@ LL | impl AnotherTrait for T {}
LL | impl AnotherTrait for D {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `D`
-error: aborting due to previous error
+error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0119`.
diff --git a/src/test/ui/impl-trait/negative-reasoning.rs b/src/test/ui/impl-trait/negative-reasoning.rs
index 70e24a3a9d029..da69bb349ae24 100644
--- a/src/test/ui/impl-trait/negative-reasoning.rs
+++ b/src/test/ui/impl-trait/negative-reasoning.rs
@@ -18,6 +18,7 @@ impl AnotherTrait for T {}
// This is in error, because we cannot assume that `OpaqueType: !Debug`
impl AnotherTrait for D {
//~^ ERROR conflicting implementations of trait `AnotherTrait` for type `D`
+ //~| ERROR cannot implement trait on type alias impl trait
}
fn main() {}
diff --git a/src/test/ui/impl-trait/negative-reasoning.stderr b/src/test/ui/impl-trait/negative-reasoning.stderr
index 6b8cc9e737423..1a7233bca5caf 100644
--- a/src/test/ui/impl-trait/negative-reasoning.stderr
+++ b/src/test/ui/impl-trait/negative-reasoning.stderr
@@ -1,3 +1,15 @@
+error: cannot implement trait on type alias impl trait
+ --> $DIR/negative-reasoning.rs:19:1
+ |
+LL | impl AnotherTrait for D {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: type alias impl trait defined here
+ --> $DIR/negative-reasoning.rs:7:19
+ |
+LL | type OpaqueType = impl OpaqueTrait;
+ | ^^^^^^^^^^^^^^^^
+
error[E0119]: conflicting implementations of trait `AnotherTrait` for type `D`
--> $DIR/negative-reasoning.rs:19:1
|
@@ -9,6 +21,6 @@ LL | impl AnotherTrait for D {
|
= note: upstream crates may add a new impl of trait `std::fmt::Debug` for type `OpaqueType` in future versions
-error: aborting due to previous error
+error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0119`.
diff --git a/src/test/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle-2.rs b/src/test/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle-2.rs
index 6b200d7e3a89e..621c4ea6e0d48 100644
--- a/src/test/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle-2.rs
+++ b/src/test/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle-2.rs
@@ -5,13 +5,14 @@ type Foo = impl PartialEq<(Foo, i32)>;
struct Bar;
impl PartialEq<(Foo, i32)> for Bar {
+//~^ ERROR cannot implement trait on type alias impl trait
fn eq(&self, _other: &(Foo, i32)) -> bool {
true
}
}
fn foo() -> Foo {
- Bar //~ ERROR can't compare `Bar` with `(Bar, i32)`
+ Bar
}
fn main() {}
diff --git a/src/test/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle-2.stderr b/src/test/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle-2.stderr
index 6cd63db44fa07..4e8d4cce0a17f 100644
--- a/src/test/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle-2.stderr
+++ b/src/test/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle-2.stderr
@@ -1,12 +1,14 @@
-error[E0277]: can't compare `Bar` with `(Bar, i32)`
- --> $DIR/recursive-type-alias-impl-trait-declaration-too-subtle-2.rs:14:5
+error: cannot implement trait on type alias impl trait
+ --> $DIR/recursive-type-alias-impl-trait-declaration-too-subtle-2.rs:7:1
|
-LL | Bar
- | ^^^ no implementation for `Bar == (Bar, i32)`
+LL | impl PartialEq<(Foo, i32)> for Bar {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = help: the trait `PartialEq<(Bar, i32)>` is not implemented for `Bar`
- = help: the trait `PartialEq<(Foo, i32)>` is implemented for `Bar`
+note: type alias impl trait defined here
+ --> $DIR/recursive-type-alias-impl-trait-declaration-too-subtle-2.rs:3:12
+ |
+LL | type Foo = impl PartialEq<(Foo, i32)>;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error
-For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle.rs b/src/test/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle.rs
index 6aa832cde71ee..df7966f00e172 100644
--- a/src/test/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle.rs
+++ b/src/test/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle.rs
@@ -2,7 +2,6 @@
mod a {
type Foo = impl PartialEq<(Foo, i32)>;
- //~^ ERROR unconstrained opaque type
struct Bar;
@@ -15,13 +14,12 @@ mod a {
mod b {
type Foo = impl PartialEq<(Foo, i32)>;
- //~^ ERROR unconstrained opaque type
struct Bar;
impl PartialEq<(Foo, i32)> for Bar {
+ //~^ ERROR cannot implement trait on type alias impl trait
fn eq(&self, _other: &(Bar, i32)) -> bool {
- //~^ ERROR impl has stricter requirements than trait
true
}
}
diff --git a/src/test/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle.stderr b/src/test/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle.stderr
index 19d5cdb9d0ac6..ccb0bc0a366df 100644
--- a/src/test/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle.stderr
+++ b/src/test/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle.stderr
@@ -1,25 +1,14 @@
-error: unconstrained opaque type
- --> $DIR/recursive-type-alias-impl-trait-declaration-too-subtle.rs:4:16
+error: cannot implement trait on type alias impl trait
+ --> $DIR/recursive-type-alias-impl-trait-declaration-too-subtle.rs:20:5
|
-LL | type Foo = impl PartialEq<(Foo, i32)>;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | impl PartialEq<(Foo, i32)> for Bar {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = note: `Foo` must be used in combination with a concrete type within the same module
-
-error: unconstrained opaque type
- --> $DIR/recursive-type-alias-impl-trait-declaration-too-subtle.rs:17:16
+note: type alias impl trait defined here
+ --> $DIR/recursive-type-alias-impl-trait-declaration-too-subtle.rs:16:16
|
LL | type Foo = impl PartialEq<(Foo, i32)>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: `Foo` must be used in combination with a concrete type within the same module
-
-error[E0276]: impl has stricter requirements than trait
- --> $DIR/recursive-type-alias-impl-trait-declaration-too-subtle.rs:23:9
- |
-LL | fn eq(&self, _other: &(Bar, i32)) -> bool {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `b::Bar: PartialEq<(b::Bar, i32)>`
-error: aborting due to 3 previous errors
+error: aborting due to previous error
-For more information about this error, try `rustc --explain E0276`.
diff --git a/src/test/ui/type-alias-impl-trait/nested-tait-inference3.rs b/src/test/ui/type-alias-impl-trait/nested-tait-inference3.rs
index fbab5470b4f6d..ebf3a99bbf9f0 100644
--- a/src/test/ui/type-alias-impl-trait/nested-tait-inference3.rs
+++ b/src/test/ui/type-alias-impl-trait/nested-tait-inference3.rs
@@ -4,11 +4,11 @@
use std::fmt::Debug;
type FooX = impl Debug;
-//~^ unconstrained opaque type
trait Foo { }
impl Foo for () { }
+//~^ cannot implement trait on type alias impl trait
fn foo() -> impl Foo {
()
diff --git a/src/test/ui/type-alias-impl-trait/nested-tait-inference3.stderr b/src/test/ui/type-alias-impl-trait/nested-tait-inference3.stderr
index b1d947a9ccf4e..bf1582639a2ca 100644
--- a/src/test/ui/type-alias-impl-trait/nested-tait-inference3.stderr
+++ b/src/test/ui/type-alias-impl-trait/nested-tait-inference3.stderr
@@ -1,10 +1,14 @@
-error: unconstrained opaque type
+error: cannot implement trait on type alias impl trait
+ --> $DIR/nested-tait-inference3.rs:10:1
+ |
+LL | impl Foo for () { }
+ | ^^^^^^^^^^^^^^^^^^^^^
+ |
+note: type alias impl trait defined here
--> $DIR/nested-tait-inference3.rs:6:13
|
LL | type FooX = impl Debug;
| ^^^^^^^^^^
- |
- = note: `FooX` must be used in combination with a concrete type within the same module
error: aborting due to previous error
From d652cd4959d13e2b33929dd6f5aeeb1e94e63c31 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?R=C3=A9my=20Rakic?=
Date: Thu, 22 Jul 2021 20:26:46 +0200
Subject: [PATCH 03/23] add regression tests for opaque types in impl headers
---
.../issue-84660-trait-impl-for-tait.rs | 23 +++++++++++
.../issue-84660-trait-impl-for-tait.stderr | 14 +++++++
.../issue-84660-unsoundness.rs | 41 +++++++++++++++++++
.../issue-84660-unsoundness.stderr | 14 +++++++
4 files changed, 92 insertions(+)
create mode 100644 src/test/ui/type-alias-impl-trait/issue-84660-trait-impl-for-tait.rs
create mode 100644 src/test/ui/type-alias-impl-trait/issue-84660-trait-impl-for-tait.stderr
create mode 100644 src/test/ui/type-alias-impl-trait/issue-84660-unsoundness.rs
create mode 100644 src/test/ui/type-alias-impl-trait/issue-84660-unsoundness.stderr
diff --git a/src/test/ui/type-alias-impl-trait/issue-84660-trait-impl-for-tait.rs b/src/test/ui/type-alias-impl-trait/issue-84660-trait-impl-for-tait.rs
new file mode 100644
index 0000000000000..1a0acca7881d1
--- /dev/null
+++ b/src/test/ui/type-alias-impl-trait/issue-84660-trait-impl-for-tait.rs
@@ -0,0 +1,23 @@
+// Regression test for issues #84660 and #86411: both are variations on #76202.
+// Tests that we don't ICE when we have an opaque type appearing anywhere in an impl header.
+
+#![feature(min_type_alias_impl_trait)]
+
+trait Foo {}
+impl Foo for () {}
+type Bar = impl Foo;
+fn _defining_use() -> Bar {}
+
+trait TraitArg {
+ fn f();
+}
+
+impl TraitArg for () { //~ ERROR cannot implement trait
+ fn f() {
+ println!("ho");
+ }
+}
+
+fn main() {
+ <() as TraitArg>::f();
+}
diff --git a/src/test/ui/type-alias-impl-trait/issue-84660-trait-impl-for-tait.stderr b/src/test/ui/type-alias-impl-trait/issue-84660-trait-impl-for-tait.stderr
new file mode 100644
index 0000000000000..1b8eee407178c
--- /dev/null
+++ b/src/test/ui/type-alias-impl-trait/issue-84660-trait-impl-for-tait.stderr
@@ -0,0 +1,14 @@
+error: cannot implement trait on type alias impl trait
+ --> $DIR/issue-84660-trait-impl-for-tait.rs:15:1
+ |
+LL | impl TraitArg for () {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: type alias impl trait defined here
+ --> $DIR/issue-84660-trait-impl-for-tait.rs:8:12
+ |
+LL | type Bar = impl Foo;
+ | ^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/type-alias-impl-trait/issue-84660-unsoundness.rs b/src/test/ui/type-alias-impl-trait/issue-84660-unsoundness.rs
new file mode 100644
index 0000000000000..18213dfbe5bf7
--- /dev/null
+++ b/src/test/ui/type-alias-impl-trait/issue-84660-unsoundness.rs
@@ -0,0 +1,41 @@
+// Another example from issue #84660, this time weaponized as a safe transmut: an opaque type in an
+// impl header being accepted was used to create unsoundness.
+
+#![feature(min_type_alias_impl_trait)]
+
+trait Foo {}
+impl Foo for () {}
+type Bar = impl Foo;
+fn _defining_use() -> Bar {}
+
+trait Trait {
+ type Out;
+ fn convert(i: In) -> Self::Out;
+}
+
+impl Trait for Out { //~ ERROR cannot implement trait
+ type Out = Out;
+ fn convert(_i: In) -> Self::Out {
+ unreachable!();
+ }
+}
+
+impl Trait<(), In> for Out {
+ type Out = In;
+ fn convert(i: In) -> Self::Out {
+ i
+ }
+}
+
+fn transmute(i: In) -> Out {
+ >::convert(i)
+}
+
+fn main() {
+ let d;
+ {
+ let x = "Hello World".to_string();
+ d = transmute::<&String, &String>(&x);
+ }
+ println!("{}", d);
+}
diff --git a/src/test/ui/type-alias-impl-trait/issue-84660-unsoundness.stderr b/src/test/ui/type-alias-impl-trait/issue-84660-unsoundness.stderr
new file mode 100644
index 0000000000000..b1128d830f967
--- /dev/null
+++ b/src/test/ui/type-alias-impl-trait/issue-84660-unsoundness.stderr
@@ -0,0 +1,14 @@
+error: cannot implement trait on type alias impl trait
+ --> $DIR/issue-84660-unsoundness.rs:16:1
+ |
+LL | impl Trait for Out {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: type alias impl trait defined here
+ --> $DIR/issue-84660-unsoundness.rs:8:12
+ |
+LL | type Bar = impl Foo;
+ | ^^^^^^^^
+
+error: aborting due to previous error
+
From 734adb3a1a8890863826fd1ce75823d104e33a64 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?R=C3=A9my=20Rakic?=
Date: Thu, 22 Jul 2021 20:31:21 +0200
Subject: [PATCH 04/23] update ui tests using opaque types in impl headers
---
.../ui/type-alias-impl-trait/issue-84660-trait-impl-for-tait.rs | 2 +-
src/test/ui/type-alias-impl-trait/issue-84660-unsoundness.rs | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/test/ui/type-alias-impl-trait/issue-84660-trait-impl-for-tait.rs b/src/test/ui/type-alias-impl-trait/issue-84660-trait-impl-for-tait.rs
index 1a0acca7881d1..fa25d8f762e6c 100644
--- a/src/test/ui/type-alias-impl-trait/issue-84660-trait-impl-for-tait.rs
+++ b/src/test/ui/type-alias-impl-trait/issue-84660-trait-impl-for-tait.rs
@@ -1,7 +1,7 @@
// Regression test for issues #84660 and #86411: both are variations on #76202.
// Tests that we don't ICE when we have an opaque type appearing anywhere in an impl header.
-#![feature(min_type_alias_impl_trait)]
+#![feature(type_alias_impl_trait)]
trait Foo {}
impl Foo for () {}
diff --git a/src/test/ui/type-alias-impl-trait/issue-84660-unsoundness.rs b/src/test/ui/type-alias-impl-trait/issue-84660-unsoundness.rs
index 18213dfbe5bf7..ee6eb605c5be9 100644
--- a/src/test/ui/type-alias-impl-trait/issue-84660-unsoundness.rs
+++ b/src/test/ui/type-alias-impl-trait/issue-84660-unsoundness.rs
@@ -1,7 +1,7 @@
// Another example from issue #84660, this time weaponized as a safe transmut: an opaque type in an
// impl header being accepted was used to create unsoundness.
-#![feature(min_type_alias_impl_trait)]
+#![feature(type_alias_impl_trait)]
trait Foo {}
impl Foo for () {}
From de2edb226b213c414546b4e739356f0524b871f6 Mon Sep 17 00:00:00 2001
From: Maybe Waffle
Date: Tue, 12 Apr 2022 11:29:23 +0400
Subject: [PATCH 05/23] Fix wrong suggestions for `T:`
This commit fixes a corner case in `suggest_constraining_type_params`
that was causing incorrect suggestions.
For the following functions:
```rust
fn a(t: T) { [t, t]; }
fn b(t: T) where T: { [t, t]; }
```
We previously suggested the following:
```text
...
help: consider restricting type parameter `T`
|
1 | fn a(t: T) { [t, t]; }
| ++++++
...
help: consider further restricting this bound
|
2 | fn b(t: T) where T: + Copy { [t, t]; }
| ++++++
```
Note that neither `T: Copy:` not `where T: + Copy` is a correct bound.
With this commit the suggestions are correct:
```text
...
help: consider restricting type parameter `T`
|
1 | fn a(t: T) { [t, t]; }
| ++++
...
help: consider further restricting this bound
|
2 | fn b(t: T) where T: Copy { [t, t]; }
| ++++
```
---
compiler/rustc_hir/src/hir.rs | 36 ++++++++++++++++++++-
compiler/rustc_middle/src/ty/diagnostics.rs | 35 ++++++++++++++++----
2 files changed, 63 insertions(+), 8 deletions(-)
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 15118073b3343..76971d7ad3a05 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -17,7 +17,7 @@ use rustc_error_messages::MultiSpan;
use rustc_index::vec::IndexVec;
use rustc_macros::HashStable_Generic;
use rustc_span::hygiene::MacroKind;
-use rustc_span::source_map::Spanned;
+use rustc_span::source_map::{SourceMap, Spanned};
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::{def_id::LocalDefId, BytePos, Span, DUMMY_SP};
use rustc_target::asm::InlineAsmRegOrRegClass;
@@ -523,6 +523,40 @@ impl<'hir> GenericParam<'hir> {
})
.map(|sp| sp.shrink_to_hi())
}
+
+ /// Returns the span of `:` after a generic parameter.
+ ///
+ /// For example:
+ ///
+ /// ```text
+ /// fn a()
+ /// ^
+ /// | here
+ /// here |
+ /// v
+ /// fn b()
+ ///
+ /// fn c()
+ /// ^
+ /// |
+ /// here
+ /// ```
+ pub fn colon_span_for_suggestions(&self, source_map: &SourceMap) -> Option {
+ let sp = source_map
+ .span_extend_while(self.span.shrink_to_hi(), |c| c.is_whitespace() || c == ':')
+ .ok()?;
+
+ let snippet = source_map.span_to_snippet(sp).ok()?;
+ let offset = snippet.find(':')?;
+
+ let colon_sp = sp
+ .with_lo(BytePos(sp.lo().0 + offset as u32))
+ .with_hi(BytePos(sp.lo().0 + (offset + ':'.len_utf8()) as u32));
+
+ Some(colon_sp)
+ }
}
#[derive(Default)]
diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs
index 49d0ce5205205..3b044b19259d0 100644
--- a/compiler/rustc_middle/src/ty/diagnostics.rs
+++ b/compiler/rustc_middle/src/ty/diagnostics.rs
@@ -336,10 +336,14 @@ pub fn suggest_constraining_type_params<'a>(
}
let constraint = constraints.iter().map(|&(c, _)| c).collect::>().join(" + ");
- let mut suggest_restrict = |span| {
+ let mut suggest_restrict = |span, bound_list_non_empty| {
suggestions.push((
span,
- format!(" + {}", constraint),
+ if bound_list_non_empty {
+ format!(" + {}", constraint)
+ } else {
+ format!(" {}", constraint)
+ },
SuggestChangingConstraintsMessage::RestrictBoundFurther,
))
};
@@ -360,7 +364,10 @@ pub fn suggest_constraining_type_params<'a>(
// |
// replace with: `impl Foo + Bar`
- suggest_restrict(param.span.shrink_to_hi());
+ // `impl Trait` must have at least one trait in the list
+ let bound_list_non_empty = true;
+
+ suggest_restrict(param.span.shrink_to_hi(), bound_list_non_empty);
continue;
}
@@ -383,15 +390,25 @@ pub fn suggest_constraining_type_params<'a>(
// --
// |
// replace with: `T: Bar +`
- suggest_restrict(span);
+
+ // `bounds_span_for_suggestions` returns `None` if the list is empty
+ let bound_list_non_empty = true;
+
+ suggest_restrict(span, bound_list_non_empty);
} else {
+ let (colon, span) = match param.colon_span_for_suggestions(tcx.sess.source_map()) {
+ // If there is already a colon after generic, do not suggest adding it again
+ Some(sp) => ("", sp.shrink_to_hi()),
+ None => (":", param.span.shrink_to_hi()),
+ };
+
// If user hasn't provided any bounds, suggest adding a new one:
//
// fn foo(t: T) { ... }
// - help: consider restricting this type parameter with `T: Foo`
suggestions.push((
- param.span.shrink_to_hi(),
- format!(": {}", constraint),
+ span,
+ format!("{colon} {constraint}"),
SuggestChangingConstraintsMessage::RestrictType { ty: param_name },
));
}
@@ -459,17 +476,21 @@ pub fn suggest_constraining_type_params<'a>(
));
} else {
let mut param_spans = Vec::new();
+ let mut non_empty = false;
for predicate in generics.where_clause.predicates {
if let WherePredicate::BoundPredicate(WhereBoundPredicate {
span,
bounded_ty,
+ bounds,
..
}) = predicate
{
if let TyKind::Path(QPath::Resolved(_, path)) = &bounded_ty.kind {
if let Some(segment) = path.segments.first() {
if segment.ident.to_string() == param_name {
+ non_empty = !bounds.is_empty();
+
param_spans.push(span);
}
}
@@ -478,7 +499,7 @@ pub fn suggest_constraining_type_params<'a>(
}
match param_spans[..] {
- [¶m_span] => suggest_restrict(param_span.shrink_to_hi()),
+ [¶m_span] => suggest_restrict(param_span.shrink_to_hi(), non_empty),
_ => {
suggestions.push((
generics.where_clause.tail_span_for_suggestion(),
From e4710fe2214ba85fd9a3514c76c7722253a3a275 Mon Sep 17 00:00:00 2001
From: Maybe Waffle
Date: Tue, 12 Apr 2022 12:14:43 +0400
Subject: [PATCH 06/23] Add test for `T:` suggestions
---
.../use_of_moved_value_copy_suggestions.fixed | 14 ++++++++
.../use_of_moved_value_copy_suggestions.rs | 14 ++++++++
...use_of_moved_value_copy_suggestions.stderr | 34 ++++++++++++++++++-
3 files changed, 61 insertions(+), 1 deletion(-)
diff --git a/src/test/ui/moves/use_of_moved_value_copy_suggestions.fixed b/src/test/ui/moves/use_of_moved_value_copy_suggestions.fixed
index d31046c77006e..fb6cf7e8996c0 100644
--- a/src/test/ui/moves/use_of_moved_value_copy_suggestions.fixed
+++ b/src/test/ui/moves/use_of_moved_value_copy_suggestions.fixed
@@ -69,4 +69,18 @@ where
(t, t) //~ use of moved value: `t`
}
+#[rustfmt::skip]
+fn existing_colon(t: T) {
+ //~^ HELP consider restricting type parameter `T`
+ [t, t]; //~ use of moved value: `t`
+}
+
+fn existing_colon_in_where(t: T)
+where
+ T: Copy,
+ //~^ HELP consider further restricting this bound
+{
+ [t, t]; //~ use of moved value: `t`
+}
+
fn main() {}
diff --git a/src/test/ui/moves/use_of_moved_value_copy_suggestions.rs b/src/test/ui/moves/use_of_moved_value_copy_suggestions.rs
index 7cc5189fac017..cadbf2a54cc37 100644
--- a/src/test/ui/moves/use_of_moved_value_copy_suggestions.rs
+++ b/src/test/ui/moves/use_of_moved_value_copy_suggestions.rs
@@ -69,4 +69,18 @@ where
(t, t) //~ use of moved value: `t`
}
+#[rustfmt::skip]
+fn existing_colon(t: T) {
+ //~^ HELP consider restricting type parameter `T`
+ [t, t]; //~ use of moved value: `t`
+}
+
+fn existing_colon_in_where(t: T)
+where
+ T:,
+ //~^ HELP consider further restricting this bound
+{
+ [t, t]; //~ use of moved value: `t`
+}
+
fn main() {}
diff --git a/src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr b/src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr
index 8e72697ca30bb..f5252084d6884 100644
--- a/src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr
+++ b/src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr
@@ -142,6 +142,38 @@ help: consider further restricting this bound
LL | T: B + Trait + Copy,
| ++++++++++++++
-error: aborting due to 9 previous errors
+error[E0382]: use of moved value: `t`
+ --> $DIR/use_of_moved_value_copy_suggestions.rs:83:9
+ |
+LL | fn existing_colon_in_where(t: T)
+ | - move occurs because `t` has type `T`, which does not implement the `Copy` trait
+...
+LL | [t, t];
+ | - ^ value used here after move
+ | |
+ | value moved here
+ |
+help: consider further restricting this bound
+ |
+LL | T: Copy,
+ | ++++
+
+error[E0382]: use of moved value: `t`
+ --> $DIR/use_of_moved_value_copy_suggestions.rs:75:9
+ |
+LL | fn existing_colon(t: T) {
+ | - move occurs because `t` has type `T`, which does not implement the `Copy` trait
+LL |
+LL | [t, t];
+ | - ^ value used here after move
+ | |
+ | value moved here
+ |
+help: consider restricting type parameter `T`
+ |
+LL | fn existing_colon(t: T) {
+ | ++++
+
+error: aborting due to 11 previous errors
For more information about this error, try `rustc --explain E0382`.
From 08ef70bd1398efd882b985959baaa4a5cc6dd738 Mon Sep 17 00:00:00 2001
From: Oli Scherer
Date: Tue, 12 Apr 2022 12:28:31 +0000
Subject: [PATCH 07/23] Compute a more precise span for opaque type impls
---
compiler/rustc_typeck/src/coherence/orphan.rs | 33 +++++++++++++++++--
src/test/ui/impl-trait/auto-trait.stderr | 4 +--
.../ui/impl-trait/negative-reasoning.stderr | 4 +--
...impl-trait-declaration-too-subtle-2.stderr | 4 +--
...s-impl-trait-declaration-too-subtle.stderr | 4 +--
src/test/ui/traits/alias/issue-83613.stderr | 4 +--
.../type-alias-impl-trait/issue-65384.stderr | 4 +--
.../issue-76202-trait-impl-for-tait.stderr | 4 +--
.../issue-84660-trait-impl-for-tait.stderr | 4 +--
.../issue-84660-unsoundness.stderr | 4 +--
.../nested-tait-inference3.stderr | 4 +--
11 files changed, 51 insertions(+), 22 deletions(-)
diff --git a/compiler/rustc_typeck/src/coherence/orphan.rs b/compiler/rustc_typeck/src/coherence/orphan.rs
index 564c3d31368b6..76c41dedf0aef 100644
--- a/compiler/rustc_typeck/src/coherence/orphan.rs
+++ b/compiler/rustc_typeck/src/coherence/orphan.rs
@@ -7,6 +7,7 @@ use rustc_errors::ErrorGuaranteed;
use rustc_hir as hir;
use rustc_index::bit_set::GrowableBitSet;
use rustc_infer::infer::TyCtxtInferExt;
+use rustc_middle::ty::subst::GenericArgKind;
use rustc_middle::ty::subst::{GenericArg, InternalSubsts};
use rustc_middle::ty::{self, ImplPolarity, Ty, TyCtxt, TypeFoldable, TypeVisitor};
use rustc_session::lint;
@@ -144,13 +145,41 @@ fn orphan_check_impl(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), ErrorGua
// Ensure no opaque types are present in this impl header. See issues #76202 and #86411 for examples,
// and #84660 where it would otherwise allow unsoundness.
if trait_ref.has_opaque_types() {
+ trace!("{:#?}", item);
for ty in trait_ref.substs {
for ty in ty.walk() {
let ty::subst::GenericArgKind::Type(ty) = ty.unpack() else { continue };
- let ty::Opaque(def_id, _) = ty.kind() else { continue };
+ let ty::Opaque(def_id, _) = *ty.kind() else { continue };
+ trace!(?def_id);
+ struct SpanFinder<'tcx> {
+ sp: Span,
+ def_id: DefId,
+ tcx: TyCtxt<'tcx>,
+ }
+ impl<'v, 'tcx> hir::intravisit::Visitor<'v> for SpanFinder<'tcx> {
+ #[instrument(level = "trace", skip(self, _id))]
+ fn visit_path(&mut self, path: &'v hir::Path<'v>, _id: hir::HirId) {
+ if let hir::def::Res::Def(hir::def::DefKind::TyAlias, def_id) = path.res {
+ for arg in self.tcx.type_of(def_id).walk() {
+ if let GenericArgKind::Type(ty) = arg.unpack() {
+ if let ty::Opaque(def_id, _) = *ty.kind() {
+ if def_id == self.def_id {
+ self.sp = path.span;
+ return;
+ }
+ }
+ }
+ }
+ }
+ hir::intravisit::walk_path(self, path)
+ }
+ }
+
+ let mut visitor = SpanFinder { sp, def_id, tcx };
+ hir::intravisit::walk_item(&mut visitor, item);
let reported = tcx
.sess
- .struct_span_err(sp, "cannot implement trait on type alias impl trait")
+ .struct_span_err(visitor.sp, "cannot implement trait on type alias impl trait")
.span_note(tcx.def_span(def_id), "type alias impl trait defined here")
.emit();
return Err(reported);
diff --git a/src/test/ui/impl-trait/auto-trait.stderr b/src/test/ui/impl-trait/auto-trait.stderr
index 9171cfca5b146..3b360f492b70e 100644
--- a/src/test/ui/impl-trait/auto-trait.stderr
+++ b/src/test/ui/impl-trait/auto-trait.stderr
@@ -1,8 +1,8 @@
error: cannot implement trait on type alias impl trait
- --> $DIR/auto-trait.rs:21:1
+ --> $DIR/auto-trait.rs:21:25
|
LL | impl AnotherTrait for D {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^
|
note: type alias impl trait defined here
--> $DIR/auto-trait.rs:7:19
diff --git a/src/test/ui/impl-trait/negative-reasoning.stderr b/src/test/ui/impl-trait/negative-reasoning.stderr
index 1a7233bca5caf..98f9fbd8fefb7 100644
--- a/src/test/ui/impl-trait/negative-reasoning.stderr
+++ b/src/test/ui/impl-trait/negative-reasoning.stderr
@@ -1,8 +1,8 @@
error: cannot implement trait on type alias impl trait
- --> $DIR/negative-reasoning.rs:19:1
+ --> $DIR/negative-reasoning.rs:19:25
|
LL | impl AnotherTrait for D {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^
|
note: type alias impl trait defined here
--> $DIR/negative-reasoning.rs:7:19
diff --git a/src/test/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle-2.stderr b/src/test/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle-2.stderr
index 4e8d4cce0a17f..2ef1697ba341d 100644
--- a/src/test/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle-2.stderr
+++ b/src/test/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle-2.stderr
@@ -1,8 +1,8 @@
error: cannot implement trait on type alias impl trait
- --> $DIR/recursive-type-alias-impl-trait-declaration-too-subtle-2.rs:7:1
+ --> $DIR/recursive-type-alias-impl-trait-declaration-too-subtle-2.rs:7:17
|
LL | impl PartialEq<(Foo, i32)> for Bar {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^^
|
note: type alias impl trait defined here
--> $DIR/recursive-type-alias-impl-trait-declaration-too-subtle-2.rs:3:12
diff --git a/src/test/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle.stderr b/src/test/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle.stderr
index ccb0bc0a366df..6cd63dcf81c7f 100644
--- a/src/test/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle.stderr
+++ b/src/test/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle.stderr
@@ -1,8 +1,8 @@
error: cannot implement trait on type alias impl trait
- --> $DIR/recursive-type-alias-impl-trait-declaration-too-subtle.rs:20:5
+ --> $DIR/recursive-type-alias-impl-trait-declaration-too-subtle.rs:20:21
|
LL | impl PartialEq<(Foo, i32)> for Bar {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^^
|
note: type alias impl trait defined here
--> $DIR/recursive-type-alias-impl-trait-declaration-too-subtle.rs:16:16
diff --git a/src/test/ui/traits/alias/issue-83613.stderr b/src/test/ui/traits/alias/issue-83613.stderr
index 4f19e6607c8d9..bbc240b6aec49 100644
--- a/src/test/ui/traits/alias/issue-83613.stderr
+++ b/src/test/ui/traits/alias/issue-83613.stderr
@@ -1,8 +1,8 @@
error: cannot implement trait on type alias impl trait
- --> $DIR/issue-83613.rs:10:1
+ --> $DIR/issue-83613.rs:10:23
|
LL | impl AnotherTrait for OpaqueType {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^
|
note: type alias impl trait defined here
--> $DIR/issue-83613.rs:4:19
diff --git a/src/test/ui/type-alias-impl-trait/issue-65384.stderr b/src/test/ui/type-alias-impl-trait/issue-65384.stderr
index 27680f0ad75ac..41bcea27e1fa3 100644
--- a/src/test/ui/type-alias-impl-trait/issue-65384.stderr
+++ b/src/test/ui/type-alias-impl-trait/issue-65384.stderr
@@ -1,8 +1,8 @@
error: cannot implement trait on type alias impl trait
- --> $DIR/issue-65384.rs:10:1
+ --> $DIR/issue-65384.rs:10:18
|
LL | impl MyTrait for Bar {}
- | ^^^^^^^^^^^^^^^^^^^^
+ | ^^^
|
note: type alias impl trait defined here
--> $DIR/issue-65384.rs:8:12
diff --git a/src/test/ui/type-alias-impl-trait/issue-76202-trait-impl-for-tait.stderr b/src/test/ui/type-alias-impl-trait/issue-76202-trait-impl-for-tait.stderr
index 8689ee53660f6..2d4a6854a920b 100644
--- a/src/test/ui/type-alias-impl-trait/issue-76202-trait-impl-for-tait.stderr
+++ b/src/test/ui/type-alias-impl-trait/issue-76202-trait-impl-for-tait.stderr
@@ -1,8 +1,8 @@
error: cannot implement trait on type alias impl trait
- --> $DIR/issue-76202-trait-impl-for-tait.rs:16:1
+ --> $DIR/issue-76202-trait-impl-for-tait.rs:16:15
|
LL | impl Test for F {
- | ^^^^^^^^^^^^^^^
+ | ^
|
note: type alias impl trait defined here
--> $DIR/issue-76202-trait-impl-for-tait.rs:9:10
diff --git a/src/test/ui/type-alias-impl-trait/issue-84660-trait-impl-for-tait.stderr b/src/test/ui/type-alias-impl-trait/issue-84660-trait-impl-for-tait.stderr
index 1b8eee407178c..bb70d07be59bb 100644
--- a/src/test/ui/type-alias-impl-trait/issue-84660-trait-impl-for-tait.stderr
+++ b/src/test/ui/type-alias-impl-trait/issue-84660-trait-impl-for-tait.stderr
@@ -1,8 +1,8 @@
error: cannot implement trait on type alias impl trait
- --> $DIR/issue-84660-trait-impl-for-tait.rs:15:1
+ --> $DIR/issue-84660-trait-impl-for-tait.rs:15:15
|
LL | impl TraitArg for () {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^^
|
note: type alias impl trait defined here
--> $DIR/issue-84660-trait-impl-for-tait.rs:8:12
diff --git a/src/test/ui/type-alias-impl-trait/issue-84660-unsoundness.stderr b/src/test/ui/type-alias-impl-trait/issue-84660-unsoundness.stderr
index b1128d830f967..f2d600fb46c54 100644
--- a/src/test/ui/type-alias-impl-trait/issue-84660-unsoundness.stderr
+++ b/src/test/ui/type-alias-impl-trait/issue-84660-unsoundness.stderr
@@ -1,8 +1,8 @@
error: cannot implement trait on type alias impl trait
- --> $DIR/issue-84660-unsoundness.rs:16:1
+ --> $DIR/issue-84660-unsoundness.rs:16:21
|
LL | impl Trait for Out {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^^
|
note: type alias impl trait defined here
--> $DIR/issue-84660-unsoundness.rs:8:12
diff --git a/src/test/ui/type-alias-impl-trait/nested-tait-inference3.stderr b/src/test/ui/type-alias-impl-trait/nested-tait-inference3.stderr
index bf1582639a2ca..4a3fb16733e04 100644
--- a/src/test/ui/type-alias-impl-trait/nested-tait-inference3.stderr
+++ b/src/test/ui/type-alias-impl-trait/nested-tait-inference3.stderr
@@ -1,8 +1,8 @@
error: cannot implement trait on type alias impl trait
- --> $DIR/nested-tait-inference3.rs:10:1
+ --> $DIR/nested-tait-inference3.rs:10:10
|
LL | impl Foo for () { }
- | ^^^^^^^^^^^^^^^^^^^^^
+ | ^^^^
|
note: type alias impl trait defined here
--> $DIR/nested-tait-inference3.rs:6:13
From 93a3cfb748546ba7729db68f2e962e8b29eafa77 Mon Sep 17 00:00:00 2001
From: Oli Scherer
Date: Tue, 12 Apr 2022 12:31:00 +0000
Subject: [PATCH 08/23] Explain the span search logic
---
compiler/rustc_typeck/src/coherence/orphan.rs | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/compiler/rustc_typeck/src/coherence/orphan.rs b/compiler/rustc_typeck/src/coherence/orphan.rs
index 76c41dedf0aef..f9e2e4c41aad9 100644
--- a/compiler/rustc_typeck/src/coherence/orphan.rs
+++ b/compiler/rustc_typeck/src/coherence/orphan.rs
@@ -146,11 +146,14 @@ fn orphan_check_impl(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), ErrorGua
// and #84660 where it would otherwise allow unsoundness.
if trait_ref.has_opaque_types() {
trace!("{:#?}", item);
+ // First we find the opaque type in question.
for ty in trait_ref.substs {
for ty in ty.walk() {
let ty::subst::GenericArgKind::Type(ty) = ty.unpack() else { continue };
let ty::Opaque(def_id, _) = *ty.kind() else { continue };
trace!(?def_id);
+
+ // Then we search for mentions of the opaque type's type alias in the HIR
struct SpanFinder<'tcx> {
sp: Span,
def_id: DefId,
@@ -159,11 +162,14 @@ fn orphan_check_impl(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), ErrorGua
impl<'v, 'tcx> hir::intravisit::Visitor<'v> for SpanFinder<'tcx> {
#[instrument(level = "trace", skip(self, _id))]
fn visit_path(&mut self, path: &'v hir::Path<'v>, _id: hir::HirId) {
+ // You can't mention an opaque type directly, so we look for type aliases
if let hir::def::Res::Def(hir::def::DefKind::TyAlias, def_id) = path.res {
+ // And check if that type alias's type contains the opaque type we're looking for
for arg in self.tcx.type_of(def_id).walk() {
if let GenericArgKind::Type(ty) = arg.unpack() {
if let ty::Opaque(def_id, _) = *ty.kind() {
if def_id == self.def_id {
+ // Finally we update the span to the mention of the type alias
self.sp = path.span;
return;
}
From 8de453a8c6a26e43876def2d757bec40ed9b2767 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?=
Date: Tue, 29 Mar 2022 19:30:54 +0200
Subject: [PATCH 09/23] rustdoc: discr. required+provided assoc consts+tys
---
src/librustdoc/clean/inline.rs | 2 +-
src/librustdoc/clean/mod.rs | 85 +++---
src/librustdoc/clean/types.rs | 35 ++-
src/librustdoc/clean/utils.rs | 2 +-
src/librustdoc/fold.rs | 6 +-
src/librustdoc/formats/cache.rs | 11 +-
src/librustdoc/formats/item_type.rs | 6 +-
src/librustdoc/html/format.rs | 15 ++
src/librustdoc/html/markdown.rs | 6 +-
src/librustdoc/html/render/mod.rs | 247 +++++++++++-------
src/librustdoc/html/render/print_item.rs | 160 ++++++------
src/librustdoc/html/static/js/search.js | 2 +-
src/librustdoc/json/conversions.rs | 11 +-
.../passes/check_doc_test_visibility.rs | 4 +-
src/librustdoc/passes/collect_trait_impls.rs | 2 +-
src/librustdoc/passes/stripper.rs | 8 +-
src/librustdoc/visit.rs | 6 +-
src/test/rustdoc/assoc-consts.rs | 4 +-
...tern-default-method.no_href_on_anchor.html | 1 +
src/test/rustdoc/extern-default-method.rs | 16 +-
src/test/rustdoc/intra-doc/prim-self.rs | 4 +
src/test/rustdoc/sidebar-items.rs | 11 +-
22 files changed, 401 insertions(+), 243 deletions(-)
create mode 100644 src/test/rustdoc/extern-default-method.no_href_on_anchor.html
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index d06e4fa1cc2f5..33db6583125ef 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -79,7 +79,7 @@ crate fn try_inline(
Res::Def(DefKind::TyAlias, did) => {
record_extern_fqn(cx, did, ItemType::Typedef);
build_impls(cx, Some(parent_module), did, attrs, &mut ret);
- clean::TypedefItem(build_type_alias(cx, did), false)
+ clean::TypedefItem(build_type_alias(cx, did))
}
Res::Def(DefKind::Enum, did) => {
record_extern_fqn(cx, did, ItemType::Enum);
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 85a3e05e8b213..a6763d2827cec 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -964,11 +964,11 @@ impl Clean- for hir::TraitItem<'_> {
let local_did = self.def_id.to_def_id();
cx.with_param_env(local_did, |cx| {
let inner = match self.kind {
- hir::TraitItemKind::Const(ref ty, default) => {
- let default =
- default.map(|e| ConstantKind::Local { def_id: local_did, body: e });
- AssocConstItem(ty.clean(cx), default)
- }
+ hir::TraitItemKind::Const(ref ty, Some(default)) => AssocConstItem(
+ ty.clean(cx),
+ ConstantKind::Local { def_id: local_did, body: default },
+ ),
+ hir::TraitItemKind::Const(ref ty, None) => TyAssocConstItem(ty.clean(cx)),
hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => {
let m = clean_function(cx, sig, &self.generics, body);
MethodItem(m, None)
@@ -983,11 +983,19 @@ impl Clean
- for hir::TraitItem<'_> {
});
TyMethodItem(Function { decl, generics })
}
- hir::TraitItemKind::Type(bounds, ref default) => {
+ hir::TraitItemKind::Type(bounds, Some(default)) => {
+ let generics = enter_impl_trait(cx, |cx| self.generics.clean(cx));
+ let bounds = bounds.iter().filter_map(|x| x.clean(cx)).collect();
+ let item_type = hir_ty_to_ty(cx.tcx, default).clean(cx);
+ AssocTypeItem(
+ Typedef { type_: default.clean(cx), generics, item_type: Some(item_type) },
+ bounds,
+ )
+ }
+ hir::TraitItemKind::Type(bounds, None) => {
let generics = enter_impl_trait(cx, |cx| self.generics.clean(cx));
let bounds = bounds.iter().filter_map(|x| x.clean(cx)).collect();
- let default = default.map(|t| t.clean(cx));
- AssocTypeItem(Box::new(generics), bounds, default)
+ TyAssocTypeItem(Box::new(generics), bounds)
}
};
let what_rustc_thinks =
@@ -1004,7 +1012,7 @@ impl Clean
- for hir::ImplItem<'_> {
cx.with_param_env(local_did, |cx| {
let inner = match self.kind {
hir::ImplItemKind::Const(ref ty, expr) => {
- let default = Some(ConstantKind::Local { def_id: local_did, body: expr });
+ let default = ConstantKind::Local { def_id: local_did, body: expr };
AssocConstItem(ty.clean(cx), default)
}
hir::ImplItemKind::Fn(ref sig, body) => {
@@ -1016,7 +1024,10 @@ impl Clean
- for hir::ImplItem<'_> {
let type_ = hir_ty.clean(cx);
let generics = self.generics.clean(cx);
let item_type = hir_ty_to_ty(cx.tcx, hir_ty).clean(cx);
- TypedefItem(Typedef { type_, generics, item_type: Some(item_type) }, true)
+ AssocTypeItem(
+ Typedef { type_, generics, item_type: Some(item_type) },
+ Vec::new(),
+ )
}
};
@@ -1041,13 +1052,17 @@ impl Clean
- for ty::AssocItem {
let tcx = cx.tcx;
let kind = match self.kind {
ty::AssocKind::Const => {
- let ty = tcx.type_of(self.def_id);
- let default = if self.defaultness.has_value() {
- Some(ConstantKind::Extern { def_id: self.def_id })
- } else {
- None
+ let ty = tcx.type_of(self.def_id).clean(cx);
+
+ let provided = match self.container {
+ ty::ImplContainer(_) => true,
+ ty::TraitContainer(_) => self.defaultness.has_value(),
};
- AssocConstItem(ty.clean(cx), default)
+ if provided {
+ AssocConstItem(ty, ConstantKind::Extern { def_id: self.def_id })
+ } else {
+ TyAssocConstItem(ty)
+ }
}
ty::AssocKind::Fn => {
let generics = clean_ty_generics(
@@ -1181,23 +1196,28 @@ impl Clean
- for ty::AssocItem {
None => bounds.push(GenericBound::maybe_sized(cx)),
}
- let ty = if self.defaultness.has_value() {
- Some(tcx.type_of(self.def_id))
+ if self.defaultness.has_value() {
+ AssocTypeItem(
+ Typedef {
+ type_: tcx.type_of(self.def_id).clean(cx),
+ generics,
+ // FIXME: should we obtain the Type from HIR and pass it on here?
+ item_type: None,
+ },
+ bounds,
+ )
} else {
- None
- };
-
- AssocTypeItem(Box::new(generics), bounds, ty.map(|t| t.clean(cx)))
+ TyAssocTypeItem(Box::new(generics), bounds)
+ }
} else {
// FIXME: when could this happen? Associated items in inherent impls?
- let type_ = tcx.type_of(self.def_id).clean(cx);
- TypedefItem(
+ AssocTypeItem(
Typedef {
- type_,
+ type_: tcx.type_of(self.def_id).clean(cx),
generics: Generics { params: Vec::new(), where_predicates: Vec::new() },
item_type: None,
},
- true,
+ Vec::new(),
)
}
}
@@ -1837,14 +1857,11 @@ fn clean_maybe_renamed_item(
ItemKind::TyAlias(hir_ty, ref generics) => {
let rustdoc_ty = hir_ty.clean(cx);
let ty = hir_ty_to_ty(cx.tcx, hir_ty).clean(cx);
- TypedefItem(
- Typedef {
- type_: rustdoc_ty,
- generics: generics.clean(cx),
- item_type: Some(ty),
- },
- false,
- )
+ TypedefItem(Typedef {
+ type_: rustdoc_ty,
+ generics: generics.clean(cx),
+ item_type: Some(ty),
+ })
}
ItemKind::Enum(ref def, ref generics) => EnumItem(Enum {
variants: def.variants.iter().map(|v| v.clean(cx)).collect(),
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index 7698c2de24d5c..d2abfc35b932c 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -577,10 +577,16 @@ impl Item {
self.type_() == ItemType::Variant
}
crate fn is_associated_type(&self) -> bool {
- self.type_() == ItemType::AssocType
+ matches!(&*self.kind, AssocTypeItem(..) | StrippedItem(box AssocTypeItem(..)))
+ }
+ crate fn is_ty_associated_type(&self) -> bool {
+ matches!(&*self.kind, TyAssocTypeItem(..) | StrippedItem(box TyAssocTypeItem(..)))
}
crate fn is_associated_const(&self) -> bool {
- self.type_() == ItemType::AssocConst
+ matches!(&*self.kind, AssocConstItem(..) | StrippedItem(box AssocConstItem(..)))
+ }
+ crate fn is_ty_associated_const(&self) -> bool {
+ matches!(&*self.kind, TyAssocConstItem(..) | StrippedItem(box TyAssocConstItem(..)))
}
crate fn is_method(&self) -> bool {
self.type_() == ItemType::Method
@@ -726,17 +732,18 @@ crate enum ItemKind {
EnumItem(Enum),
FunctionItem(Function),
ModuleItem(Module),
- TypedefItem(Typedef, bool /* is associated type */),
+ TypedefItem(Typedef),
OpaqueTyItem(OpaqueTy),
StaticItem(Static),
ConstantItem(Constant),
TraitItem(Trait),
TraitAliasItem(TraitAlias),
ImplItem(Impl),
- /// A method signature only. Used for required methods in traits (ie,
- /// non-default-methods).
+ /// A required method in a trait declaration meaning it's only a function signature.
TyMethodItem(Function),
- /// A method with a body.
+ /// A method in a trait impl or a provided method in a trait declaration.
+ ///
+ /// Compared to [TyMethodItem], it also contains a method body.
MethodItem(Function, Option),
StructFieldItem(Type),
VariantItem(Variant),
@@ -749,12 +756,16 @@ crate enum ItemKind {
MacroItem(Macro),
ProcMacroItem(ProcMacro),
PrimitiveItem(PrimitiveType),
- AssocConstItem(Type, Option),
- /// An associated item in a trait or trait impl.
+ /// A required associated constant in a trait declaration.
+ TyAssocConstItem(Type),
+ /// An associated associated constant in a trait impl or a provided one in a trait declaration.
+ AssocConstItem(Type, ConstantKind),
+ /// A required associated type in a trait declaration.
///
/// The bounds may be non-empty if there is a `where` clause.
- /// The `Option` is the default concrete type (e.g. `trait Trait { type Target = usize; }`)
- AssocTypeItem(Box, Vec, Option),
+ TyAssocTypeItem(Box, Vec),
+ /// An associated type in a trait impl or a provided one in a trait declaration.
+ AssocTypeItem(Typedef, Vec),
/// An item that has been stripped by a rustdoc pass
StrippedItem(Box),
KeywordItem(Symbol),
@@ -776,7 +787,7 @@ impl ItemKind {
ExternCrateItem { .. }
| ImportItem(_)
| FunctionItem(_)
- | TypedefItem(_, _)
+ | TypedefItem(_)
| OpaqueTyItem(_)
| StaticItem(_)
| ConstantItem(_)
@@ -791,7 +802,9 @@ impl ItemKind {
| MacroItem(_)
| ProcMacroItem(_)
| PrimitiveItem(_)
+ | TyAssocConstItem(_)
| AssocConstItem(_, _)
+ | TyAssocTypeItem(..)
| AssocTypeItem(..)
| StrippedItem(_)
| KeywordItem(_) => [].iter(),
diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs
index c85ef0ac05452..fe6d680991f80 100644
--- a/src/librustdoc/clean/utils.rs
+++ b/src/librustdoc/clean/utils.rs
@@ -178,7 +178,7 @@ crate fn build_deref_target_impls(cx: &mut DocContext<'_>, items: &[Item], ret:
for item in items {
let target = match *item.kind {
- ItemKind::TypedefItem(ref t, true) => &t.type_,
+ ItemKind::AssocTypeItem(ref t, _) => &t.type_,
_ => continue,
};
diff --git a/src/librustdoc/fold.rs b/src/librustdoc/fold.rs
index fbb8b572ea430..95adc4426b585 100644
--- a/src/librustdoc/fold.rs
+++ b/src/librustdoc/fold.rs
@@ -71,7 +71,7 @@ crate trait DocFolder: Sized {
ExternCrateItem { src: _ }
| ImportItem(_)
| FunctionItem(_)
- | TypedefItem(_, _)
+ | TypedefItem(_)
| OpaqueTyItem(_)
| StaticItem(_)
| ConstantItem(_)
@@ -85,7 +85,9 @@ crate trait DocFolder: Sized {
| MacroItem(_)
| ProcMacroItem(_)
| PrimitiveItem(_)
- | AssocConstItem(_, _)
+ | TyAssocConstItem(..)
+ | AssocConstItem(..)
+ | TyAssocTypeItem(..)
| AssocTypeItem(..)
| KeywordItem(_) => kind,
}
diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs
index 53159709586c6..663e18fe9129f 100644
--- a/src/librustdoc/formats/cache.rs
+++ b/src/librustdoc/formats/cache.rs
@@ -242,14 +242,15 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
if let Some(ref s) = item.name {
let (parent, is_inherent_impl_item) = match *item.kind {
clean::StrippedItem(..) => ((None, None), false),
- clean::AssocConstItem(..) | clean::TypedefItem(_, true)
+ clean::AssocConstItem(..) | clean::AssocTypeItem(..)
if self.cache.parent_is_trait_impl =>
{
// skip associated items in trait impls
((None, None), false)
}
- clean::AssocTypeItem(..)
- | clean::TyMethodItem(..)
+ clean::TyMethodItem(..)
+ | clean::TyAssocConstItem(..)
+ | clean::TyAssocTypeItem(..)
| clean::StructFieldItem(..)
| clean::VariantItem(..) => (
(
@@ -258,7 +259,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
),
false,
),
- clean::MethodItem(..) | clean::AssocConstItem(..) => {
+ clean::MethodItem(..) | clean::AssocConstItem(..) | clean::AssocTypeItem(..) => {
if self.cache.parent_stack.is_empty() {
((None, None), false)
} else {
@@ -373,7 +374,9 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
| clean::TyMethodItem(..)
| clean::MethodItem(..)
| clean::StructFieldItem(..)
+ | clean::TyAssocConstItem(..)
| clean::AssocConstItem(..)
+ | clean::TyAssocTypeItem(..)
| clean::AssocTypeItem(..)
| clean::StrippedItem(..)
| clean::KeywordItem(..) => {
diff --git a/src/librustdoc/formats/item_type.rs b/src/librustdoc/formats/item_type.rs
index f8660c296cb0b..fb4afb769ad15 100644
--- a/src/librustdoc/formats/item_type.rs
+++ b/src/librustdoc/formats/item_type.rs
@@ -14,7 +14,7 @@ use crate::clean;
/// The search index uses item types encoded as smaller numbers which equal to
/// discriminants. JavaScript then is used to decode them into the original value.
/// Consequently, every change to this type should be synchronized to
-/// the `itemTypes` mapping table in `html/static/main.js`.
+/// the `itemTypes` mapping table in `html/static/js/search.js`.
///
/// In addition, code in `html::render` uses this enum to generate CSS classes, page prefixes, and
/// module headings. If you are adding to this enum and want to ensure that the sidebar also prints
@@ -89,8 +89,8 @@ impl<'a> From<&'a clean::Item> for ItemType {
clean::ForeignStaticItem(..) => ItemType::Static, // no ForeignStatic
clean::MacroItem(..) => ItemType::Macro,
clean::PrimitiveItem(..) => ItemType::Primitive,
- clean::AssocConstItem(..) => ItemType::AssocConst,
- clean::AssocTypeItem(..) => ItemType::AssocType,
+ clean::TyAssocConstItem(..) | clean::AssocConstItem(..) => ItemType::AssocConst,
+ clean::TyAssocTypeItem(..) | clean::AssocTypeItem(..) => ItemType::AssocType,
clean::ForeignTypeItem => ItemType::ForeignType,
clean::KeywordItem(..) => ItemType::Keyword,
clean::TraitAliasItem(..) => ItemType::TraitAlias,
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index 5c59609d5b8c6..55b0028180f66 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -527,6 +527,21 @@ crate enum HrefError {
/// This item is known to rustdoc, but from a crate that does not have documentation generated.
///
/// This can only happen for non-local items.
+ ///
+ /// # Example
+ ///
+ /// Crate `a` defines a public trait and crate `b` – the target crate that depends on `a` –
+ /// implements it for a local type.
+ /// We document `b` but **not** `a` (we only _build_ the latter – with `rustc`):
+ ///
+ /// ```sh
+ /// rustc a.rs --crate-type=lib
+ /// rustdoc b.rs --crate-type=lib --extern=a=liba.rlib
+ /// ```
+ ///
+ /// Now, the associated items in the trait impl want to link to the corresponding item in the
+ /// trait declaration (see `html::render::assoc_href_attr`) but it's not available since their
+ /// *documentation (was) not built*.
DocumentationNotBuilt,
/// This can only happen for non-local items when `--document-private-items` is not passed.
Private,
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index 943c521485b18..1ebb41b5933d0 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -1452,8 +1452,10 @@ fn init_id_map() -> FxHashMap {
map.insert("trait-implementations".to_owned(), 1);
map.insert("synthetic-implementations".to_owned(), 1);
map.insert("blanket-implementations".to_owned(), 1);
- map.insert("associated-types".to_owned(), 1);
- map.insert("associated-const".to_owned(), 1);
+ map.insert("required-associated-types".to_owned(), 1);
+ map.insert("provided-associated-types".to_owned(), 1);
+ map.insert("provided-associated-consts".to_owned(), 1);
+ map.insert("required-associated-consts".to_owned(), 1);
map.insert("required-methods".to_owned(), 1);
map.insert("provided-methods".to_owned(), 1);
map.insert("implementors".to_owned(), 1);
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 12da16527a0eb..9891c4b676fb4 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -521,7 +521,7 @@ fn document_short(
let mut summary_html = MarkdownSummaryLine(&s, &item.links(cx)).into_string();
if s.contains('\n') {
- let link = format!(r#" Read more"#, naive_assoc_href(item, link, cx));
+ let link = format!(r#" Read more"#, assoc_href_attr(item, link, cx));
if let Some(idx) = summary_html.rfind("
") {
summary_html.insert_str(idx, &link);
@@ -737,42 +737,82 @@ fn render_impls(
w.write_str(&rendered_impls.join(""));
}
-fn naive_assoc_href(it: &clean::Item, link: AssocItemLink<'_>, cx: &Context<'_>) -> String {
- use crate::formats::item_type::ItemType::*;
+/// Build a (possibly empty) `href` attribute (a key-value pair) for the given associated item.
+fn assoc_href_attr(it: &clean::Item, link: AssocItemLink<'_>, cx: &Context<'_>) -> String {
+ let name = it.name.unwrap();
+ let item_type = it.type_();
- let name = it.name.as_ref().unwrap();
- let ty = match it.type_() {
- Typedef | AssocType => AssocType,
- s => s,
- };
+ let href = match link {
+ AssocItemLink::Anchor(Some(ref id)) => Some(format!("#{}", id)),
+ AssocItemLink::Anchor(None) => Some(format!("#{}.{}", item_type, name)),
+ AssocItemLink::GotoSource(did, provided_methods) => {
+ // We're creating a link from the implementation of an associated item to its
+ // declaration in the trait declaration.
+ let item_type = match item_type {
+ // For historical but not technical reasons, the item type of methods in
+ // trait declarations depends on whether the method is required (`TyMethod`) or
+ // provided (`Method`).
+ ItemType::Method | ItemType::TyMethod => {
+ if provided_methods.contains(&name) {
+ ItemType::Method
+ } else {
+ ItemType::TyMethod
+ }
+ }
+ // For associated types and constants, no such distinction exists.
+ item_type => item_type,
+ };
- let anchor = format!("#{}.{}", ty, name);
- match link {
- AssocItemLink::Anchor(Some(ref id)) => format!("#{}", id),
- AssocItemLink::Anchor(None) => anchor,
- AssocItemLink::GotoSource(did, _) => {
- href(did.expect_def_id(), cx).map(|p| format!("{}{}", p.0, anchor)).unwrap_or(anchor)
+ match href(did.expect_def_id(), cx) {
+ Ok((url, ..)) => Some(format!("{}#{}.{}", url, item_type, name)),
+ // The link is broken since it points to an external crate that wasn't documented.
+ // Do not create any link in such case. This is better than falling back to a
+ // dummy anchor like `#{item_type}.{name}` representing the `id` of *this* impl item
+ // (that used to happen in older versions). Indeed, in most cases this dummy would
+ // coincide with the `id`. However, it would not always do so.
+ // In general, this dummy would be incorrect:
+ // If the type with the trait impl also had an inherent impl with an assoc. item of
+ // the *same* name as this impl item, the dummy would link to that one even though
+ // those two items are distinct!
+ // In this scenario, the actual `id` of this impl item would be
+ // `#{item_type}.{name}-{n}` for some number `n` (a disambiguator).
+ Err(HrefError::DocumentationNotBuilt) => None,
+ Err(_) => Some(format!("#{}.{}", item_type, name)),
+ }
}
- }
+ };
+
+ // If there is no `href` for the reason explained above, simply do not render it which is valid:
+ // https://html.spec.whatwg.org/multipage/links.html#links-created-by-a-and-area-elements
+ href.map(|href| format!(" href=\"{}\"", href)).unwrap_or_default()
}
fn assoc_const(
w: &mut Buffer,
it: &clean::Item,
ty: &clean::Type,
+ default: Option<&clean::ConstantKind>,
link: AssocItemLink<'_>,
extra: &str,
cx: &Context<'_>,
) {
write!(
w,
- "{}{}const {}: {}",
- extra,
- it.visibility.print_with_space(it.def_id, cx),
- naive_assoc_href(it, link, cx),
- it.name.as_ref().unwrap(),
- ty.print(cx)
+ "{extra}{vis}const {name}: {ty}",
+ extra = extra,
+ vis = it.visibility.print_with_space(it.def_id, cx),
+ href = assoc_href_attr(it, link, cx),
+ name = it.name.as_ref().unwrap(),
+ ty = ty.print(cx),
);
+ if let Some(default) = default {
+ // FIXME: `.value()` uses `clean::utils::format_integer_with_underscore_sep` under the
+ // hood which adds noisy underscores and a type suffix to number literals.
+ // This hurts readability in this context especially when more complex expressions
+ // are involved and it doesn't add much of value.
+ // Find a way to print constants here without all that jazz.
+ write!(w, " = {}", default.value(cx.tcx()).unwrap_or_else(|| default.expr(cx.tcx())));
+ }
}
fn assoc_type(
@@ -787,9 +827,9 @@ fn assoc_type(
) {
write!(
w,
- "{indent}type {name}{generics}",
+ "{indent}type {name}{generics}",
indent = " ".repeat(indent),
- href = naive_assoc_href(it, link, cx),
+ href = assoc_href_attr(it, link, cx),
name = it.name.as_ref().unwrap(),
generics = generics.print(cx),
);
@@ -814,22 +854,6 @@ fn assoc_method(
) {
let header = meth.fn_header(cx.tcx()).expect("Trying to get header from a non-function item");
let name = meth.name.as_ref().unwrap();
- let href = match link {
- AssocItemLink::Anchor(Some(ref id)) => Some(format!("#{}", id)),
- AssocItemLink::Anchor(None) => Some(format!("#{}.{}", meth.type_(), name)),
- AssocItemLink::GotoSource(did, provided_methods) => {
- // We're creating a link from an impl-item to the corresponding
- // trait-item and need to map the anchored type accordingly.
- let ty =
- if provided_methods.contains(name) { ItemType::Method } else { ItemType::TyMethod };
-
- match (href(did.expect_def_id(), cx), ty) {
- (Ok(p), ty) => Some(format!("{}#{}.{}", p.0, ty, name)),
- (Err(HrefError::DocumentationNotBuilt), ItemType::TyMethod) => None,
- (Err(_), ty) => Some(format!("#{}.{}", ty, name)),
- }
- }
- };
let vis = meth.visibility.print_with_space(meth.def_id, cx).to_string();
// FIXME: Once https://github.com/rust-lang/rust/issues/67792 is implemented, we can remove
// this condition.
@@ -843,6 +867,7 @@ fn assoc_method(
let unsafety = header.unsafety.print_with_space();
let defaultness = print_default_space(meth.is_default());
let abi = print_abi_with_space(header.abi).to_string();
+ let href = assoc_href_attr(meth, link, cx);
// NOTE: `{:#}` does not print HTML formatting, `{}` does. So `g.print` can't be reused between the length calculation and `write!`.
let generics_len = format!("{:#}", g.print(cx)).len();
@@ -868,7 +893,7 @@ fn assoc_method(
w.reserve(header_len + "{".len() + "".len());
write!(
w,
- "{indent}{vis}{constness}{asyncness}{unsafety}{defaultness}{abi}fn {name}\
+ "{indent}{vis}{constness}{asyncness}{unsafety}{defaultness}{abi}fn {name}\
{generics}{decl}{notable_traits}{where_clause}",
indent = indent_str,
vis = vis,
@@ -877,8 +902,7 @@ fn assoc_method(
unsafety = unsafety,
defaultness = defaultness,
abi = abi,
- // links without a href are valid - https://www.w3schools.com/tags/att_a_href.asp
- href = href.map(|href| format!("href=\"{}\"", href)).unwrap_or_else(|| "".to_string()),
+ href = href,
name = name,
generics = g.print(cx),
decl = d.full_print(header_len, indent, header.asyncness, cx),
@@ -968,23 +992,43 @@ fn render_assoc_item(
cx: &Context<'_>,
render_mode: RenderMode,
) {
- match *item.kind {
+ match &*item.kind {
clean::StrippedItem(..) => {}
- clean::TyMethodItem(ref m) => {
+ clean::TyMethodItem(m) => {
assoc_method(w, item, &m.generics, &m.decl, link, parent, cx, render_mode)
}
- clean::MethodItem(ref m, _) => {
+ clean::MethodItem(m, _) => {
assoc_method(w, item, &m.generics, &m.decl, link, parent, cx, render_mode)
}
- clean::AssocConstItem(ref ty, _) => {
- assoc_const(w, item, ty, link, if parent == ItemType::Trait { " " } else { "" }, cx)
- }
- clean::AssocTypeItem(ref generics, ref bounds, ref default) => assoc_type(
+ kind @ (clean::TyAssocConstItem(ty) | clean::AssocConstItem(ty, _)) => assoc_const(
+ w,
+ item,
+ ty,
+ match kind {
+ clean::TyAssocConstItem(_) => None,
+ clean::AssocConstItem(_, default) => Some(default),
+ _ => unreachable!(),
+ },
+ link,
+ if parent == ItemType::Trait { " " } else { "" },
+ cx,
+ ),
+ clean::TyAssocTypeItem(ref generics, ref bounds) => assoc_type(
w,
item,
generics,
bounds,
- default.as_ref(),
+ None,
+ link,
+ if parent == ItemType::Trait { 4 } else { 0 },
+ cx,
+ ),
+ clean::AssocTypeItem(ref ty, ref bounds) => assoc_type(
+ w,
+ item,
+ &ty.generics,
+ bounds,
+ Some(ty.item_type.as_ref().unwrap_or(&ty.type_)),
link,
if parent == ItemType::Trait { 4 } else { 0 },
cx,
@@ -1205,7 +1249,7 @@ fn render_deref_methods(
.items
.iter()
.find_map(|item| match *item.kind {
- clean::TypedefItem(ref t, true) => Some(match *t {
+ clean::AssocTypeItem(ref t, _) => Some(match *t {
clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_),
_ => (&t.type_, &t.type_),
}),
@@ -1291,7 +1335,7 @@ fn notable_traits_decl(decl: &clean::FnDecl, cx: &Context<'_>) -> String {
impl_.print(false, cx)
);
for it in &impl_.items {
- if let clean::TypedefItem(ref tydef, _) = *it.kind {
+ if let clean::AssocTypeItem(ref tydef, ref _bounds) = *it.kind {
out.push_str(" ");
let empty_set = FxHashSet::default();
let src_link =
@@ -1300,7 +1344,7 @@ fn notable_traits_decl(decl: &clean::FnDecl, cx: &Context<'_>) -> String {
&mut out,
it,
&tydef.generics,
- &[],
+ &[], // intentionally leaving out bounds
Some(&tydef.type_),
src_link,
0,
@@ -1439,7 +1483,7 @@ fn render_impl(
if item_type == ItemType::Method { " method-toggle" } else { "" };
write!(w, "", method_toggle_class);
}
- match *item.kind {
+ match &*item.kind {
clean::MethodItem(..) | clean::TyMethodItem(_) => {
// Only render when the method is not static or we allow static methods
if render_method_item {
@@ -1471,63 +1515,68 @@ fn render_impl(
w.write_str("");
}
}
- clean::TypedefItem(ref tydef, _) => {
- let source_id = format!("{}.{}", ItemType::AssocType, name);
+ kind @ (clean::TyAssocConstItem(ty) | clean::AssocConstItem(ty, _)) => {
+ let source_id = format!("{}.{}", item_type, name);
let id = cx.derive_id(source_id.clone());
write!(
w,
"",
id, item_type, in_trait_class
);
+ render_rightside(w, cx, item, containing_item, render_mode);
write!(w, "", id);
w.write_str("");
w.write_str("");
}
- clean::AssocConstItem(ref ty, _) => {
+ clean::TyAssocTypeItem(generics, bounds) => {
let source_id = format!("{}.{}", item_type, name);
let id = cx.derive_id(source_id.clone());
- write!(
- w,
- "",
- id, item_type, in_trait_class
- );
- render_rightside(w, cx, item, containing_item, render_mode);
+ write!(w, "", id, item_type, in_trait_class);
write!(w, "", id);
w.write_str("");
w.write_str("");
}
- clean::AssocTypeItem(ref generics, ref bounds, ref default) => {
+ clean::AssocTypeItem(tydef, _bounds) => {
let source_id = format!("{}.{}", item_type, name);
let id = cx.derive_id(source_id.clone());
- write!(w, "", id, item_type, in_trait_class,);
+ write!(
+ w,
+ "",
+ id, item_type, in_trait_class
+ );
write!(w, "", id);
w.write_str("