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("

"); - assoc_type( + assoc_const( w, item, - &tydef.generics, - &[], - Some(&tydef.type_), + ty, + match kind { + clean::TyAssocConstItem(_) => None, + clean::AssocConstItem(_, default) => Some(default), + _ => unreachable!(), + }, link.anchor(if trait_.is_some() { &source_id } else { &id }), - 0, + "", cx, ); 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("

"); - assoc_const( + assoc_type( w, item, - ty, + generics, + bounds, + None, link.anchor(if trait_.is_some() { &source_id } else { &id }), - "", + 0, cx, ); 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("

"); assoc_type( w, item, - generics, - bounds, - default.as_ref(), + &tydef.generics, + &[], // intentionally leaving out bounds + Some(tydef.item_type.as_ref().unwrap_or(&tydef.type_)), link.anchor(if trait_.is_some() { &source_id } else { &id }), 0, cx, @@ -1748,13 +1797,13 @@ pub(crate) fn render_impl_summary( write!(w, "{}", i.inner_impl().print(use_absolute, cx)); if show_def_docs { for it in &i.inner_impl().items { - if let clean::TypedefItem(ref tydef, _) = *it.kind { + if let clean::AssocTypeItem(ref tydef, ref _bounds) = *it.kind { w.write_str(" "); assoc_type( w, it, &tydef.generics, - &[], + &[], // intentionally leaving out bounds Some(&tydef.type_), AssocItemLink::Anchor(None), 0, @@ -1822,7 +1871,7 @@ fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) { clean::PrimitiveItem(_) => sidebar_primitive(cx, buffer, it), clean::UnionItem(ref u) => sidebar_union(cx, buffer, it, u), clean::EnumItem(ref e) => sidebar_enum(cx, buffer, it, e), - clean::TypedefItem(_, _) => sidebar_typedef(cx, buffer, it), + clean::TypedefItem(_) => sidebar_typedef(cx, buffer, it), clean::ModuleItem(ref m) => sidebar_module(buffer, &m.items), clean::ForeignTypeItem => sidebar_foreign_type(cx, buffer, it), _ => {} @@ -1917,7 +1966,7 @@ fn get_methods( if !for_deref || should_render_item(item, deref_mut, tcx) { Some(SidebarLink { name, - url: get_next_url(used_links, format!("method.{}", name)), + url: get_next_url(used_links, format!("{}.{}", ItemType::Method, name)), }) } else { None @@ -1937,7 +1986,7 @@ fn get_associated_constants( .filter_map(|item| match item.name { Some(name) if !name.is_empty() && item.is_associated_const() => Some(SidebarLink { name, - url: get_next_url(used_links, format!("associatedconstant.{}", name)), + url: get_next_url(used_links, format!("{}.{}", ItemType::AssocConst, name)), }), _ => None, }) @@ -2106,7 +2155,7 @@ fn sidebar_deref_methods( debug!("found Deref: {:?}", impl_); if let Some((target, real_target)) = impl_.inner_impl().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_), }), @@ -2281,19 +2330,37 @@ fn sidebar_trait(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, t: &clean print_sidebar_section( buf, &t.items, - "associated-types", - "Associated Types", + "required-associated-types", + "Required Associated Types", + |m| m.is_ty_associated_type(), + |sym| format!("{0}", sym, ItemType::AssocType), + ); + + print_sidebar_section( + buf, + &t.items, + "provided-associated-types", + "Provided Associated Types", |m| m.is_associated_type(), - |sym| format!("{0}", sym), + |sym| format!("{0}", sym, ItemType::AssocType), + ); + + print_sidebar_section( + buf, + &t.items, + "required-associated-consts", + "Required Associated Constants", + |m| m.is_ty_associated_const(), + |sym| format!("{0}", sym, ItemType::AssocConst), ); print_sidebar_section( buf, &t.items, - "associated-const", - "Associated Constants", + "provided-associated-consts", + "Provided Associated Constants", |m| m.is_associated_const(), - |sym| format!("{0}", sym), + |sym| format!("{0}", sym, ItemType::AssocConst), ); print_sidebar_section( @@ -2302,7 +2369,7 @@ fn sidebar_trait(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, t: &clean "required-methods", "Required Methods", |m| m.is_ty_method(), - |sym| format!("{0}", sym), + |sym| format!("{0}", sym, ItemType::TyMethod), ); print_sidebar_section( @@ -2311,7 +2378,7 @@ fn sidebar_trait(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, t: &clean "provided-methods", "Provided Methods", |m| m.is_method(), - |sym| format!("{0}", sym), + |sym| format!("{0}", sym, ItemType::Method), ); let cache = cx.cache(); diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 99d7475da3396..1ed5c662c41cc 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -141,7 +141,7 @@ pub(super) fn print_item(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer, item_vars.render_into(buf).unwrap(); - match *item.kind { + match &*item.kind { clean::ModuleItem(ref m) => item_module(buf, cx, item, &m.items), clean::FunctionItem(ref f) | clean::ForeignFunctionItem(ref f) => { item_function(buf, cx, item, f) @@ -150,7 +150,7 @@ pub(super) fn print_item(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer, clean::StructItem(ref s) => item_struct(buf, cx, item, s), clean::UnionItem(ref s) => item_union(buf, cx, item, s), clean::EnumItem(ref e) => item_enum(buf, cx, item, e), - clean::TypedefItem(ref t, is_associated) => item_typedef(buf, cx, item, t, is_associated), + clean::TypedefItem(ref t) => item_typedef(buf, cx, item, t), clean::MacroItem(ref m) => item_macro(buf, cx, item, m), clean::ProcMacroItem(ref m) => item_proc_macro(buf, cx, item, m), clean::PrimitiveItem(_) => item_primitive(buf, cx, item), @@ -507,13 +507,15 @@ fn item_function(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, f: &clean:: fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) { let bounds = bounds(&t.bounds, false, cx); - let types = t.items.iter().filter(|m| m.is_associated_type()).collect::>(); - let consts = t.items.iter().filter(|m| m.is_associated_const()).collect::>(); - let required = t.items.iter().filter(|m| m.is_ty_method()).collect::>(); - let provided = t.items.iter().filter(|m| m.is_method()).collect::>(); - let count_types = types.len(); - let count_consts = consts.len(); - let count_methods = required.len() + provided.len(); + let required_types = t.items.iter().filter(|m| m.is_ty_associated_type()).collect::>(); + let provided_types = t.items.iter().filter(|m| m.is_associated_type()).collect::>(); + let required_consts = t.items.iter().filter(|m| m.is_ty_associated_const()).collect::>(); + let provided_consts = t.items.iter().filter(|m| m.is_associated_const()).collect::>(); + let required_methods = t.items.iter().filter(|m| m.is_ty_method()).collect::>(); + let provided_methods = t.items.iter().filter(|m| m.is_method()).collect::>(); + let count_types = required_types.len() + provided_types.len(); + let count_consts = required_consts.len() + provided_consts.len(); + let count_methods = required_methods.len() + provided_methods.len(); // Output the trait definition wrap_into_docblock(w, |w| { @@ -554,16 +556,18 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra ), ); } - for t in &types { - render_assoc_item( - w, - t, - AssocItemLink::Anchor(None), - ItemType::Trait, - cx, - RenderMode::Normal, - ); - w.write_str(";\n"); + for types in [&required_types, &provided_types] { + for t in types { + render_assoc_item( + w, + t, + AssocItemLink::Anchor(None), + ItemType::Trait, + cx, + RenderMode::Normal, + ); + w.write_str(";\n"); + } } // If there are too many associated constants, hide everything after them // We also do this if the types + consts is large because otherwise we could @@ -582,28 +586,30 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra ), ); } - if !types.is_empty() && !consts.is_empty() { + if count_types != 0 && (count_consts != 0 || count_methods != 0) { w.write_str("\n"); } - for t in &consts { - render_assoc_item( - w, - t, - AssocItemLink::Anchor(None), - ItemType::Trait, - cx, - RenderMode::Normal, - ); - w.write_str(";\n"); + for consts in [&required_consts, &provided_consts] { + for c in consts { + render_assoc_item( + w, + c, + AssocItemLink::Anchor(None), + ItemType::Trait, + cx, + RenderMode::Normal, + ); + w.write_str(";\n"); + } } if !toggle && should_hide_fields(count_methods) { toggle = true; toggle_open(w, format_args!("{} methods", count_methods)); } - if !consts.is_empty() && !required.is_empty() { + if count_consts != 0 && count_methods != 0 { w.write_str("\n"); } - for (pos, m) in required.iter().enumerate() { + for (pos, m) in required_methods.iter().enumerate() { render_assoc_item( w, m, @@ -614,14 +620,14 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra ); w.write_str(";\n"); - if pos < required.len() - 1 { + if pos < required_methods.len() - 1 { w.write_str(""); } } - if !required.is_empty() && !provided.is_empty() { + if !required_methods.is_empty() && !provided_methods.is_empty() { w.write_str("\n"); } - for (pos, m) in provided.iter().enumerate() { + for (pos, m) in provided_methods.iter().enumerate() { render_assoc_item( w, m, @@ -640,7 +646,8 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra w.write_str(" { ... }\n"); } } - if pos < provided.len() - 1 { + + if pos < provided_methods.len() - 1 { w.write_str(""); } } @@ -703,53 +710,77 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra } } - if !types.is_empty() { + if !required_types.is_empty() { write_small_section_header( w, - "associated-types", - "Associated Types", + "required-associated-types", + "Required Associated Types", "
", ); - for t in types { + for t in required_types { + trait_item(w, cx, t, it); + } + w.write_str("
"); + } + if !provided_types.is_empty() { + write_small_section_header( + w, + "provided-associated-types", + "Provided Associated Types", + "
", + ); + for t in provided_types { trait_item(w, cx, t, it); } w.write_str("
"); } - if !consts.is_empty() { + if !required_consts.is_empty() { + write_small_section_header( + w, + "required-associated-consts", + "Required Associated Constants", + "
", + ); + for t in required_consts { + trait_item(w, cx, t, it); + } + w.write_str("
"); + } + if !provided_consts.is_empty() { write_small_section_header( w, - "associated-const", - "Associated Constants", + "provided-associated-consts", + "Provided Associated Constants", "
", ); - for t in consts { + for t in provided_consts { trait_item(w, cx, t, it); } w.write_str("
"); } // Output the documentation for each function individually - if !required.is_empty() { + if !required_methods.is_empty() { write_small_section_header( w, "required-methods", - "Required methods", + "Required Methods", "
", ); - for m in required { + for m in required_methods { trait_item(w, cx, m, it); } w.write_str("
"); } - if !provided.is_empty() { + if !provided_methods.is_empty() { write_small_section_header( w, "provided-methods", - "Provided methods", + "Provided Methods", "
", ); - for m in provided { + for m in provided_methods { trait_item(w, cx, m, it); } w.write_str("
"); @@ -933,25 +964,11 @@ fn item_opaque_ty(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean: render_assoc_items(w, cx, it, it.def_id.expect_def_id(), AssocItemRender::All) } -fn item_typedef( - w: &mut Buffer, - cx: &Context<'_>, - it: &clean::Item, - t: &clean::Typedef, - is_associated: bool, -) { - fn write_content( - w: &mut Buffer, - cx: &Context<'_>, - it: &clean::Item, - t: &clean::Typedef, - is_associated: bool, - ) { +fn item_typedef(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Typedef) { + fn write_content(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Typedef) { wrap_item(w, "typedef", |w| { render_attributes_in_pre(w, it, ""); - if !is_associated { - write!(w, "{}", it.visibility.print_with_space(it.def_id, cx)); - } + write!(w, "{}", it.visibility.print_with_space(it.def_id, cx)); write!( w, "type {}{}{where_clause} = {type_};", @@ -963,14 +980,7 @@ fn item_typedef( }); } - // If this is an associated typedef, we don't want to wrap it into a docblock. - if is_associated { - write_content(w, cx, it, t, is_associated); - } else { - wrap_into_docblock(w, |w| { - write_content(w, cx, it, t, is_associated); - }); - } + wrap_into_docblock(w, |w| write_content(w, cx, it, t)); document(w, cx, it, None, HeadingOffset::H2); diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index b0ce63a4ec1d5..ab52304491a2b 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -3,7 +3,7 @@ (function() { // This mapping table should match the discriminants of -// `rustdoc::html::item_type::ItemType` type in Rust. +// `rustdoc::formats::item_type::ItemType` type in Rust. var itemTypes = [ "mod", "externcrate", diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index bc638200533fd..a9a6a31fccd0d 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -219,20 +219,23 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum { StaticItem(s) => ItemEnum::Static(s.into_tcx(tcx)), ForeignStaticItem(s) => ItemEnum::Static(s.into_tcx(tcx)), ForeignTypeItem => ItemEnum::ForeignType, - TypedefItem(t, _) => ItemEnum::Typedef(t.into_tcx(tcx)), + TypedefItem(t) => ItemEnum::Typedef(t.into_tcx(tcx)), OpaqueTyItem(t) => ItemEnum::OpaqueTy(t.into_tcx(tcx)), ConstantItem(c) => ItemEnum::Constant(c.into_tcx(tcx)), MacroItem(m) => ItemEnum::Macro(m.source), ProcMacroItem(m) => ItemEnum::ProcMacro(m.into_tcx(tcx)), PrimitiveItem(p) => ItemEnum::PrimitiveType(p.as_sym().to_string()), + TyAssocConstItem(ty) => ItemEnum::AssocConst { type_: ty.into_tcx(tcx), default: None }, AssocConstItem(ty, default) => { - ItemEnum::AssocConst { type_: ty.into_tcx(tcx), default: default.map(|c| c.expr(tcx)) } + ItemEnum::AssocConst { type_: ty.into_tcx(tcx), default: Some(default.expr(tcx)) } } - AssocTypeItem(g, b, t) => ItemEnum::AssocType { + TyAssocTypeItem(g, b) => ItemEnum::AssocType { generics: (*g).into_tcx(tcx), bounds: b.into_iter().map(|x| x.into_tcx(tcx)).collect(), - default: t.map(|x| x.into_tcx(tcx)), + default: None, }, + // FIXME: do not map to Typedef but to a custom variant + AssocTypeItem(t, _) => ItemEnum::Typedef(t.into_tcx(tcx)), // `convert_item` early returns `None` for striped items StrippedItem(_) => unreachable!(), KeywordItem(_) => { diff --git a/src/librustdoc/passes/check_doc_test_visibility.rs b/src/librustdoc/passes/check_doc_test_visibility.rs index 2b17e3457d293..b541fb63bd413 100644 --- a/src/librustdoc/passes/check_doc_test_visibility.rs +++ b/src/librustdoc/passes/check_doc_test_visibility.rs @@ -61,9 +61,9 @@ crate fn should_have_doc_example(cx: &DocContext<'_>, item: &clean::Item) -> boo *item.kind, clean::StructFieldItem(_) | clean::VariantItem(_) - | clean::AssocConstItem(_, _) + | clean::AssocConstItem(..) | clean::AssocTypeItem(..) - | clean::TypedefItem(_, _) + | clean::TypedefItem(_) | clean::StaticItem(_) | clean::ConstantItem(_) | clean::ExternCrateItem { .. } diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs index 2852c3b616df1..4ab942c8f1bdc 100644 --- a/src/librustdoc/passes/collect_trait_impls.rs +++ b/src/librustdoc/passes/collect_trait_impls.rs @@ -93,7 +93,7 @@ crate fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) -> Crate let target = items .iter() .find_map(|item| match *item.kind { - TypedefItem(ref t, true) => Some(&t.type_), + AssocTypeItem(ref t, _) => Some(&t.type_), _ => None, }) .expect("Deref impl without Target type"); diff --git a/src/librustdoc/passes/stripper.rs b/src/librustdoc/passes/stripper.rs index 717dc078b343c..82627aaf7a4cb 100644 --- a/src/librustdoc/passes/stripper.rs +++ b/src/librustdoc/passes/stripper.rs @@ -41,6 +41,7 @@ impl<'a> DocFolder for Stripper<'a> { | clean::ConstantItem(..) | clean::UnionItem(..) | clean::AssocConstItem(..) + | clean::AssocTypeItem(..) | clean::TraitAliasItem(..) | clean::MacroItem(..) | clean::ForeignTypeItem => { @@ -72,8 +73,8 @@ impl<'a> DocFolder for Stripper<'a> { clean::ImplItem(..) => {} - // tymethods have no control over privacy - clean::TyMethodItem(..) => {} + // tymethods etc. have no control over privacy + clean::TyMethodItem(..) | clean::TyAssocConstItem(..) | clean::TyAssocTypeItem(..) => {} // Proc-macros are always public clean::ProcMacroItem(..) => {} @@ -81,9 +82,6 @@ impl<'a> DocFolder for Stripper<'a> { // Primitives are never stripped clean::PrimitiveItem(..) => {} - // Associated types are never stripped - clean::AssocTypeItem(..) => {} - // Keywords are never stripped clean::KeywordItem(..) => {} } diff --git a/src/librustdoc/visit.rs b/src/librustdoc/visit.rs index b16cab1c646f1..ef50292674276 100644 --- a/src/librustdoc/visit.rs +++ b/src/librustdoc/visit.rs @@ -26,7 +26,7 @@ crate trait DocVisitor: Sized { ExternCrateItem { src: _ } | ImportItem(_) | FunctionItem(_) - | TypedefItem(_, _) + | TypedefItem(_) | OpaqueTyItem(_) | StaticItem(_) | ConstantItem(_) @@ -40,7 +40,9 @@ crate trait DocVisitor: Sized { | MacroItem(_) | ProcMacroItem(_) | PrimitiveItem(_) - | AssocConstItem(_, _) + | TyAssocConstItem(..) + | AssocConstItem(..) + | TyAssocTypeItem(..) | AssocTypeItem(..) | KeywordItem(_) => {} } diff --git a/src/test/rustdoc/assoc-consts.rs b/src/test/rustdoc/assoc-consts.rs index ff7fd66391628..0ac6dc763df88 100644 --- a/src/test/rustdoc/assoc-consts.rs +++ b/src/test/rustdoc/assoc-consts.rs @@ -1,8 +1,8 @@ pub trait Foo { // @has assoc_consts/trait.Foo.html '//*[@class="rust trait"]' \ - // 'const FOO: usize;' + // 'const FOO: usize = 13usize;' // @has - '//*[@id="associatedconstant.FOO"]' 'const FOO: usize' - const FOO: usize = 12; + const FOO: usize = 12 + 1; // @has - '//*[@id="associatedconstant.FOO_NO_DEFAULT"]' 'const FOO_NO_DEFAULT: bool' const FOO_NO_DEFAULT: bool; // @!has - FOO_HIDDEN diff --git a/src/test/rustdoc/extern-default-method.no_href_on_anchor.html b/src/test/rustdoc/extern-default-method.no_href_on_anchor.html new file mode 100644 index 0000000000000..dab0a64952955 --- /dev/null +++ b/src/test/rustdoc/extern-default-method.no_href_on_anchor.html @@ -0,0 +1 @@ +provided(&self) \ No newline at end of file diff --git a/src/test/rustdoc/extern-default-method.rs b/src/test/rustdoc/extern-default-method.rs index 93cf16346b6f7..8139f5b2619b3 100644 --- a/src/test/rustdoc/extern-default-method.rs +++ b/src/test/rustdoc/extern-default-method.rs @@ -1,9 +1,23 @@ // aux-build:rustdoc-extern-default-method.rs // ignore-cross-compile +// ignore-tidy-linelength extern crate rustdoc_extern_default_method as ext; +// For this test, the dependency is compiled but not documented. +// +// Still, the struct from the external crate and its impl should be documented since +// the struct is re-exported from this crate. +// However, the method in the trait impl should *not* have a link (an `href` attribute) to +// its corresponding item in the trait declaration since it would otherwise be broken. +// +// In older versions of rustdoc, the impl item (`a[@class="fnname"]`) used to link to +// `#method.provided` – i.e. "to itself". Put in quotes since that was actually incorrect in +// general: If the type `Struct` also had an inherent method called `provided`, the impl item +// would link to that one even though those two methods are distinct items! + // @count extern_default_method/struct.Struct.html '//*[@id="method.provided"]' 1 -// @has extern_default_method/struct.Struct.html '//*[@id="method.provided"]//a[@class="fnname"]/@href' #method.provided +// @count extern_default_method/struct.Struct.html '//*[@id="method.provided"]//a[@class="fnname"]' 1 +// @snapshot no_href_on_anchor - '//*[@id="method.provided"]//a[@class="fnname"]' // @has extern_default_method/struct.Struct.html '//*[@id="method.provided"]//a[@class="anchor"]/@href' #method.provided pub use ext::Struct; diff --git a/src/test/rustdoc/intra-doc/prim-self.rs b/src/test/rustdoc/intra-doc/prim-self.rs index de053d70f0354..c7ce71b15f30c 100644 --- a/src/test/rustdoc/intra-doc/prim-self.rs +++ b/src/test/rustdoc/intra-doc/prim-self.rs @@ -5,6 +5,7 @@ #![feature(no_core)] #![feature(rustdoc_internals)] #![feature(inherent_associated_types)] +#![feature(lang_items)] #![no_core] /// [Self::f] @@ -35,3 +36,6 @@ pub struct S; impl S { pub fn f() {} } + +#[lang = "sized"] +pub trait Sized {} diff --git a/src/test/rustdoc/sidebar-items.rs b/src/test/rustdoc/sidebar-items.rs index 375cad9da7f88..b5b681ab085bf 100644 --- a/src/test/rustdoc/sidebar-items.rs +++ b/src/test/rustdoc/sidebar-items.rs @@ -1,3 +1,4 @@ +#![feature(associated_type_defaults)] #![crate_name = "foo"] // @has foo/trait.Foo.html @@ -5,12 +6,18 @@ // @has - '//*[@class="sidebar-elems"]//section//a' 'bar' // @has - '//*[@class="sidebar-title"]/a[@href="#provided-methods"]' 'Provided Methods' // @has - '//*[@class="sidebar-elems"]//section//a' 'foo' -// @has - '//*[@class="sidebar-title"]/a[@href="#associated-const"]' 'Associated Constants' +// @has - '//*[@class="sidebar-title"]/a[@href="#required-associated-consts"]' 'Required Associated Constants' +// @has - '//*[@class="sidebar-elems"]//section//a' 'FOO' +// @has - '//*[@class="sidebar-title"]/a[@href="#provided-associated-consts"]' 'Provided Associated Constants' // @has - '//*[@class="sidebar-elems"]//section//a' 'BAR' -// @has - '//*[@class="sidebar-title"]/a[@href="#associated-types"]' 'Associated Types' +// @has - '//*[@class="sidebar-title"]/a[@href="#required-associated-types"]' 'Required Associated Types' // @has - '//*[@class="sidebar-elems"]//section//a' 'Output' +// @has - '//*[@class="sidebar-title"]/a[@href="#provided-associated-types"]' 'Provided Associated Types' +// @has - '//*[@class="sidebar-elems"]//section//a' 'Extra' pub trait Foo { + const FOO: usize; const BAR: u32 = 0; + type Extra: Copy = (); type Output: ?Sized; fn foo() {} From a21238cc292ff134107e77a71c5e9ee419f6b42c Mon Sep 17 00:00:00 2001 From: Jane Lusby Date: Tue, 12 Apr 2022 10:48:56 -0700 Subject: [PATCH 10/23] Autotag library PRs with T-libs --- triagebot.toml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/triagebot.toml b/triagebot.toml index f6f1b918f061f..a5724ddfb88eb 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -136,6 +136,21 @@ exclude_labels = [ "T-*", ] +[autolabel."T-libs"] +trigger_files = [ + "library/alloc", + "library/core", + "library/panic_abort", + "library/panic_unwind", + "library/std", + "library/stdarch", + "library/term", + "library/test", +] +exclude_labels = [ + "T-*", +] + [notify-zulip."I-prioritize"] zulip_stream = 245100 # #t-compiler/wg-prioritization/alerts topic = "#{number} {title}" From e9a52c27d2bca3b39742dcc89c2b5ee643660fec Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 26 Mar 2022 20:59:09 +0100 Subject: [PATCH 11/23] Move ident resolution to a dedicated module. --- compiler/rustc_resolve/src/ident.rs | 1645 +++++++++++++++++++++++++ compiler/rustc_resolve/src/imports.rs | 289 +---- compiler/rustc_resolve/src/lib.rs | 1005 +-------------- compiler/rustc_resolve/src/macros.rs | 372 +----- 4 files changed, 1668 insertions(+), 1643 deletions(-) create mode 100644 compiler/rustc_resolve/src/ident.rs diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs new file mode 100644 index 0000000000000..0111ab506c54f --- /dev/null +++ b/compiler/rustc_resolve/src/ident.rs @@ -0,0 +1,1645 @@ +use rustc_ast::{self as ast, NodeId}; +use rustc_ast_pretty::pprust; +use rustc_errors::Applicability; +use rustc_feature::is_builtin_attr_name; +use rustc_hir::def::{DefKind, Namespace, NonMacroAttrKind, PartialRes, PerNS}; +use rustc_hir::PrimTy; +use rustc_middle::bug; +use rustc_middle::ty; +use rustc_session::lint::builtin::PROC_MACRO_DERIVE_RESOLUTION_FALLBACK; +use rustc_session::lint::BuiltinLintDiagnostics; +use rustc_span::edition::Edition; +use rustc_span::hygiene::{ExpnId, ExpnKind, LocalExpnId, MacroKind, SyntaxContext}; +use rustc_span::symbol::{kw, sym, Ident}; +use rustc_span::{Span, DUMMY_SP}; + +use std::ptr; + +use crate::late::{ConstantItemKind, HasGenericParams, PathSource, Rib, RibKind}; +use crate::macros::{sub_namespace_match, MacroRulesScope}; +use crate::{AmbiguityError, AmbiguityErrorMisc, AmbiguityKind, Determinacy, Finalize}; +use crate::{ + ImportKind, LexicalScopeBinding, Module, ModuleKind, ModuleOrUniformRoot, NameBinding, +}; +use crate::{ + NameBindingKind, ParentScope, PathResult, PrivacyError, Res, ResolutionError, Resolver, Scope, + ScopeSet, Segment, +}; +use crate::{ToNameBinding, Weak}; + +use Determinacy::*; +use Namespace::*; +use RibKind::*; + +impl<'a> Resolver<'a> { + /// A generic scope visitor. + /// Visits scopes in order to resolve some identifier in them or perform other actions. + /// If the callback returns `Some` result, we stop visiting scopes and return it. + crate fn visit_scopes( + &mut self, + scope_set: ScopeSet<'a>, + parent_scope: &ParentScope<'a>, + ctxt: SyntaxContext, + mut visitor: impl FnMut( + &mut Self, + Scope<'a>, + /*use_prelude*/ bool, + SyntaxContext, + ) -> Option, + ) -> Option { + // General principles: + // 1. Not controlled (user-defined) names should have higher priority than controlled names + // built into the language or standard library. This way we can add new names into the + // language or standard library without breaking user code. + // 2. "Closed set" below means new names cannot appear after the current resolution attempt. + // Places to search (in order of decreasing priority): + // (Type NS) + // 1. FIXME: Ribs (type parameters), there's no necessary infrastructure yet + // (open set, not controlled). + // 2. Names in modules (both normal `mod`ules and blocks), loop through hygienic parents + // (open, not controlled). + // 3. Extern prelude (open, the open part is from macro expansions, not controlled). + // 4. Tool modules (closed, controlled right now, but not in the future). + // 5. Standard library prelude (de-facto closed, controlled). + // 6. Language prelude (closed, controlled). + // (Value NS) + // 1. FIXME: Ribs (local variables), there's no necessary infrastructure yet + // (open set, not controlled). + // 2. Names in modules (both normal `mod`ules and blocks), loop through hygienic parents + // (open, not controlled). + // 3. Standard library prelude (de-facto closed, controlled). + // (Macro NS) + // 1-3. Derive helpers (open, not controlled). All ambiguities with other names + // are currently reported as errors. They should be higher in priority than preludes + // and probably even names in modules according to the "general principles" above. They + // also should be subject to restricted shadowing because are effectively produced by + // derives (you need to resolve the derive first to add helpers into scope), but they + // should be available before the derive is expanded for compatibility. + // It's mess in general, so we are being conservative for now. + // 1-3. `macro_rules` (open, not controlled), loop through `macro_rules` scopes. Have higher + // priority than prelude macros, but create ambiguities with macros in modules. + // 1-3. Names in modules (both normal `mod`ules and blocks), loop through hygienic parents + // (open, not controlled). Have higher priority than prelude macros, but create + // ambiguities with `macro_rules`. + // 4. `macro_use` prelude (open, the open part is from macro expansions, not controlled). + // 4a. User-defined prelude from macro-use + // (open, the open part is from macro expansions, not controlled). + // 4b. "Standard library prelude" part implemented through `macro-use` (closed, controlled). + // 4c. Standard library prelude (de-facto closed, controlled). + // 6. Language prelude: builtin attributes (closed, controlled). + + let rust_2015 = ctxt.edition() == Edition::Edition2015; + let (ns, macro_kind, is_absolute_path) = match scope_set { + ScopeSet::All(ns, _) => (ns, None, false), + ScopeSet::AbsolutePath(ns) => (ns, None, true), + ScopeSet::Macro(macro_kind) => (MacroNS, Some(macro_kind), false), + ScopeSet::Late(ns, ..) => (ns, None, false), + }; + let module = match scope_set { + // Start with the specified module. + ScopeSet::Late(_, module, _) => module, + // Jump out of trait or enum modules, they do not act as scopes. + _ => parent_scope.module.nearest_item_scope(), + }; + let mut scope = match ns { + _ if is_absolute_path => Scope::CrateRoot, + TypeNS | ValueNS => Scope::Module(module, None), + MacroNS => Scope::DeriveHelpers(parent_scope.expansion), + }; + let mut ctxt = ctxt.normalize_to_macros_2_0(); + let mut use_prelude = !module.no_implicit_prelude; + + loop { + let visit = match scope { + // Derive helpers are not in scope when resolving derives in the same container. + Scope::DeriveHelpers(expn_id) => { + !(expn_id == parent_scope.expansion && macro_kind == Some(MacroKind::Derive)) + } + Scope::DeriveHelpersCompat => true, + Scope::MacroRules(macro_rules_scope) => { + // Use "path compression" on `macro_rules` scope chains. This is an optimization + // used to avoid long scope chains, see the comments on `MacroRulesScopeRef`. + // As another consequence of this optimization visitors never observe invocation + // scopes for macros that were already expanded. + while let MacroRulesScope::Invocation(invoc_id) = macro_rules_scope.get() { + if let Some(next_scope) = self.output_macro_rules_scopes.get(&invoc_id) { + macro_rules_scope.set(next_scope.get()); + } else { + break; + } + } + true + } + Scope::CrateRoot => true, + Scope::Module(..) => true, + Scope::RegisteredAttrs => use_prelude, + Scope::MacroUsePrelude => use_prelude || rust_2015, + Scope::BuiltinAttrs => true, + Scope::ExternPrelude => use_prelude || is_absolute_path, + Scope::ToolPrelude => use_prelude, + Scope::StdLibPrelude => use_prelude || ns == MacroNS, + Scope::BuiltinTypes => true, + }; + + if visit { + if let break_result @ Some(..) = visitor(self, scope, use_prelude, ctxt) { + return break_result; + } + } + + scope = match scope { + Scope::DeriveHelpers(LocalExpnId::ROOT) => Scope::DeriveHelpersCompat, + Scope::DeriveHelpers(expn_id) => { + // Derive helpers are not visible to code generated by bang or derive macros. + let expn_data = expn_id.expn_data(); + match expn_data.kind { + ExpnKind::Root + | ExpnKind::Macro(MacroKind::Bang | MacroKind::Derive, _) => { + Scope::DeriveHelpersCompat + } + _ => Scope::DeriveHelpers(expn_data.parent.expect_local()), + } + } + Scope::DeriveHelpersCompat => Scope::MacroRules(parent_scope.macro_rules), + Scope::MacroRules(macro_rules_scope) => match macro_rules_scope.get() { + MacroRulesScope::Binding(binding) => { + Scope::MacroRules(binding.parent_macro_rules_scope) + } + MacroRulesScope::Invocation(invoc_id) => { + Scope::MacroRules(self.invocation_parent_scopes[&invoc_id].macro_rules) + } + MacroRulesScope::Empty => Scope::Module(module, None), + }, + Scope::CrateRoot => match ns { + TypeNS => { + ctxt.adjust(ExpnId::root()); + Scope::ExternPrelude + } + ValueNS | MacroNS => break, + }, + Scope::Module(module, prev_lint_id) => { + use_prelude = !module.no_implicit_prelude; + let derive_fallback_lint_id = match scope_set { + ScopeSet::Late(.., lint_id) => lint_id, + _ => None, + }; + match self.hygienic_lexical_parent(module, &mut ctxt, derive_fallback_lint_id) { + Some((parent_module, lint_id)) => { + Scope::Module(parent_module, lint_id.or(prev_lint_id)) + } + None => { + ctxt.adjust(ExpnId::root()); + match ns { + TypeNS => Scope::ExternPrelude, + ValueNS => Scope::StdLibPrelude, + MacroNS => Scope::RegisteredAttrs, + } + } + } + } + Scope::RegisteredAttrs => Scope::MacroUsePrelude, + Scope::MacroUsePrelude => Scope::StdLibPrelude, + Scope::BuiltinAttrs => break, // nowhere else to search + Scope::ExternPrelude if is_absolute_path => break, + Scope::ExternPrelude => Scope::ToolPrelude, + Scope::ToolPrelude => Scope::StdLibPrelude, + Scope::StdLibPrelude => match ns { + TypeNS => Scope::BuiltinTypes, + ValueNS => break, // nowhere else to search + MacroNS => Scope::BuiltinAttrs, + }, + Scope::BuiltinTypes => break, // nowhere else to search + }; + } + + None + } + + fn hygienic_lexical_parent( + &mut self, + module: Module<'a>, + ctxt: &mut SyntaxContext, + derive_fallback_lint_id: Option, + ) -> Option<(Module<'a>, Option)> { + if !module.expansion.outer_expn_is_descendant_of(*ctxt) { + return Some((self.expn_def_scope(ctxt.remove_mark()), None)); + } + + if let ModuleKind::Block(..) = module.kind { + return Some((module.parent.unwrap().nearest_item_scope(), None)); + } + + // We need to support the next case under a deprecation warning + // ``` + // struct MyStruct; + // ---- begin: this comes from a proc macro derive + // mod implementation_details { + // // Note that `MyStruct` is not in scope here. + // impl SomeTrait for MyStruct { ... } + // } + // ---- end + // ``` + // So we have to fall back to the module's parent during lexical resolution in this case. + if derive_fallback_lint_id.is_some() { + if let Some(parent) = module.parent { + // Inner module is inside the macro, parent module is outside of the macro. + if module.expansion != parent.expansion + && module.expansion.is_descendant_of(parent.expansion) + { + // The macro is a proc macro derive + if let Some(def_id) = module.expansion.expn_data().macro_def_id { + let ext = self.get_macro_by_def_id(def_id); + if ext.builtin_name.is_none() + && ext.macro_kind() == MacroKind::Derive + && parent.expansion.outer_expn_is_descendant_of(*ctxt) + { + return Some((parent, derive_fallback_lint_id)); + } + } + } + } + } + + None + } + + /// This resolves the identifier `ident` in the namespace `ns` in the current lexical scope. + /// More specifically, we proceed up the hierarchy of scopes and return the binding for + /// `ident` in the first scope that defines it (or None if no scopes define it). + /// + /// A block's items are above its local variables in the scope hierarchy, regardless of where + /// the items are defined in the block. For example, + /// ```rust + /// fn f() { + /// g(); // Since there are no local variables in scope yet, this resolves to the item. + /// let g = || {}; + /// fn g() {} + /// g(); // This resolves to the local variable `g` since it shadows the item. + /// } + /// ``` + /// + /// Invariant: This must only be called during main resolution, not during + /// import resolution. + crate fn resolve_ident_in_lexical_scope( + &mut self, + mut ident: Ident, + ns: Namespace, + parent_scope: &ParentScope<'a>, + finalize_full: Finalize, + ribs: &[Rib<'a>], + ) -> Option> { + assert!(ns == TypeNS || ns == ValueNS); + let orig_ident = ident; + if ident.name == kw::Empty { + return Some(LexicalScopeBinding::Res(Res::Err)); + } + let (general_span, normalized_span) = if ident.name == kw::SelfUpper { + // FIXME(jseyfried) improve `Self` hygiene + let empty_span = ident.span.with_ctxt(SyntaxContext::root()); + (empty_span, empty_span) + } else if ns == TypeNS { + let normalized_span = ident.span.normalize_to_macros_2_0(); + (normalized_span, normalized_span) + } else { + (ident.span.normalize_to_macro_rules(), ident.span.normalize_to_macros_2_0()) + }; + ident.span = general_span; + let normalized_ident = Ident { span: normalized_span, ..ident }; + + // Walk backwards up the ribs in scope. + let finalize = finalize_full.path_span(); + let mut module = self.graph_root; + for i in (0..ribs.len()).rev() { + debug!("walk rib\n{:?}", ribs[i].bindings); + // Use the rib kind to determine whether we are resolving parameters + // (macro 2.0 hygiene) or local variables (`macro_rules` hygiene). + let rib_ident = if ribs[i].kind.contains_params() { normalized_ident } else { ident }; + if let Some((original_rib_ident_def, res)) = ribs[i].bindings.get_key_value(&rib_ident) + { + // The ident resolves to a type parameter or local variable. + return Some(LexicalScopeBinding::Res(self.validate_res_from_ribs( + i, + rib_ident, + *res, + finalize, + *original_rib_ident_def, + ribs, + ))); + } + + module = match ribs[i].kind { + ModuleRibKind(module) => module, + MacroDefinition(def) if def == self.macro_def(ident.span.ctxt()) => { + // If an invocation of this macro created `ident`, give up on `ident` + // and switch to `ident`'s source from the macro definition. + ident.span.remove_mark(); + continue; + } + _ => continue, + }; + + match module.kind { + ModuleKind::Block(..) => {} // We can see through blocks + _ => break, + } + + let item = self.resolve_ident_in_module_unadjusted( + ModuleOrUniformRoot::Module(module), + ident, + ns, + parent_scope, + finalize, + ); + if let Ok(binding) = item { + // The ident resolves to an item. + return Some(LexicalScopeBinding::Item(binding)); + } + } + self.early_resolve_ident_in_lexical_scope( + orig_ident, + ScopeSet::Late(ns, module, finalize_full.node_id()), + parent_scope, + finalize, + finalize.is_some(), + ) + .ok() + .map(LexicalScopeBinding::Item) + } + + /// Resolve an identifier in lexical scope. + /// This is a variation of `fn resolve_ident_in_lexical_scope` that can be run during + /// expansion and import resolution (perhaps they can be merged in the future). + /// The function is used for resolving initial segments of macro paths (e.g., `foo` in + /// `foo::bar!(); or `foo!();`) and also for import paths on 2018 edition. + crate fn early_resolve_ident_in_lexical_scope( + &mut self, + orig_ident: Ident, + scope_set: ScopeSet<'a>, + parent_scope: &ParentScope<'a>, + finalize: Option, + force: bool, + ) -> Result<&'a NameBinding<'a>, Determinacy> { + bitflags::bitflags! { + struct Flags: u8 { + const MACRO_RULES = 1 << 0; + const MODULE = 1 << 1; + const MISC_SUGGEST_CRATE = 1 << 2; + const MISC_SUGGEST_SELF = 1 << 3; + const MISC_FROM_PRELUDE = 1 << 4; + } + } + + assert!(force || !finalize.is_some()); // `finalize` implies `force` + + // Make sure `self`, `super` etc produce an error when passed to here. + if orig_ident.is_path_segment_keyword() { + return Err(Determinacy::Determined); + } + + let (ns, macro_kind, is_import) = match scope_set { + ScopeSet::All(ns, is_import) => (ns, None, is_import), + ScopeSet::AbsolutePath(ns) => (ns, None, false), + ScopeSet::Macro(macro_kind) => (MacroNS, Some(macro_kind), false), + ScopeSet::Late(ns, ..) => (ns, None, false), + }; + + // This is *the* result, resolution from the scope closest to the resolved identifier. + // However, sometimes this result is "weak" because it comes from a glob import or + // a macro expansion, and in this case it cannot shadow names from outer scopes, e.g. + // mod m { ... } // solution in outer scope + // { + // use prefix::*; // imports another `m` - innermost solution + // // weak, cannot shadow the outer `m`, need to report ambiguity error + // m::mac!(); + // } + // So we have to save the innermost solution and continue searching in outer scopes + // to detect potential ambiguities. + let mut innermost_result: Option<(&NameBinding<'_>, Flags)> = None; + let mut determinacy = Determinacy::Determined; + + // Go through all the scopes and try to resolve the name. + let break_result = self.visit_scopes( + scope_set, + parent_scope, + orig_ident.span.ctxt(), + |this, scope, use_prelude, ctxt| { + let ident = Ident::new(orig_ident.name, orig_ident.span.with_ctxt(ctxt)); + let ok = |res, span, arenas| { + Ok(( + (res, ty::Visibility::Public, span, LocalExpnId::ROOT) + .to_name_binding(arenas), + Flags::empty(), + )) + }; + let result = match scope { + Scope::DeriveHelpers(expn_id) => { + if let Some(attr) = this + .helper_attrs + .get(&expn_id) + .and_then(|attrs| attrs.iter().rfind(|i| ident == **i)) + { + let binding = ( + Res::NonMacroAttr(NonMacroAttrKind::DeriveHelper), + ty::Visibility::Public, + attr.span, + expn_id, + ) + .to_name_binding(this.arenas); + Ok((binding, Flags::empty())) + } else { + Err(Determinacy::Determined) + } + } + Scope::DeriveHelpersCompat => { + let mut result = Err(Determinacy::Determined); + for derive in parent_scope.derives { + let parent_scope = &ParentScope { derives: &[], ..*parent_scope }; + match this.resolve_macro_path( + derive, + Some(MacroKind::Derive), + parent_scope, + true, + force, + ) { + Ok((Some(ext), _)) => { + if ext.helper_attrs.contains(&ident.name) { + result = ok( + Res::NonMacroAttr(NonMacroAttrKind::DeriveHelperCompat), + derive.span, + this.arenas, + ); + break; + } + } + Ok(_) | Err(Determinacy::Determined) => {} + Err(Determinacy::Undetermined) => { + result = Err(Determinacy::Undetermined) + } + } + } + result + } + Scope::MacroRules(macro_rules_scope) => match macro_rules_scope.get() { + MacroRulesScope::Binding(macro_rules_binding) + if ident == macro_rules_binding.ident => + { + Ok((macro_rules_binding.binding, Flags::MACRO_RULES)) + } + MacroRulesScope::Invocation(_) => Err(Determinacy::Undetermined), + _ => Err(Determinacy::Determined), + }, + Scope::CrateRoot => { + let root_ident = Ident::new(kw::PathRoot, ident.span); + let root_module = this.resolve_crate_root(root_ident); + let binding = this.resolve_ident_in_module_ext( + ModuleOrUniformRoot::Module(root_module), + ident, + ns, + parent_scope, + finalize, + ); + match binding { + Ok(binding) => Ok((binding, Flags::MODULE | Flags::MISC_SUGGEST_CRATE)), + Err((Determinacy::Undetermined, Weak::No)) => { + return Some(Err(Determinacy::determined(force))); + } + Err((Determinacy::Undetermined, Weak::Yes)) => { + Err(Determinacy::Undetermined) + } + Err((Determinacy::Determined, _)) => Err(Determinacy::Determined), + } + } + Scope::Module(module, derive_fallback_lint_id) => { + let adjusted_parent_scope = &ParentScope { module, ..*parent_scope }; + let binding = this.resolve_ident_in_module_unadjusted_ext( + ModuleOrUniformRoot::Module(module), + ident, + ns, + adjusted_parent_scope, + !matches!(scope_set, ScopeSet::Late(..)), + finalize, + ); + match binding { + Ok(binding) => { + if let Some(lint_id) = derive_fallback_lint_id { + this.lint_buffer.buffer_lint_with_diagnostic( + PROC_MACRO_DERIVE_RESOLUTION_FALLBACK, + lint_id, + orig_ident.span, + &format!( + "cannot find {} `{}` in this scope", + ns.descr(), + ident + ), + BuiltinLintDiagnostics::ProcMacroDeriveResolutionFallback( + orig_ident.span, + ), + ); + } + let misc_flags = if ptr::eq(module, this.graph_root) { + Flags::MISC_SUGGEST_CRATE + } else if module.is_normal() { + Flags::MISC_SUGGEST_SELF + } else { + Flags::empty() + }; + Ok((binding, Flags::MODULE | misc_flags)) + } + Err((Determinacy::Undetermined, Weak::No)) => { + return Some(Err(Determinacy::determined(force))); + } + Err((Determinacy::Undetermined, Weak::Yes)) => { + Err(Determinacy::Undetermined) + } + Err((Determinacy::Determined, _)) => Err(Determinacy::Determined), + } + } + Scope::RegisteredAttrs => match this.registered_attrs.get(&ident).cloned() { + Some(ident) => ok( + Res::NonMacroAttr(NonMacroAttrKind::Registered), + ident.span, + this.arenas, + ), + None => Err(Determinacy::Determined), + }, + Scope::MacroUsePrelude => { + match this.macro_use_prelude.get(&ident.name).cloned() { + Some(binding) => Ok((binding, Flags::MISC_FROM_PRELUDE)), + None => Err(Determinacy::determined( + this.graph_root.unexpanded_invocations.borrow().is_empty(), + )), + } + } + Scope::BuiltinAttrs => { + if is_builtin_attr_name(ident.name) { + ok( + Res::NonMacroAttr(NonMacroAttrKind::Builtin(ident.name)), + DUMMY_SP, + this.arenas, + ) + } else { + Err(Determinacy::Determined) + } + } + Scope::ExternPrelude => { + match this.extern_prelude_get(ident, finalize.is_some()) { + Some(binding) => Ok((binding, Flags::empty())), + None => Err(Determinacy::determined( + this.graph_root.unexpanded_invocations.borrow().is_empty(), + )), + } + } + Scope::ToolPrelude => match this.registered_tools.get(&ident).cloned() { + Some(ident) => ok(Res::ToolMod, ident.span, this.arenas), + None => Err(Determinacy::Determined), + }, + Scope::StdLibPrelude => { + let mut result = Err(Determinacy::Determined); + if let Some(prelude) = this.prelude { + if let Ok(binding) = this.resolve_ident_in_module_unadjusted( + ModuleOrUniformRoot::Module(prelude), + ident, + ns, + parent_scope, + None, + ) { + if use_prelude || this.is_builtin_macro(binding.res()) { + result = Ok((binding, Flags::MISC_FROM_PRELUDE)); + } + } + } + result + } + Scope::BuiltinTypes => match PrimTy::from_name(ident.name) { + Some(prim_ty) => ok(Res::PrimTy(prim_ty), DUMMY_SP, this.arenas), + None => Err(Determinacy::Determined), + }, + }; + + match result { + Ok((binding, flags)) + if sub_namespace_match(binding.macro_kind(), macro_kind) => + { + if finalize.is_none() || matches!(scope_set, ScopeSet::Late(..)) { + return Some(Ok(binding)); + } + + if let Some((innermost_binding, innermost_flags)) = innermost_result { + // Found another solution, if the first one was "weak", report an error. + let (res, innermost_res) = (binding.res(), innermost_binding.res()); + if res != innermost_res { + let is_builtin = |res| { + matches!(res, Res::NonMacroAttr(NonMacroAttrKind::Builtin(..))) + }; + let derive_helper = + Res::NonMacroAttr(NonMacroAttrKind::DeriveHelper); + let derive_helper_compat = + Res::NonMacroAttr(NonMacroAttrKind::DeriveHelperCompat); + + let ambiguity_error_kind = if is_import { + Some(AmbiguityKind::Import) + } else if is_builtin(innermost_res) || is_builtin(res) { + Some(AmbiguityKind::BuiltinAttr) + } else if innermost_res == derive_helper_compat + || res == derive_helper_compat && innermost_res != derive_helper + { + Some(AmbiguityKind::DeriveHelper) + } else if innermost_flags.contains(Flags::MACRO_RULES) + && flags.contains(Flags::MODULE) + && !this.disambiguate_macro_rules_vs_modularized( + innermost_binding, + binding, + ) + || flags.contains(Flags::MACRO_RULES) + && innermost_flags.contains(Flags::MODULE) + && !this.disambiguate_macro_rules_vs_modularized( + binding, + innermost_binding, + ) + { + Some(AmbiguityKind::MacroRulesVsModularized) + } else if innermost_binding.is_glob_import() { + Some(AmbiguityKind::GlobVsOuter) + } else if innermost_binding + .may_appear_after(parent_scope.expansion, binding) + { + Some(AmbiguityKind::MoreExpandedVsOuter) + } else { + None + }; + if let Some(kind) = ambiguity_error_kind { + let misc = |f: Flags| { + if f.contains(Flags::MISC_SUGGEST_CRATE) { + AmbiguityErrorMisc::SuggestCrate + } else if f.contains(Flags::MISC_SUGGEST_SELF) { + AmbiguityErrorMisc::SuggestSelf + } else if f.contains(Flags::MISC_FROM_PRELUDE) { + AmbiguityErrorMisc::FromPrelude + } else { + AmbiguityErrorMisc::None + } + }; + this.ambiguity_errors.push(AmbiguityError { + kind, + ident: orig_ident, + b1: innermost_binding, + b2: binding, + misc1: misc(innermost_flags), + misc2: misc(flags), + }); + return Some(Ok(innermost_binding)); + } + } + } else { + // Found the first solution. + innermost_result = Some((binding, flags)); + } + } + Ok(..) | Err(Determinacy::Determined) => {} + Err(Determinacy::Undetermined) => determinacy = Determinacy::Undetermined, + } + + None + }, + ); + + if let Some(break_result) = break_result { + return break_result; + } + + // The first found solution was the only one, return it. + if let Some((binding, _)) = innermost_result { + return Ok(binding); + } + + Err(Determinacy::determined(determinacy == Determinacy::Determined || force)) + } + + crate fn resolve_ident_in_module( + &mut self, + module: ModuleOrUniformRoot<'a>, + ident: Ident, + ns: Namespace, + parent_scope: &ParentScope<'a>, + finalize: Option, + ) -> Result<&'a NameBinding<'a>, Determinacy> { + self.resolve_ident_in_module_ext(module, ident, ns, parent_scope, finalize) + .map_err(|(determinacy, _)| determinacy) + } + + fn resolve_ident_in_module_ext( + &mut self, + module: ModuleOrUniformRoot<'a>, + mut ident: Ident, + ns: Namespace, + parent_scope: &ParentScope<'a>, + finalize: Option, + ) -> Result<&'a NameBinding<'a>, (Determinacy, Weak)> { + let tmp_parent_scope; + let mut adjusted_parent_scope = parent_scope; + match module { + ModuleOrUniformRoot::Module(m) => { + if let Some(def) = ident.span.normalize_to_macros_2_0_and_adjust(m.expansion) { + tmp_parent_scope = + ParentScope { module: self.expn_def_scope(def), ..*parent_scope }; + adjusted_parent_scope = &tmp_parent_scope; + } + } + ModuleOrUniformRoot::ExternPrelude => { + ident.span.normalize_to_macros_2_0_and_adjust(ExpnId::root()); + } + ModuleOrUniformRoot::CrateRootAndExternPrelude | ModuleOrUniformRoot::CurrentScope => { + // No adjustments + } + } + self.resolve_ident_in_module_unadjusted_ext( + module, + ident, + ns, + adjusted_parent_scope, + false, + finalize, + ) + } + + fn resolve_ident_in_module_unadjusted( + &mut self, + module: ModuleOrUniformRoot<'a>, + ident: Ident, + ns: Namespace, + parent_scope: &ParentScope<'a>, + finalize: Option, + ) -> Result<&'a NameBinding<'a>, Determinacy> { + self.resolve_ident_in_module_unadjusted_ext( + module, + ident, + ns, + parent_scope, + false, + finalize, + ) + .map_err(|(determinacy, _)| determinacy) + } + + /// Attempts to resolve `ident` in namespaces `ns` of `module`. + /// Invariant: if `finalize` is `Some`, expansion and import resolution must be complete. + fn resolve_ident_in_module_unadjusted_ext( + &mut self, + module: ModuleOrUniformRoot<'a>, + ident: Ident, + ns: Namespace, + parent_scope: &ParentScope<'a>, + restricted_shadowing: bool, + finalize: Option, + ) -> Result<&'a NameBinding<'a>, (Determinacy, Weak)> { + let module = match module { + ModuleOrUniformRoot::Module(module) => module, + ModuleOrUniformRoot::CrateRootAndExternPrelude => { + assert!(!restricted_shadowing); + let binding = self.early_resolve_ident_in_lexical_scope( + ident, + ScopeSet::AbsolutePath(ns), + parent_scope, + finalize, + finalize.is_some(), + ); + return binding.map_err(|determinacy| (determinacy, Weak::No)); + } + ModuleOrUniformRoot::ExternPrelude => { + assert!(!restricted_shadowing); + return if ns != TypeNS { + Err((Determined, Weak::No)) + } else if let Some(binding) = self.extern_prelude_get(ident, finalize.is_some()) { + Ok(binding) + } else if !self.graph_root.unexpanded_invocations.borrow().is_empty() { + // Macro-expanded `extern crate` items can add names to extern prelude. + Err((Undetermined, Weak::No)) + } else { + Err((Determined, Weak::No)) + }; + } + ModuleOrUniformRoot::CurrentScope => { + assert!(!restricted_shadowing); + if ns == TypeNS { + if ident.name == kw::Crate || ident.name == kw::DollarCrate { + let module = self.resolve_crate_root(ident); + let binding = + (module, ty::Visibility::Public, module.span, LocalExpnId::ROOT) + .to_name_binding(self.arenas); + return Ok(binding); + } else if ident.name == kw::Super || ident.name == kw::SelfLower { + // FIXME: Implement these with renaming requirements so that e.g. + // `use super;` doesn't work, but `use super as name;` does. + // Fall through here to get an error from `early_resolve_...`. + } + } + + let scopes = ScopeSet::All(ns, true); + let binding = self.early_resolve_ident_in_lexical_scope( + ident, + scopes, + parent_scope, + finalize, + finalize.is_some(), + ); + return binding.map_err(|determinacy| (determinacy, Weak::No)); + } + }; + + let key = self.new_key(ident, ns); + let resolution = + self.resolution(module, key).try_borrow_mut().map_err(|_| (Determined, Weak::No))?; // This happens when there is a cycle of imports. + + if let Some(binding) = resolution.binding && let Some(path_span) = finalize { + if !restricted_shadowing && binding.expansion != LocalExpnId::ROOT { + if let NameBindingKind::Res(_, true) = binding.kind { + self.macro_expanded_macro_export_errors.insert((path_span, binding.span)); + } + } + } + + let check_usable = |this: &mut Self, binding: &'a NameBinding<'a>| { + if let Some(unusable_binding) = this.unusable_binding { + if ptr::eq(binding, unusable_binding) { + return Err((Determined, Weak::No)); + } + } + let usable = this.is_accessible_from(binding.vis, parent_scope.module); + if usable { Ok(binding) } else { Err((Determined, Weak::No)) } + }; + + if let Some(path_span) = finalize { + return resolution + .binding + .and_then(|binding| { + // If the primary binding is unusable, search further and return the shadowed glob + // binding if it exists. What we really want here is having two separate scopes in + // a module - one for non-globs and one for globs, but until that's done use this + // hack to avoid inconsistent resolution ICEs during import validation. + if let Some(unusable_binding) = self.unusable_binding { + if ptr::eq(binding, unusable_binding) { + return resolution.shadowed_glob; + } + } + Some(binding) + }) + .ok_or((Determined, Weak::No)) + .and_then(|binding| { + if self.last_import_segment && check_usable(self, binding).is_err() { + Err((Determined, Weak::No)) + } else { + self.record_use(ident, binding, restricted_shadowing); + + if let Some(shadowed_glob) = resolution.shadowed_glob { + // Forbid expanded shadowing to avoid time travel. + if restricted_shadowing + && binding.expansion != LocalExpnId::ROOT + && binding.res() != shadowed_glob.res() + { + self.ambiguity_errors.push(AmbiguityError { + kind: AmbiguityKind::GlobVsExpanded, + ident, + b1: binding, + b2: shadowed_glob, + misc1: AmbiguityErrorMisc::None, + misc2: AmbiguityErrorMisc::None, + }); + } + } + + if !self.is_accessible_from(binding.vis, parent_scope.module) { + self.privacy_errors.push(PrivacyError { + ident, + binding, + dedup_span: path_span, + }); + } + + Ok(binding) + } + }); + } + + // Items and single imports are not shadowable, if we have one, then it's determined. + if let Some(binding) = resolution.binding { + if !binding.is_glob_import() { + return check_usable(self, binding); + } + } + + // --- From now on we either have a glob resolution or no resolution. --- + + // Check if one of single imports can still define the name, + // if it can then our result is not determined and can be invalidated. + for single_import in &resolution.single_imports { + if !self.is_accessible_from(single_import.vis.get(), parent_scope.module) { + continue; + } + let Some(module) = single_import.imported_module.get() else { + return Err((Undetermined, Weak::No)); + }; + let ImportKind::Single { source: ident, .. } = single_import.kind else { + unreachable!(); + }; + match self.resolve_ident_in_module(module, ident, ns, &single_import.parent_scope, None) + { + Err(Determined) => continue, + Ok(binding) + if !self.is_accessible_from(binding.vis, single_import.parent_scope.module) => + { + continue; + } + Ok(_) | Err(Undetermined) => return Err((Undetermined, Weak::No)), + } + } + + // So we have a resolution that's from a glob import. This resolution is determined + // if it cannot be shadowed by some new item/import expanded from a macro. + // This happens either if there are no unexpanded macros, or expanded names cannot + // shadow globs (that happens in macro namespace or with restricted shadowing). + // + // Additionally, any macro in any module can plant names in the root module if it creates + // `macro_export` macros, so the root module effectively has unresolved invocations if any + // module has unresolved invocations. + // However, it causes resolution/expansion to stuck too often (#53144), so, to make + // progress, we have to ignore those potential unresolved invocations from other modules + // and prohibit access to macro-expanded `macro_export` macros instead (unless restricted + // shadowing is enabled, see `macro_expanded_macro_export_errors`). + let unexpanded_macros = !module.unexpanded_invocations.borrow().is_empty(); + if let Some(binding) = resolution.binding { + if !unexpanded_macros || ns == MacroNS || restricted_shadowing { + return check_usable(self, binding); + } else { + return Err((Undetermined, Weak::No)); + } + } + + // --- From now on we have no resolution. --- + + // Now we are in situation when new item/import can appear only from a glob or a macro + // expansion. With restricted shadowing names from globs and macro expansions cannot + // shadow names from outer scopes, so we can freely fallback from module search to search + // in outer scopes. For `early_resolve_ident_in_lexical_scope` to continue search in outer + // scopes we return `Undetermined` with `Weak::Yes`. + + // Check if one of unexpanded macros can still define the name, + // if it can then our "no resolution" result is not determined and can be invalidated. + if unexpanded_macros { + return Err((Undetermined, Weak::Yes)); + } + + // Check if one of glob imports can still define the name, + // if it can then our "no resolution" result is not determined and can be invalidated. + for glob_import in module.globs.borrow().iter() { + if !self.is_accessible_from(glob_import.vis.get(), parent_scope.module) { + continue; + } + let module = match glob_import.imported_module.get() { + Some(ModuleOrUniformRoot::Module(module)) => module, + Some(_) => continue, + None => return Err((Undetermined, Weak::Yes)), + }; + let tmp_parent_scope; + let (mut adjusted_parent_scope, mut ident) = + (parent_scope, ident.normalize_to_macros_2_0()); + match ident.span.glob_adjust(module.expansion, glob_import.span) { + Some(Some(def)) => { + tmp_parent_scope = + ParentScope { module: self.expn_def_scope(def), ..*parent_scope }; + adjusted_parent_scope = &tmp_parent_scope; + } + Some(None) => {} + None => continue, + }; + let result = self.resolve_ident_in_module_unadjusted( + ModuleOrUniformRoot::Module(module), + ident, + ns, + adjusted_parent_scope, + None, + ); + + match result { + Err(Determined) => continue, + Ok(binding) + if !self.is_accessible_from(binding.vis, glob_import.parent_scope.module) => + { + continue; + } + Ok(_) | Err(Undetermined) => return Err((Undetermined, Weak::Yes)), + } + } + + // No resolution and no one else can define the name - determinate error. + Err((Determined, Weak::No)) + } + + /// Validate a local resolution (from ribs). + fn validate_res_from_ribs( + &mut self, + rib_index: usize, + rib_ident: Ident, + mut res: Res, + finalize: Option, + original_rib_ident_def: Ident, + all_ribs: &[Rib<'a>], + ) -> Res { + const CG_BUG_STR: &str = "min_const_generics resolve check didn't stop compilation"; + debug!("validate_res_from_ribs({:?})", res); + let ribs = &all_ribs[rib_index + 1..]; + + // An invalid forward use of a generic parameter from a previous default. + if let ForwardGenericParamBanRibKind = all_ribs[rib_index].kind { + if let Some(span) = finalize { + let res_error = if rib_ident.name == kw::SelfUpper { + ResolutionError::SelfInGenericParamDefault + } else { + ResolutionError::ForwardDeclaredGenericParam + }; + self.report_error(span, res_error); + } + assert_eq!(res, Res::Err); + return Res::Err; + } + + match res { + Res::Local(_) => { + use ResolutionError::*; + let mut res_err = None; + + for rib in ribs { + match rib.kind { + NormalRibKind + | ClosureOrAsyncRibKind + | ModuleRibKind(..) + | MacroDefinition(..) + | ForwardGenericParamBanRibKind => { + // Nothing to do. Continue. + } + ItemRibKind(_) | FnItemRibKind | AssocItemRibKind => { + // This was an attempt to access an upvar inside a + // named function item. This is not allowed, so we + // report an error. + if let Some(span) = finalize { + // We don't immediately trigger a resolve error, because + // we want certain other resolution errors (namely those + // emitted for `ConstantItemRibKind` below) to take + // precedence. + res_err = Some((span, CannotCaptureDynamicEnvironmentInFnItem)); + } + } + ConstantItemRibKind(_, item) => { + // Still doesn't deal with upvars + if let Some(span) = finalize { + let (span, resolution_error) = + if let Some((ident, constant_item_kind)) = item { + let kind_str = match constant_item_kind { + ConstantItemKind::Const => "const", + ConstantItemKind::Static => "static", + }; + ( + span, + AttemptToUseNonConstantValueInConstant( + ident, "let", kind_str, + ), + ) + } else { + ( + rib_ident.span, + AttemptToUseNonConstantValueInConstant( + original_rib_ident_def, + "const", + "let", + ), + ) + }; + self.report_error(span, resolution_error); + } + return Res::Err; + } + ConstParamTyRibKind => { + if let Some(span) = finalize { + self.report_error(span, ParamInTyOfConstParam(rib_ident.name)); + } + return Res::Err; + } + } + } + if let Some((span, res_err)) = res_err { + self.report_error(span, res_err); + return Res::Err; + } + } + Res::Def(DefKind::TyParam, _) | Res::SelfTy { .. } => { + for rib in ribs { + let has_generic_params: HasGenericParams = match rib.kind { + NormalRibKind + | ClosureOrAsyncRibKind + | AssocItemRibKind + | ModuleRibKind(..) + | MacroDefinition(..) + | ForwardGenericParamBanRibKind => { + // Nothing to do. Continue. + continue; + } + + ConstantItemRibKind(trivial, _) => { + let features = self.session.features_untracked(); + // HACK(min_const_generics): We currently only allow `N` or `{ N }`. + if !(trivial || features.generic_const_exprs) { + // HACK(min_const_generics): If we encounter `Self` in an anonymous constant + // we can't easily tell if it's generic at this stage, so we instead remember + // this and then enforce the self type to be concrete later on. + if let Res::SelfTy { trait_, alias_to: Some((def, _)) } = res { + res = Res::SelfTy { trait_, alias_to: Some((def, true)) } + } else { + if let Some(span) = finalize { + self.report_error( + span, + ResolutionError::ParamInNonTrivialAnonConst { + name: rib_ident.name, + is_type: true, + }, + ); + self.session.delay_span_bug(span, CG_BUG_STR); + } + + return Res::Err; + } + } + + continue; + } + + // This was an attempt to use a type parameter outside its scope. + ItemRibKind(has_generic_params) => has_generic_params, + FnItemRibKind => HasGenericParams::Yes, + ConstParamTyRibKind => { + if let Some(span) = finalize { + self.report_error( + span, + ResolutionError::ParamInTyOfConstParam(rib_ident.name), + ); + } + return Res::Err; + } + }; + + if let Some(span) = finalize { + self.report_error( + span, + ResolutionError::GenericParamsFromOuterFunction( + res, + has_generic_params, + ), + ); + } + return Res::Err; + } + } + Res::Def(DefKind::ConstParam, _) => { + let mut ribs = ribs.iter().peekable(); + if let Some(Rib { kind: FnItemRibKind, .. }) = ribs.peek() { + // When declaring const parameters inside function signatures, the first rib + // is always a `FnItemRibKind`. In this case, we can skip it, to avoid it + // (spuriously) conflicting with the const param. + ribs.next(); + } + + for rib in ribs { + let has_generic_params = match rib.kind { + NormalRibKind + | ClosureOrAsyncRibKind + | AssocItemRibKind + | ModuleRibKind(..) + | MacroDefinition(..) + | ForwardGenericParamBanRibKind => continue, + + ConstantItemRibKind(trivial, _) => { + let features = self.session.features_untracked(); + // HACK(min_const_generics): We currently only allow `N` or `{ N }`. + if !(trivial || features.generic_const_exprs) { + if let Some(span) = finalize { + self.report_error( + span, + ResolutionError::ParamInNonTrivialAnonConst { + name: rib_ident.name, + is_type: false, + }, + ); + self.session.delay_span_bug(span, CG_BUG_STR); + } + + return Res::Err; + } + + continue; + } + + ItemRibKind(has_generic_params) => has_generic_params, + FnItemRibKind => HasGenericParams::Yes, + ConstParamTyRibKind => { + if let Some(span) = finalize { + self.report_error( + span, + ResolutionError::ParamInTyOfConstParam(rib_ident.name), + ); + } + return Res::Err; + } + }; + + // This was an attempt to use a const parameter outside its scope. + if let Some(span) = finalize { + self.report_error( + span, + ResolutionError::GenericParamsFromOuterFunction( + res, + has_generic_params, + ), + ); + } + return Res::Err; + } + } + _ => {} + } + res + } + + crate fn resolve_path( + &mut self, + path: &[Segment], + opt_ns: Option, // `None` indicates a module path in import + parent_scope: &ParentScope<'a>, + finalize: Finalize, + ) -> PathResult<'a> { + self.resolve_path_with_ribs(path, opt_ns, parent_scope, finalize, None) + } + + crate fn resolve_path_with_ribs( + &mut self, + path: &[Segment], + opt_ns: Option, // `None` indicates a module path in import + parent_scope: &ParentScope<'a>, + finalize_full: Finalize, + ribs: Option<&PerNS>>>, + ) -> PathResult<'a> { + debug!("resolve_path(path={:?}, opt_ns={:?}, finalize={:?})", path, opt_ns, finalize_full); + + let finalize = finalize_full.path_span(); + let mut module = None; + let mut allow_super = true; + let mut second_binding = None; + + for (i, &Segment { ident, id, has_generic_args: _ }) in path.iter().enumerate() { + debug!("resolve_path ident {} {:?} {:?}", i, ident, id); + let record_segment_res = |this: &mut Self, res| { + if finalize.is_some() { + if let Some(id) = id { + if !this.partial_res_map.contains_key(&id) { + assert!(id != ast::DUMMY_NODE_ID, "Trying to resolve dummy id"); + this.record_partial_res(id, PartialRes::new(res)); + } + } + } + }; + + let is_last = i == path.len() - 1; + let ns = if is_last { opt_ns.unwrap_or(TypeNS) } else { TypeNS }; + let name = ident.name; + + allow_super &= ns == TypeNS && (name == kw::SelfLower || name == kw::Super); + + if ns == TypeNS { + if allow_super && name == kw::Super { + let mut ctxt = ident.span.ctxt().normalize_to_macros_2_0(); + let self_module = match i { + 0 => Some(self.resolve_self(&mut ctxt, parent_scope.module)), + _ => match module { + Some(ModuleOrUniformRoot::Module(module)) => Some(module), + _ => None, + }, + }; + if let Some(self_module) = self_module { + if let Some(parent) = self_module.parent { + module = Some(ModuleOrUniformRoot::Module( + self.resolve_self(&mut ctxt, parent), + )); + continue; + } + } + return PathResult::failed(ident.span, false, finalize.is_some(), || { + ("there are too many leading `super` keywords".to_string(), None) + }); + } + if i == 0 { + if name == kw::SelfLower { + let mut ctxt = ident.span.ctxt().normalize_to_macros_2_0(); + module = Some(ModuleOrUniformRoot::Module( + self.resolve_self(&mut ctxt, parent_scope.module), + )); + continue; + } + if name == kw::PathRoot && ident.span.rust_2018() { + module = Some(ModuleOrUniformRoot::ExternPrelude); + continue; + } + if name == kw::PathRoot && ident.span.rust_2015() && self.session.rust_2018() { + // `::a::b` from 2015 macro on 2018 global edition + module = Some(ModuleOrUniformRoot::CrateRootAndExternPrelude); + continue; + } + if name == kw::PathRoot || name == kw::Crate || name == kw::DollarCrate { + // `::a::b`, `crate::a::b` or `$crate::a::b` + module = Some(ModuleOrUniformRoot::Module(self.resolve_crate_root(ident))); + continue; + } + } + } + + // Report special messages for path segment keywords in wrong positions. + if ident.is_path_segment_keyword() && i != 0 { + return PathResult::failed(ident.span, false, finalize.is_some(), || { + let name_str = if name == kw::PathRoot { + "crate root".to_string() + } else { + format!("`{}`", name) + }; + let label = if i == 1 && path[0].ident.name == kw::PathRoot { + format!("global paths cannot start with {}", name_str) + } else { + format!("{} in paths can only be used in start position", name_str) + }; + (label, None) + }); + } + + enum FindBindingResult<'a> { + Binding(Result<&'a NameBinding<'a>, Determinacy>), + PathResult(PathResult<'a>), + } + let find_binding_in_ns = |this: &mut Self, ns| { + let binding = if let Some(module) = module { + this.resolve_ident_in_module(module, ident, ns, parent_scope, finalize) + } else if ribs.is_none() || opt_ns.is_none() || opt_ns == Some(MacroNS) { + let scopes = ScopeSet::All(ns, opt_ns.is_none()); + this.early_resolve_ident_in_lexical_scope( + ident, + scopes, + parent_scope, + finalize, + finalize.is_some(), + ) + } else { + match this.resolve_ident_in_lexical_scope( + ident, + ns, + parent_scope, + finalize_full, + &ribs.unwrap()[ns], + ) { + // we found a locally-imported or available item/module + Some(LexicalScopeBinding::Item(binding)) => Ok(binding), + // we found a local variable or type param + Some(LexicalScopeBinding::Res(res)) + if opt_ns == Some(TypeNS) || opt_ns == Some(ValueNS) => + { + record_segment_res(this, res); + return FindBindingResult::PathResult(PathResult::NonModule( + PartialRes::with_unresolved_segments(res, path.len() - 1), + )); + } + _ => Err(Determinacy::determined(finalize.is_some())), + } + }; + FindBindingResult::Binding(binding) + }; + let binding = match find_binding_in_ns(self, ns) { + FindBindingResult::PathResult(x) => return x, + FindBindingResult::Binding(binding) => binding, + }; + match binding { + Ok(binding) => { + if i == 1 { + second_binding = Some(binding); + } + let res = binding.res(); + let maybe_assoc = opt_ns != Some(MacroNS) && PathSource::Type.is_expected(res); + if let Some(next_module) = binding.module() { + module = Some(ModuleOrUniformRoot::Module(next_module)); + record_segment_res(self, res); + } else if res == Res::ToolMod && i + 1 != path.len() { + if binding.is_import() { + self.session + .struct_span_err( + ident.span, + "cannot use a tool module through an import", + ) + .span_note(binding.span, "the tool module imported here") + .emit(); + } + let res = Res::NonMacroAttr(NonMacroAttrKind::Tool); + return PathResult::NonModule(PartialRes::new(res)); + } else if res == Res::Err { + return PathResult::NonModule(PartialRes::new(Res::Err)); + } else if opt_ns.is_some() && (is_last || maybe_assoc) { + self.lint_if_path_starts_with_module(finalize_full, path, second_binding); + return PathResult::NonModule(PartialRes::with_unresolved_segments( + res, + path.len() - i - 1, + )); + } else { + return PathResult::failed(ident.span, is_last, finalize.is_some(), || { + let label = format!( + "`{ident}` is {} {}, not a module", + res.article(), + res.descr() + ); + (label, None) + }); + } + } + Err(Undetermined) => return PathResult::Indeterminate, + Err(Determined) => { + if let Some(ModuleOrUniformRoot::Module(module)) = module { + if opt_ns.is_some() && !module.is_normal() { + return PathResult::NonModule(PartialRes::with_unresolved_segments( + module.res().unwrap(), + path.len() - i, + )); + } + } + + return PathResult::failed(ident.span, is_last, finalize.is_some(), || { + let module_res = match module { + Some(ModuleOrUniformRoot::Module(module)) => module.res(), + _ => None, + }; + if module_res == self.graph_root.res() { + let is_mod = |res| matches!(res, Res::Def(DefKind::Mod, _)); + let mut candidates = + self.lookup_import_candidates(ident, TypeNS, parent_scope, is_mod); + candidates.sort_by_cached_key(|c| { + (c.path.segments.len(), pprust::path_to_string(&c.path)) + }); + if let Some(candidate) = candidates.get(0) { + ( + String::from("unresolved import"), + Some(( + vec![(ident.span, pprust::path_to_string(&candidate.path))], + String::from("a similar path exists"), + Applicability::MaybeIncorrect, + )), + ) + } else if self.session.edition() == Edition::Edition2015 { + (format!("maybe a missing crate `{}`?", ident), None) + } else { + (format!("could not find `{}` in the crate root", ident), None) + } + } else if i == 0 { + if ident + .name + .as_str() + .chars() + .next() + .map_or(false, |c| c.is_ascii_uppercase()) + { + // Check whether the name refers to an item in the value namespace. + let suggestion = if ribs.is_some() { + let match_span = match self.resolve_ident_in_lexical_scope( + ident, + ValueNS, + parent_scope, + Finalize::No, + &ribs.unwrap()[ValueNS], + ) { + // Name matches a local variable. For example: + // ``` + // fn f() { + // let Foo: &str = ""; + // println!("{}", Foo::Bar); // Name refers to local + // // variable `Foo`. + // } + // ``` + Some(LexicalScopeBinding::Res(Res::Local(id))) => { + Some(*self.pat_span_map.get(&id).unwrap()) + } + + // Name matches item from a local name binding + // created by `use` declaration. For example: + // ``` + // pub Foo: &str = ""; + // + // mod submod { + // use super::Foo; + // println!("{}", Foo::Bar); // Name refers to local + // // binding `Foo`. + // } + // ``` + Some(LexicalScopeBinding::Item(name_binding)) => { + Some(name_binding.span) + } + _ => None, + }; + + if let Some(span) = match_span { + Some(( + vec![(span, String::from(""))], + format!( + "`{}` is defined here, but is not a type", + ident + ), + Applicability::MaybeIncorrect, + )) + } else { + None + } + } else { + None + }; + + (format!("use of undeclared type `{}`", ident), suggestion) + } else { + ( + format!("use of undeclared crate or module `{}`", ident), + if ident.name == sym::alloc { + Some(( + vec![], + String::from( + "add `extern crate alloc` to use the `alloc` crate", + ), + Applicability::MaybeIncorrect, + )) + } else { + self.find_similarly_named_module_or_crate( + ident.name, + &parent_scope.module, + ) + .map(|sugg| { + ( + vec![(ident.span, sugg.to_string())], + String::from( + "there is a crate or module with a similar name", + ), + Applicability::MaybeIncorrect, + ) + }) + }, + ) + } + } else { + let parent = path[i - 1].ident.name; + let parent = match parent { + // ::foo is mounted at the crate root for 2015, and is the extern + // prelude for 2018+ + kw::PathRoot if self.session.edition() > Edition::Edition2015 => { + "the list of imported crates".to_owned() + } + kw::PathRoot | kw::Crate => "the crate root".to_owned(), + _ => { + format!("`{}`", parent) + } + }; + + let mut msg = format!("could not find `{}` in {}", ident, parent); + if ns == TypeNS || ns == ValueNS { + let ns_to_try = if ns == TypeNS { ValueNS } else { TypeNS }; + if let FindBindingResult::Binding(Ok(binding)) = + find_binding_in_ns(self, ns_to_try) + { + let mut found = |what| { + msg = format!( + "expected {}, found {} `{}` in {}", + ns.descr(), + what, + ident, + parent + ) + }; + if binding.module().is_some() { + found("module") + } else { + match binding.res() { + Res::Def(kind, id) => found(kind.descr(id)), + _ => found(ns_to_try.descr()), + } + } + }; + } + (msg, None) + } + }); + } + } + } + + self.lint_if_path_starts_with_module(finalize_full, path, second_binding); + + PathResult::Module(match module { + Some(module) => module, + None if path.is_empty() => ModuleOrUniformRoot::CurrentScope, + _ => bug!("resolve_path: non-empty path `{:?}` has no module", path), + }) + } +} diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 02abdbaa98354..3d199afb13d41 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -2,12 +2,11 @@ use crate::diagnostics::Suggestion; use crate::Determinacy::{self, *}; -use crate::Namespace::{self, MacroNS, TypeNS}; +use crate::Namespace::{MacroNS, TypeNS}; use crate::{module_to_string, names_to_string}; -use crate::{AmbiguityError, AmbiguityErrorMisc, AmbiguityKind}; -use crate::{BindingKey, ModuleKind, ResolutionError, Resolver, Segment}; -use crate::{Finalize, Module, ModuleOrUniformRoot, ParentScope, PerNS, ScopeSet, Weak}; -use crate::{NameBinding, NameBindingKind, PathResult, PrivacyError, ToNameBinding}; +use crate::{AmbiguityKind, BindingKey, ModuleKind, ResolutionError, Resolver, Segment}; +use crate::{Finalize, Module, ModuleOrUniformRoot, ParentScope, PerNS, ScopeSet}; +use crate::{NameBinding, NameBindingKind, PathResult}; use rustc_ast::NodeId; use rustc_data_structures::fx::FxHashSet; @@ -125,15 +124,15 @@ impl<'a> Import<'a> { } } -#[derive(Clone, Default, Debug)] /// Records information about the resolution of a name in a namespace of a module. -pub struct NameResolution<'a> { +#[derive(Clone, Default, Debug)] +crate struct NameResolution<'a> { /// Single imports that may define the name in the namespace. /// Imports are arena-allocated, so it's ok to use pointers as keys. - single_imports: FxHashSet>>, + pub single_imports: FxHashSet>>, /// The least shadowable known binding for this name, or None if there are no known bindings. pub binding: Option<&'a NameBinding<'a>>, - shadowed_glob: Option<&'a NameBinding<'a>>, + pub shadowed_glob: Option<&'a NameBinding<'a>>, } impl<'a> NameResolution<'a> { @@ -169,278 +168,6 @@ fn pub_use_of_private_extern_crate_hack(import: &Import<'_>, binding: &NameBindi } impl<'a> Resolver<'a> { - crate fn resolve_ident_in_module_unadjusted( - &mut self, - module: ModuleOrUniformRoot<'a>, - ident: Ident, - ns: Namespace, - parent_scope: &ParentScope<'a>, - finalize: Option, - ) -> Result<&'a NameBinding<'a>, Determinacy> { - self.resolve_ident_in_module_unadjusted_ext( - module, - ident, - ns, - parent_scope, - false, - finalize, - ) - .map_err(|(determinacy, _)| determinacy) - } - - /// Attempts to resolve `ident` in namespaces `ns` of `module`. - /// Invariant: if `finalize` is `Some`, expansion and import resolution must be complete. - crate fn resolve_ident_in_module_unadjusted_ext( - &mut self, - module: ModuleOrUniformRoot<'a>, - ident: Ident, - ns: Namespace, - parent_scope: &ParentScope<'a>, - restricted_shadowing: bool, - finalize: Option, - ) -> Result<&'a NameBinding<'a>, (Determinacy, Weak)> { - let module = match module { - ModuleOrUniformRoot::Module(module) => module, - ModuleOrUniformRoot::CrateRootAndExternPrelude => { - assert!(!restricted_shadowing); - let binding = self.early_resolve_ident_in_lexical_scope( - ident, - ScopeSet::AbsolutePath(ns), - parent_scope, - finalize, - finalize.is_some(), - ); - return binding.map_err(|determinacy| (determinacy, Weak::No)); - } - ModuleOrUniformRoot::ExternPrelude => { - assert!(!restricted_shadowing); - return if ns != TypeNS { - Err((Determined, Weak::No)) - } else if let Some(binding) = self.extern_prelude_get(ident, finalize.is_some()) { - Ok(binding) - } else if !self.graph_root.unexpanded_invocations.borrow().is_empty() { - // Macro-expanded `extern crate` items can add names to extern prelude. - Err((Undetermined, Weak::No)) - } else { - Err((Determined, Weak::No)) - }; - } - ModuleOrUniformRoot::CurrentScope => { - assert!(!restricted_shadowing); - if ns == TypeNS { - if ident.name == kw::Crate || ident.name == kw::DollarCrate { - let module = self.resolve_crate_root(ident); - let binding = - (module, ty::Visibility::Public, module.span, LocalExpnId::ROOT) - .to_name_binding(self.arenas); - return Ok(binding); - } else if ident.name == kw::Super || ident.name == kw::SelfLower { - // FIXME: Implement these with renaming requirements so that e.g. - // `use super;` doesn't work, but `use super as name;` does. - // Fall through here to get an error from `early_resolve_...`. - } - } - - let scopes = ScopeSet::All(ns, true); - let binding = self.early_resolve_ident_in_lexical_scope( - ident, - scopes, - parent_scope, - finalize, - finalize.is_some(), - ); - return binding.map_err(|determinacy| (determinacy, Weak::No)); - } - }; - - let key = self.new_key(ident, ns); - let resolution = - self.resolution(module, key).try_borrow_mut().map_err(|_| (Determined, Weak::No))?; // This happens when there is a cycle of imports. - - if let Some(binding) = resolution.binding && let Some(path_span) = finalize { - if !restricted_shadowing && binding.expansion != LocalExpnId::ROOT { - if let NameBindingKind::Res(_, true) = binding.kind { - self.macro_expanded_macro_export_errors.insert((path_span, binding.span)); - } - } - } - - let check_usable = |this: &mut Self, binding: &'a NameBinding<'a>| { - if let Some(unusable_binding) = this.unusable_binding { - if ptr::eq(binding, unusable_binding) { - return Err((Determined, Weak::No)); - } - } - let usable = this.is_accessible_from(binding.vis, parent_scope.module); - if usable { Ok(binding) } else { Err((Determined, Weak::No)) } - }; - - if let Some(path_span) = finalize { - return resolution - .binding - .and_then(|binding| { - // If the primary binding is unusable, search further and return the shadowed glob - // binding if it exists. What we really want here is having two separate scopes in - // a module - one for non-globs and one for globs, but until that's done use this - // hack to avoid inconsistent resolution ICEs during import validation. - if let Some(unusable_binding) = self.unusable_binding { - if ptr::eq(binding, unusable_binding) { - return resolution.shadowed_glob; - } - } - Some(binding) - }) - .ok_or((Determined, Weak::No)) - .and_then(|binding| { - if self.last_import_segment && check_usable(self, binding).is_err() { - Err((Determined, Weak::No)) - } else { - self.record_use(ident, binding, restricted_shadowing); - - if let Some(shadowed_glob) = resolution.shadowed_glob { - // Forbid expanded shadowing to avoid time travel. - if restricted_shadowing - && binding.expansion != LocalExpnId::ROOT - && binding.res() != shadowed_glob.res() - { - self.ambiguity_errors.push(AmbiguityError { - kind: AmbiguityKind::GlobVsExpanded, - ident, - b1: binding, - b2: shadowed_glob, - misc1: AmbiguityErrorMisc::None, - misc2: AmbiguityErrorMisc::None, - }); - } - } - - if !self.is_accessible_from(binding.vis, parent_scope.module) { - self.privacy_errors.push(PrivacyError { - ident, - binding, - dedup_span: path_span, - }); - } - - Ok(binding) - } - }); - } - - // Items and single imports are not shadowable, if we have one, then it's determined. - if let Some(binding) = resolution.binding { - if !binding.is_glob_import() { - return check_usable(self, binding); - } - } - - // --- From now on we either have a glob resolution or no resolution. --- - - // Check if one of single imports can still define the name, - // if it can then our result is not determined and can be invalidated. - for single_import in &resolution.single_imports { - if !self.is_accessible_from(single_import.vis.get(), parent_scope.module) { - continue; - } - let Some(module) = single_import.imported_module.get() else { - return Err((Undetermined, Weak::No)); - }; - let ImportKind::Single { source: ident, .. } = single_import.kind else { - unreachable!(); - }; - match self.resolve_ident_in_module(module, ident, ns, &single_import.parent_scope, None) - { - Err(Determined) => continue, - Ok(binding) - if !self.is_accessible_from(binding.vis, single_import.parent_scope.module) => - { - continue; - } - Ok(_) | Err(Undetermined) => return Err((Undetermined, Weak::No)), - } - } - - // So we have a resolution that's from a glob import. This resolution is determined - // if it cannot be shadowed by some new item/import expanded from a macro. - // This happens either if there are no unexpanded macros, or expanded names cannot - // shadow globs (that happens in macro namespace or with restricted shadowing). - // - // Additionally, any macro in any module can plant names in the root module if it creates - // `macro_export` macros, so the root module effectively has unresolved invocations if any - // module has unresolved invocations. - // However, it causes resolution/expansion to stuck too often (#53144), so, to make - // progress, we have to ignore those potential unresolved invocations from other modules - // and prohibit access to macro-expanded `macro_export` macros instead (unless restricted - // shadowing is enabled, see `macro_expanded_macro_export_errors`). - let unexpanded_macros = !module.unexpanded_invocations.borrow().is_empty(); - if let Some(binding) = resolution.binding { - if !unexpanded_macros || ns == MacroNS || restricted_shadowing { - return check_usable(self, binding); - } else { - return Err((Undetermined, Weak::No)); - } - } - - // --- From now on we have no resolution. --- - - // Now we are in situation when new item/import can appear only from a glob or a macro - // expansion. With restricted shadowing names from globs and macro expansions cannot - // shadow names from outer scopes, so we can freely fallback from module search to search - // in outer scopes. For `early_resolve_ident_in_lexical_scope` to continue search in outer - // scopes we return `Undetermined` with `Weak::Yes`. - - // Check if one of unexpanded macros can still define the name, - // if it can then our "no resolution" result is not determined and can be invalidated. - if unexpanded_macros { - return Err((Undetermined, Weak::Yes)); - } - - // Check if one of glob imports can still define the name, - // if it can then our "no resolution" result is not determined and can be invalidated. - for glob_import in module.globs.borrow().iter() { - if !self.is_accessible_from(glob_import.vis.get(), parent_scope.module) { - continue; - } - let module = match glob_import.imported_module.get() { - Some(ModuleOrUniformRoot::Module(module)) => module, - Some(_) => continue, - None => return Err((Undetermined, Weak::Yes)), - }; - let tmp_parent_scope; - let (mut adjusted_parent_scope, mut ident) = - (parent_scope, ident.normalize_to_macros_2_0()); - match ident.span.glob_adjust(module.expansion, glob_import.span) { - Some(Some(def)) => { - tmp_parent_scope = - ParentScope { module: self.expn_def_scope(def), ..*parent_scope }; - adjusted_parent_scope = &tmp_parent_scope; - } - Some(None) => {} - None => continue, - }; - let result = self.resolve_ident_in_module_unadjusted( - ModuleOrUniformRoot::Module(module), - ident, - ns, - adjusted_parent_scope, - None, - ); - - match result { - Err(Determined) => continue, - Ok(binding) - if !self.is_accessible_from(binding.vis, glob_import.parent_scope.module) => - { - continue; - } - Ok(_) | Err(Undetermined) => return Err((Undetermined, Weak::Yes)), - } - } - - // No resolution and no one else can define the name - determinate error. - Err((Determined, Weak::No)) - } - // Given a binding and an import that resolves to it, // return the corresponding binding defined by the import. crate fn import( diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index a09a225a2b5d7..f4a650e8d2678 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -26,8 +26,6 @@ extern crate tracing; pub use rustc_hir::def::{Namespace, PerNS}; -use Determinacy::*; - use rustc_arena::{DroplessArena, TypedArena}; use rustc_ast::node_id::NodeMap; use rustc_ast::ptr::P; @@ -37,7 +35,6 @@ use rustc_ast::{Crate, CRATE_NODE_ID}; use rustc_ast::{Expr, ExprKind, LitKind}; use rustc_ast::{ItemKind, ModKind, Path}; use rustc_ast_lowering::ResolverAstLowering; -use rustc_ast_pretty::pprust; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_data_structures::intern::Interned; use rustc_data_structures::sync::Lrc; @@ -46,7 +43,7 @@ use rustc_errors::{ }; use rustc_expand::base::{DeriveResolutions, SyntaxExtension, SyntaxExtensionKind}; use rustc_hir::def::Namespace::*; -use rustc_hir::def::{self, CtorOf, DefKind, NonMacroAttrKind, PartialRes}; +use rustc_hir::def::{self, CtorOf, DefKind, PartialRes}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefPathHash, LocalDefId}; use rustc_hir::def_id::{CRATE_DEF_ID, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_hir::definitions::{DefKey, DefPathData, Definitions}; @@ -55,16 +52,15 @@ use rustc_index::vec::IndexVec; use rustc_metadata::creader::{CStore, CrateLoader}; use rustc_middle::metadata::ModChild; use rustc_middle::middle::privacy::AccessLevels; +use rustc_middle::span_bug; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, DefIdTree, MainDefinition, RegisteredTools, ResolverOutputs}; -use rustc_middle::{bug, span_bug}; use rustc_query_system::ich::StableHashingContext; use rustc_session::cstore::{CrateStore, MetadataLoaderDyn}; use rustc_session::lint; use rustc_session::lint::{BuiltinLintDiagnostics, LintBuffer}; use rustc_session::Session; -use rustc_span::edition::Edition; -use rustc_span::hygiene::{ExpnId, ExpnKind, LocalExpnId, MacroKind, SyntaxContext, Transparency}; +use rustc_span::hygiene::{ExpnId, LocalExpnId, MacroKind, SyntaxContext, Transparency}; use rustc_span::source_map::Spanned; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; @@ -78,7 +74,7 @@ use tracing::debug; use diagnostics::{extend_span_to_previous_binding, find_span_of_binding_until_next_binding}; use diagnostics::{ImportSuggestion, LabelSuggestion, Suggestion}; use imports::{Import, ImportKind, ImportResolver, NameResolution}; -use late::{ConstantItemKind, HasGenericParams, PathSource, Rib, RibKind::*}; +use late::{HasGenericParams, PathSource}; use macros::{MacroRulesBinding, MacroRulesScope, MacroRulesScopeRef}; use crate::access_levels::AccessLevelsVisitor; @@ -90,6 +86,7 @@ mod build_reduced_graph; mod check_unused; mod def_collector; mod diagnostics; +mod ident; mod imports; mod late; mod macros; @@ -1731,387 +1728,6 @@ impl<'a> Resolver<'a> { } } - /// A generic scope visitor. - /// Visits scopes in order to resolve some identifier in them or perform other actions. - /// If the callback returns `Some` result, we stop visiting scopes and return it. - fn visit_scopes( - &mut self, - scope_set: ScopeSet<'a>, - parent_scope: &ParentScope<'a>, - ctxt: SyntaxContext, - mut visitor: impl FnMut( - &mut Self, - Scope<'a>, - /*use_prelude*/ bool, - SyntaxContext, - ) -> Option, - ) -> Option { - // General principles: - // 1. Not controlled (user-defined) names should have higher priority than controlled names - // built into the language or standard library. This way we can add new names into the - // language or standard library without breaking user code. - // 2. "Closed set" below means new names cannot appear after the current resolution attempt. - // Places to search (in order of decreasing priority): - // (Type NS) - // 1. FIXME: Ribs (type parameters), there's no necessary infrastructure yet - // (open set, not controlled). - // 2. Names in modules (both normal `mod`ules and blocks), loop through hygienic parents - // (open, not controlled). - // 3. Extern prelude (open, the open part is from macro expansions, not controlled). - // 4. Tool modules (closed, controlled right now, but not in the future). - // 5. Standard library prelude (de-facto closed, controlled). - // 6. Language prelude (closed, controlled). - // (Value NS) - // 1. FIXME: Ribs (local variables), there's no necessary infrastructure yet - // (open set, not controlled). - // 2. Names in modules (both normal `mod`ules and blocks), loop through hygienic parents - // (open, not controlled). - // 3. Standard library prelude (de-facto closed, controlled). - // (Macro NS) - // 1-3. Derive helpers (open, not controlled). All ambiguities with other names - // are currently reported as errors. They should be higher in priority than preludes - // and probably even names in modules according to the "general principles" above. They - // also should be subject to restricted shadowing because are effectively produced by - // derives (you need to resolve the derive first to add helpers into scope), but they - // should be available before the derive is expanded for compatibility. - // It's mess in general, so we are being conservative for now. - // 1-3. `macro_rules` (open, not controlled), loop through `macro_rules` scopes. Have higher - // priority than prelude macros, but create ambiguities with macros in modules. - // 1-3. Names in modules (both normal `mod`ules and blocks), loop through hygienic parents - // (open, not controlled). Have higher priority than prelude macros, but create - // ambiguities with `macro_rules`. - // 4. `macro_use` prelude (open, the open part is from macro expansions, not controlled). - // 4a. User-defined prelude from macro-use - // (open, the open part is from macro expansions, not controlled). - // 4b. "Standard library prelude" part implemented through `macro-use` (closed, controlled). - // 4c. Standard library prelude (de-facto closed, controlled). - // 6. Language prelude: builtin attributes (closed, controlled). - - let rust_2015 = ctxt.edition() == Edition::Edition2015; - let (ns, macro_kind, is_absolute_path) = match scope_set { - ScopeSet::All(ns, _) => (ns, None, false), - ScopeSet::AbsolutePath(ns) => (ns, None, true), - ScopeSet::Macro(macro_kind) => (MacroNS, Some(macro_kind), false), - ScopeSet::Late(ns, ..) => (ns, None, false), - }; - let module = match scope_set { - // Start with the specified module. - ScopeSet::Late(_, module, _) => module, - // Jump out of trait or enum modules, they do not act as scopes. - _ => parent_scope.module.nearest_item_scope(), - }; - let mut scope = match ns { - _ if is_absolute_path => Scope::CrateRoot, - TypeNS | ValueNS => Scope::Module(module, None), - MacroNS => Scope::DeriveHelpers(parent_scope.expansion), - }; - let mut ctxt = ctxt.normalize_to_macros_2_0(); - let mut use_prelude = !module.no_implicit_prelude; - - loop { - let visit = match scope { - // Derive helpers are not in scope when resolving derives in the same container. - Scope::DeriveHelpers(expn_id) => { - !(expn_id == parent_scope.expansion && macro_kind == Some(MacroKind::Derive)) - } - Scope::DeriveHelpersCompat => true, - Scope::MacroRules(macro_rules_scope) => { - // Use "path compression" on `macro_rules` scope chains. This is an optimization - // used to avoid long scope chains, see the comments on `MacroRulesScopeRef`. - // As another consequence of this optimization visitors never observe invocation - // scopes for macros that were already expanded. - while let MacroRulesScope::Invocation(invoc_id) = macro_rules_scope.get() { - if let Some(next_scope) = self.output_macro_rules_scopes.get(&invoc_id) { - macro_rules_scope.set(next_scope.get()); - } else { - break; - } - } - true - } - Scope::CrateRoot => true, - Scope::Module(..) => true, - Scope::RegisteredAttrs => use_prelude, - Scope::MacroUsePrelude => use_prelude || rust_2015, - Scope::BuiltinAttrs => true, - Scope::ExternPrelude => use_prelude || is_absolute_path, - Scope::ToolPrelude => use_prelude, - Scope::StdLibPrelude => use_prelude || ns == MacroNS, - Scope::BuiltinTypes => true, - }; - - if visit { - if let break_result @ Some(..) = visitor(self, scope, use_prelude, ctxt) { - return break_result; - } - } - - scope = match scope { - Scope::DeriveHelpers(LocalExpnId::ROOT) => Scope::DeriveHelpersCompat, - Scope::DeriveHelpers(expn_id) => { - // Derive helpers are not visible to code generated by bang or derive macros. - let expn_data = expn_id.expn_data(); - match expn_data.kind { - ExpnKind::Root - | ExpnKind::Macro(MacroKind::Bang | MacroKind::Derive, _) => { - Scope::DeriveHelpersCompat - } - _ => Scope::DeriveHelpers(expn_data.parent.expect_local()), - } - } - Scope::DeriveHelpersCompat => Scope::MacroRules(parent_scope.macro_rules), - Scope::MacroRules(macro_rules_scope) => match macro_rules_scope.get() { - MacroRulesScope::Binding(binding) => { - Scope::MacroRules(binding.parent_macro_rules_scope) - } - MacroRulesScope::Invocation(invoc_id) => { - Scope::MacroRules(self.invocation_parent_scopes[&invoc_id].macro_rules) - } - MacroRulesScope::Empty => Scope::Module(module, None), - }, - Scope::CrateRoot => match ns { - TypeNS => { - ctxt.adjust(ExpnId::root()); - Scope::ExternPrelude - } - ValueNS | MacroNS => break, - }, - Scope::Module(module, prev_lint_id) => { - use_prelude = !module.no_implicit_prelude; - let derive_fallback_lint_id = match scope_set { - ScopeSet::Late(.., lint_id) => lint_id, - _ => None, - }; - match self.hygienic_lexical_parent(module, &mut ctxt, derive_fallback_lint_id) { - Some((parent_module, lint_id)) => { - Scope::Module(parent_module, lint_id.or(prev_lint_id)) - } - None => { - ctxt.adjust(ExpnId::root()); - match ns { - TypeNS => Scope::ExternPrelude, - ValueNS => Scope::StdLibPrelude, - MacroNS => Scope::RegisteredAttrs, - } - } - } - } - Scope::RegisteredAttrs => Scope::MacroUsePrelude, - Scope::MacroUsePrelude => Scope::StdLibPrelude, - Scope::BuiltinAttrs => break, // nowhere else to search - Scope::ExternPrelude if is_absolute_path => break, - Scope::ExternPrelude => Scope::ToolPrelude, - Scope::ToolPrelude => Scope::StdLibPrelude, - Scope::StdLibPrelude => match ns { - TypeNS => Scope::BuiltinTypes, - ValueNS => break, // nowhere else to search - MacroNS => Scope::BuiltinAttrs, - }, - Scope::BuiltinTypes => break, // nowhere else to search - }; - } - - None - } - - /// This resolves the identifier `ident` in the namespace `ns` in the current lexical scope. - /// More specifically, we proceed up the hierarchy of scopes and return the binding for - /// `ident` in the first scope that defines it (or None if no scopes define it). - /// - /// A block's items are above its local variables in the scope hierarchy, regardless of where - /// the items are defined in the block. For example, - /// ```rust - /// fn f() { - /// g(); // Since there are no local variables in scope yet, this resolves to the item. - /// let g = || {}; - /// fn g() {} - /// g(); // This resolves to the local variable `g` since it shadows the item. - /// } - /// ``` - /// - /// Invariant: This must only be called during main resolution, not during - /// import resolution. - fn resolve_ident_in_lexical_scope( - &mut self, - mut ident: Ident, - ns: Namespace, - parent_scope: &ParentScope<'a>, - finalize_full: Finalize, - ribs: &[Rib<'a>], - ) -> Option> { - assert!(ns == TypeNS || ns == ValueNS); - let orig_ident = ident; - if ident.name == kw::Empty { - return Some(LexicalScopeBinding::Res(Res::Err)); - } - let (general_span, normalized_span) = if ident.name == kw::SelfUpper { - // FIXME(jseyfried) improve `Self` hygiene - let empty_span = ident.span.with_ctxt(SyntaxContext::root()); - (empty_span, empty_span) - } else if ns == TypeNS { - let normalized_span = ident.span.normalize_to_macros_2_0(); - (normalized_span, normalized_span) - } else { - (ident.span.normalize_to_macro_rules(), ident.span.normalize_to_macros_2_0()) - }; - ident.span = general_span; - let normalized_ident = Ident { span: normalized_span, ..ident }; - - // Walk backwards up the ribs in scope. - let finalize = finalize_full.path_span(); - let mut module = self.graph_root; - for i in (0..ribs.len()).rev() { - debug!("walk rib\n{:?}", ribs[i].bindings); - // Use the rib kind to determine whether we are resolving parameters - // (macro 2.0 hygiene) or local variables (`macro_rules` hygiene). - let rib_ident = if ribs[i].kind.contains_params() { normalized_ident } else { ident }; - if let Some((original_rib_ident_def, res)) = ribs[i].bindings.get_key_value(&rib_ident) - { - // The ident resolves to a type parameter or local variable. - return Some(LexicalScopeBinding::Res(self.validate_res_from_ribs( - i, - rib_ident, - *res, - finalize, - *original_rib_ident_def, - ribs, - ))); - } - - module = match ribs[i].kind { - ModuleRibKind(module) => module, - MacroDefinition(def) if def == self.macro_def(ident.span.ctxt()) => { - // If an invocation of this macro created `ident`, give up on `ident` - // and switch to `ident`'s source from the macro definition. - ident.span.remove_mark(); - continue; - } - _ => continue, - }; - - match module.kind { - ModuleKind::Block(..) => {} // We can see through blocks - _ => break, - } - - let item = self.resolve_ident_in_module_unadjusted( - ModuleOrUniformRoot::Module(module), - ident, - ns, - parent_scope, - finalize, - ); - if let Ok(binding) = item { - // The ident resolves to an item. - return Some(LexicalScopeBinding::Item(binding)); - } - } - self.early_resolve_ident_in_lexical_scope( - orig_ident, - ScopeSet::Late(ns, module, finalize_full.node_id()), - parent_scope, - finalize, - finalize.is_some(), - ) - .ok() - .map(LexicalScopeBinding::Item) - } - - fn hygienic_lexical_parent( - &mut self, - module: Module<'a>, - ctxt: &mut SyntaxContext, - derive_fallback_lint_id: Option, - ) -> Option<(Module<'a>, Option)> { - if !module.expansion.outer_expn_is_descendant_of(*ctxt) { - return Some((self.expn_def_scope(ctxt.remove_mark()), None)); - } - - if let ModuleKind::Block(..) = module.kind { - return Some((module.parent.unwrap().nearest_item_scope(), None)); - } - - // We need to support the next case under a deprecation warning - // ``` - // struct MyStruct; - // ---- begin: this comes from a proc macro derive - // mod implementation_details { - // // Note that `MyStruct` is not in scope here. - // impl SomeTrait for MyStruct { ... } - // } - // ---- end - // ``` - // So we have to fall back to the module's parent during lexical resolution in this case. - if derive_fallback_lint_id.is_some() { - if let Some(parent) = module.parent { - // Inner module is inside the macro, parent module is outside of the macro. - if module.expansion != parent.expansion - && module.expansion.is_descendant_of(parent.expansion) - { - // The macro is a proc macro derive - if let Some(def_id) = module.expansion.expn_data().macro_def_id { - let ext = self.get_macro_by_def_id(def_id); - if ext.builtin_name.is_none() - && ext.macro_kind() == MacroKind::Derive - && parent.expansion.outer_expn_is_descendant_of(*ctxt) - { - return Some((parent, derive_fallback_lint_id)); - } - } - } - } - } - - None - } - - fn resolve_ident_in_module( - &mut self, - module: ModuleOrUniformRoot<'a>, - ident: Ident, - ns: Namespace, - parent_scope: &ParentScope<'a>, - finalize: Option, - ) -> Result<&'a NameBinding<'a>, Determinacy> { - self.resolve_ident_in_module_ext(module, ident, ns, parent_scope, finalize) - .map_err(|(determinacy, _)| determinacy) - } - - fn resolve_ident_in_module_ext( - &mut self, - module: ModuleOrUniformRoot<'a>, - mut ident: Ident, - ns: Namespace, - parent_scope: &ParentScope<'a>, - finalize: Option, - ) -> Result<&'a NameBinding<'a>, (Determinacy, Weak)> { - let tmp_parent_scope; - let mut adjusted_parent_scope = parent_scope; - match module { - ModuleOrUniformRoot::Module(m) => { - if let Some(def) = ident.span.normalize_to_macros_2_0_and_adjust(m.expansion) { - tmp_parent_scope = - ParentScope { module: self.expn_def_scope(def), ..*parent_scope }; - adjusted_parent_scope = &tmp_parent_scope; - } - } - ModuleOrUniformRoot::ExternPrelude => { - ident.span.normalize_to_macros_2_0_and_adjust(ExpnId::root()); - } - ModuleOrUniformRoot::CrateRootAndExternPrelude | ModuleOrUniformRoot::CurrentScope => { - // No adjustments - } - } - self.resolve_ident_in_module_unadjusted_ext( - module, - ident, - ns, - adjusted_parent_scope, - false, - finalize, - ) - } - fn resolve_crate_root(&mut self, ident: Ident) -> Module<'a> { debug!("resolve_crate_root({:?})", ident); let mut ctxt = ident.span.ctxt(); @@ -2194,384 +1810,6 @@ impl<'a> Resolver<'a> { module } - fn resolve_path( - &mut self, - path: &[Segment], - opt_ns: Option, // `None` indicates a module path in import - parent_scope: &ParentScope<'a>, - finalize: Finalize, - ) -> PathResult<'a> { - self.resolve_path_with_ribs(path, opt_ns, parent_scope, finalize, None) - } - - fn resolve_path_with_ribs( - &mut self, - path: &[Segment], - opt_ns: Option, // `None` indicates a module path in import - parent_scope: &ParentScope<'a>, - finalize_full: Finalize, - ribs: Option<&PerNS>>>, - ) -> PathResult<'a> { - debug!("resolve_path(path={:?}, opt_ns={:?}, finalize={:?})", path, opt_ns, finalize_full); - - let finalize = finalize_full.path_span(); - let mut module = None; - let mut allow_super = true; - let mut second_binding = None; - - for (i, &Segment { ident, id, has_generic_args: _ }) in path.iter().enumerate() { - debug!("resolve_path ident {} {:?} {:?}", i, ident, id); - let record_segment_res = |this: &mut Self, res| { - if finalize.is_some() { - if let Some(id) = id { - if !this.partial_res_map.contains_key(&id) { - assert!(id != ast::DUMMY_NODE_ID, "Trying to resolve dummy id"); - this.record_partial_res(id, PartialRes::new(res)); - } - } - } - }; - - let is_last = i == path.len() - 1; - let ns = if is_last { opt_ns.unwrap_or(TypeNS) } else { TypeNS }; - let name = ident.name; - - allow_super &= ns == TypeNS && (name == kw::SelfLower || name == kw::Super); - - if ns == TypeNS { - if allow_super && name == kw::Super { - let mut ctxt = ident.span.ctxt().normalize_to_macros_2_0(); - let self_module = match i { - 0 => Some(self.resolve_self(&mut ctxt, parent_scope.module)), - _ => match module { - Some(ModuleOrUniformRoot::Module(module)) => Some(module), - _ => None, - }, - }; - if let Some(self_module) = self_module { - if let Some(parent) = self_module.parent { - module = Some(ModuleOrUniformRoot::Module( - self.resolve_self(&mut ctxt, parent), - )); - continue; - } - } - return PathResult::failed(ident.span, false, finalize.is_some(), || { - ("there are too many leading `super` keywords".to_string(), None) - }); - } - if i == 0 { - if name == kw::SelfLower { - let mut ctxt = ident.span.ctxt().normalize_to_macros_2_0(); - module = Some(ModuleOrUniformRoot::Module( - self.resolve_self(&mut ctxt, parent_scope.module), - )); - continue; - } - if name == kw::PathRoot && ident.span.rust_2018() { - module = Some(ModuleOrUniformRoot::ExternPrelude); - continue; - } - if name == kw::PathRoot && ident.span.rust_2015() && self.session.rust_2018() { - // `::a::b` from 2015 macro on 2018 global edition - module = Some(ModuleOrUniformRoot::CrateRootAndExternPrelude); - continue; - } - if name == kw::PathRoot || name == kw::Crate || name == kw::DollarCrate { - // `::a::b`, `crate::a::b` or `$crate::a::b` - module = Some(ModuleOrUniformRoot::Module(self.resolve_crate_root(ident))); - continue; - } - } - } - - // Report special messages for path segment keywords in wrong positions. - if ident.is_path_segment_keyword() && i != 0 { - return PathResult::failed(ident.span, false, finalize.is_some(), || { - let name_str = if name == kw::PathRoot { - "crate root".to_string() - } else { - format!("`{}`", name) - }; - let label = if i == 1 && path[0].ident.name == kw::PathRoot { - format!("global paths cannot start with {}", name_str) - } else { - format!("{} in paths can only be used in start position", name_str) - }; - (label, None) - }); - } - - enum FindBindingResult<'a> { - Binding(Result<&'a NameBinding<'a>, Determinacy>), - PathResult(PathResult<'a>), - } - let find_binding_in_ns = |this: &mut Self, ns| { - let binding = if let Some(module) = module { - this.resolve_ident_in_module(module, ident, ns, parent_scope, finalize) - } else if ribs.is_none() || opt_ns.is_none() || opt_ns == Some(MacroNS) { - let scopes = ScopeSet::All(ns, opt_ns.is_none()); - this.early_resolve_ident_in_lexical_scope( - ident, - scopes, - parent_scope, - finalize, - finalize.is_some(), - ) - } else { - match this.resolve_ident_in_lexical_scope( - ident, - ns, - parent_scope, - finalize_full, - &ribs.unwrap()[ns], - ) { - // we found a locally-imported or available item/module - Some(LexicalScopeBinding::Item(binding)) => Ok(binding), - // we found a local variable or type param - Some(LexicalScopeBinding::Res(res)) - if opt_ns == Some(TypeNS) || opt_ns == Some(ValueNS) => - { - record_segment_res(this, res); - return FindBindingResult::PathResult(PathResult::NonModule( - PartialRes::with_unresolved_segments(res, path.len() - 1), - )); - } - _ => Err(Determinacy::determined(finalize.is_some())), - } - }; - FindBindingResult::Binding(binding) - }; - let binding = match find_binding_in_ns(self, ns) { - FindBindingResult::PathResult(x) => return x, - FindBindingResult::Binding(binding) => binding, - }; - match binding { - Ok(binding) => { - if i == 1 { - second_binding = Some(binding); - } - let res = binding.res(); - let maybe_assoc = opt_ns != Some(MacroNS) && PathSource::Type.is_expected(res); - if let Some(next_module) = binding.module() { - module = Some(ModuleOrUniformRoot::Module(next_module)); - record_segment_res(self, res); - } else if res == Res::ToolMod && i + 1 != path.len() { - if binding.is_import() { - self.session - .struct_span_err( - ident.span, - "cannot use a tool module through an import", - ) - .span_note(binding.span, "the tool module imported here") - .emit(); - } - let res = Res::NonMacroAttr(NonMacroAttrKind::Tool); - return PathResult::NonModule(PartialRes::new(res)); - } else if res == Res::Err { - return PathResult::NonModule(PartialRes::new(Res::Err)); - } else if opt_ns.is_some() && (is_last || maybe_assoc) { - self.lint_if_path_starts_with_module(finalize_full, path, second_binding); - return PathResult::NonModule(PartialRes::with_unresolved_segments( - res, - path.len() - i - 1, - )); - } else { - return PathResult::failed(ident.span, is_last, finalize.is_some(), || { - let label = format!( - "`{ident}` is {} {}, not a module", - res.article(), - res.descr() - ); - (label, None) - }); - } - } - Err(Undetermined) => return PathResult::Indeterminate, - Err(Determined) => { - if let Some(ModuleOrUniformRoot::Module(module)) = module { - if opt_ns.is_some() && !module.is_normal() { - return PathResult::NonModule(PartialRes::with_unresolved_segments( - module.res().unwrap(), - path.len() - i, - )); - } - } - - return PathResult::failed(ident.span, is_last, finalize.is_some(), || { - let module_res = match module { - Some(ModuleOrUniformRoot::Module(module)) => module.res(), - _ => None, - }; - if module_res == self.graph_root.res() { - let is_mod = |res| matches!(res, Res::Def(DefKind::Mod, _)); - let mut candidates = - self.lookup_import_candidates(ident, TypeNS, parent_scope, is_mod); - candidates.sort_by_cached_key(|c| { - (c.path.segments.len(), pprust::path_to_string(&c.path)) - }); - if let Some(candidate) = candidates.get(0) { - ( - String::from("unresolved import"), - Some(( - vec![(ident.span, pprust::path_to_string(&candidate.path))], - String::from("a similar path exists"), - Applicability::MaybeIncorrect, - )), - ) - } else if self.session.edition() == Edition::Edition2015 { - (format!("maybe a missing crate `{}`?", ident), None) - } else { - (format!("could not find `{}` in the crate root", ident), None) - } - } else if i == 0 { - if ident - .name - .as_str() - .chars() - .next() - .map_or(false, |c| c.is_ascii_uppercase()) - { - // Check whether the name refers to an item in the value namespace. - let suggestion = if ribs.is_some() { - let match_span = match self.resolve_ident_in_lexical_scope( - ident, - ValueNS, - parent_scope, - Finalize::No, - &ribs.unwrap()[ValueNS], - ) { - // Name matches a local variable. For example: - // ``` - // fn f() { - // let Foo: &str = ""; - // println!("{}", Foo::Bar); // Name refers to local - // // variable `Foo`. - // } - // ``` - Some(LexicalScopeBinding::Res(Res::Local(id))) => { - Some(*self.pat_span_map.get(&id).unwrap()) - } - - // Name matches item from a local name binding - // created by `use` declaration. For example: - // ``` - // pub Foo: &str = ""; - // - // mod submod { - // use super::Foo; - // println!("{}", Foo::Bar); // Name refers to local - // // binding `Foo`. - // } - // ``` - Some(LexicalScopeBinding::Item(name_binding)) => { - Some(name_binding.span) - } - _ => None, - }; - - if let Some(span) = match_span { - Some(( - vec![(span, String::from(""))], - format!( - "`{}` is defined here, but is not a type", - ident - ), - Applicability::MaybeIncorrect, - )) - } else { - None - } - } else { - None - }; - - (format!("use of undeclared type `{}`", ident), suggestion) - } else { - ( - format!("use of undeclared crate or module `{}`", ident), - if ident.name == sym::alloc { - Some(( - vec![], - String::from( - "add `extern crate alloc` to use the `alloc` crate", - ), - Applicability::MaybeIncorrect, - )) - } else { - self.find_similarly_named_module_or_crate( - ident.name, - &parent_scope.module, - ) - .map(|sugg| { - ( - vec![(ident.span, sugg.to_string())], - String::from( - "there is a crate or module with a similar name", - ), - Applicability::MaybeIncorrect, - ) - }) - }, - ) - } - } else { - let parent = path[i - 1].ident.name; - let parent = match parent { - // ::foo is mounted at the crate root for 2015, and is the extern - // prelude for 2018+ - kw::PathRoot if self.session.edition() > Edition::Edition2015 => { - "the list of imported crates".to_owned() - } - kw::PathRoot | kw::Crate => "the crate root".to_owned(), - _ => { - format!("`{}`", parent) - } - }; - - let mut msg = format!("could not find `{}` in {}", ident, parent); - if ns == TypeNS || ns == ValueNS { - let ns_to_try = if ns == TypeNS { ValueNS } else { TypeNS }; - if let FindBindingResult::Binding(Ok(binding)) = - find_binding_in_ns(self, ns_to_try) - { - let mut found = |what| { - msg = format!( - "expected {}, found {} `{}` in {}", - ns.descr(), - what, - ident, - parent - ) - }; - if binding.module().is_some() { - found("module") - } else { - match binding.res() { - def::Res::::Def(kind, id) => { - found(kind.descr(id)) - } - _ => found(ns_to_try.descr()), - } - } - }; - } - (msg, None) - } - }); - } - } - } - - self.lint_if_path_starts_with_module(finalize_full, path, second_binding); - - PathResult::Module(match module { - Some(module) => module, - None if path.is_empty() => ModuleOrUniformRoot::CurrentScope, - _ => bug!("resolve_path: non-empty path `{:?}` has no module", path), - }) - } - fn lint_if_path_starts_with_module( &mut self, finalize: Finalize, @@ -2631,239 +1869,6 @@ impl<'a> Resolver<'a> { ); } - // Validate a local resolution (from ribs). - fn validate_res_from_ribs( - &mut self, - rib_index: usize, - rib_ident: Ident, - mut res: Res, - finalize: Option, - original_rib_ident_def: Ident, - all_ribs: &[Rib<'a>], - ) -> Res { - const CG_BUG_STR: &str = "min_const_generics resolve check didn't stop compilation"; - debug!("validate_res_from_ribs({:?})", res); - let ribs = &all_ribs[rib_index + 1..]; - - // An invalid forward use of a generic parameter from a previous default. - if let ForwardGenericParamBanRibKind = all_ribs[rib_index].kind { - if let Some(span) = finalize { - let res_error = if rib_ident.name == kw::SelfUpper { - ResolutionError::SelfInGenericParamDefault - } else { - ResolutionError::ForwardDeclaredGenericParam - }; - self.report_error(span, res_error); - } - assert_eq!(res, Res::Err); - return Res::Err; - } - - match res { - Res::Local(_) => { - use ResolutionError::*; - let mut res_err = None; - - for rib in ribs { - match rib.kind { - NormalRibKind - | ClosureOrAsyncRibKind - | ModuleRibKind(..) - | MacroDefinition(..) - | ForwardGenericParamBanRibKind => { - // Nothing to do. Continue. - } - ItemRibKind(_) | FnItemRibKind | AssocItemRibKind => { - // This was an attempt to access an upvar inside a - // named function item. This is not allowed, so we - // report an error. - if let Some(span) = finalize { - // We don't immediately trigger a resolve error, because - // we want certain other resolution errors (namely those - // emitted for `ConstantItemRibKind` below) to take - // precedence. - res_err = Some((span, CannotCaptureDynamicEnvironmentInFnItem)); - } - } - ConstantItemRibKind(_, item) => { - // Still doesn't deal with upvars - if let Some(span) = finalize { - let (span, resolution_error) = - if let Some((ident, constant_item_kind)) = item { - let kind_str = match constant_item_kind { - ConstantItemKind::Const => "const", - ConstantItemKind::Static => "static", - }; - ( - span, - AttemptToUseNonConstantValueInConstant( - ident, "let", kind_str, - ), - ) - } else { - ( - rib_ident.span, - AttemptToUseNonConstantValueInConstant( - original_rib_ident_def, - "const", - "let", - ), - ) - }; - self.report_error(span, resolution_error); - } - return Res::Err; - } - ConstParamTyRibKind => { - if let Some(span) = finalize { - self.report_error(span, ParamInTyOfConstParam(rib_ident.name)); - } - return Res::Err; - } - } - } - if let Some((span, res_err)) = res_err { - self.report_error(span, res_err); - return Res::Err; - } - } - Res::Def(DefKind::TyParam, _) | Res::SelfTy { .. } => { - for rib in ribs { - let has_generic_params: HasGenericParams = match rib.kind { - NormalRibKind - | ClosureOrAsyncRibKind - | AssocItemRibKind - | ModuleRibKind(..) - | MacroDefinition(..) - | ForwardGenericParamBanRibKind => { - // Nothing to do. Continue. - continue; - } - - ConstantItemRibKind(trivial, _) => { - let features = self.session.features_untracked(); - // HACK(min_const_generics): We currently only allow `N` or `{ N }`. - if !(trivial || features.generic_const_exprs) { - // HACK(min_const_generics): If we encounter `Self` in an anonymous constant - // we can't easily tell if it's generic at this stage, so we instead remember - // this and then enforce the self type to be concrete later on. - if let Res::SelfTy { trait_, alias_to: Some((def, _)) } = res { - res = Res::SelfTy { trait_, alias_to: Some((def, true)) } - } else { - if let Some(span) = finalize { - self.report_error( - span, - ResolutionError::ParamInNonTrivialAnonConst { - name: rib_ident.name, - is_type: true, - }, - ); - self.session.delay_span_bug(span, CG_BUG_STR); - } - - return Res::Err; - } - } - - continue; - } - - // This was an attempt to use a type parameter outside its scope. - ItemRibKind(has_generic_params) => has_generic_params, - FnItemRibKind => HasGenericParams::Yes, - ConstParamTyRibKind => { - if let Some(span) = finalize { - self.report_error( - span, - ResolutionError::ParamInTyOfConstParam(rib_ident.name), - ); - } - return Res::Err; - } - }; - - if let Some(span) = finalize { - self.report_error( - span, - ResolutionError::GenericParamsFromOuterFunction( - res, - has_generic_params, - ), - ); - } - return Res::Err; - } - } - Res::Def(DefKind::ConstParam, _) => { - let mut ribs = ribs.iter().peekable(); - if let Some(Rib { kind: FnItemRibKind, .. }) = ribs.peek() { - // When declaring const parameters inside function signatures, the first rib - // is always a `FnItemRibKind`. In this case, we can skip it, to avoid it - // (spuriously) conflicting with the const param. - ribs.next(); - } - - for rib in ribs { - let has_generic_params = match rib.kind { - NormalRibKind - | ClosureOrAsyncRibKind - | AssocItemRibKind - | ModuleRibKind(..) - | MacroDefinition(..) - | ForwardGenericParamBanRibKind => continue, - - ConstantItemRibKind(trivial, _) => { - let features = self.session.features_untracked(); - // HACK(min_const_generics): We currently only allow `N` or `{ N }`. - if !(trivial || features.generic_const_exprs) { - if let Some(span) = finalize { - self.report_error( - span, - ResolutionError::ParamInNonTrivialAnonConst { - name: rib_ident.name, - is_type: false, - }, - ); - self.session.delay_span_bug(span, CG_BUG_STR); - } - - return Res::Err; - } - - continue; - } - - ItemRibKind(has_generic_params) => has_generic_params, - FnItemRibKind => HasGenericParams::Yes, - ConstParamTyRibKind => { - if let Some(span) = finalize { - self.report_error( - span, - ResolutionError::ParamInTyOfConstParam(rib_ident.name), - ); - } - return Res::Err; - } - }; - - // This was an attempt to use a const parameter outside its scope. - if let Some(span) = finalize { - self.report_error( - span, - ResolutionError::GenericParamsFromOuterFunction( - res, - has_generic_params, - ), - ); - } - return Res::Err; - } - } - _ => {} - } - res - } - fn record_partial_res(&mut self, node_id: NodeId, resolution: PartialRes) { debug!("(recording res) recording {:?} for {}", resolution, node_id); if let Some(prev_res) = self.partial_res_map.insert(node_id, resolution) { diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 3a90908c0df0a..71795ef1633f2 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -3,9 +3,9 @@ use crate::imports::ImportResolver; use crate::Namespace::*; -use crate::{AmbiguityError, AmbiguityErrorMisc, AmbiguityKind, BuiltinMacroState, Determinacy}; -use crate::{DeriveData, Finalize, ParentScope, ResolutionError, Resolver, Scope, ScopeSet, Weak}; -use crate::{ModuleKind, ModuleOrUniformRoot, NameBinding, PathResult, Segment, ToNameBinding}; +use crate::{BuiltinMacroState, Determinacy}; +use crate::{DeriveData, Finalize, ParentScope, ResolutionError, Resolver, ScopeSet}; +use crate::{ModuleKind, ModuleOrUniformRoot, NameBinding, PathResult, Segment}; use rustc_ast::{self as ast, Inline, ItemKind, ModKind, NodeId}; use rustc_ast_lowering::ResolverAstLowering; use rustc_ast_pretty::pprust; @@ -18,14 +18,11 @@ use rustc_expand::base::{Annotatable, DeriveResolutions, Indeterminate, Resolver use rustc_expand::base::{SyntaxExtension, SyntaxExtensionKind}; use rustc_expand::compile_declarative_macro; use rustc_expand::expand::{AstFragment, Invocation, InvocationKind, SupportsMacroExpansion}; -use rustc_feature::is_builtin_attr_name; use rustc_hir::def::{self, DefKind, NonMacroAttrKind}; use rustc_hir::def_id::{CrateNum, LocalDefId}; -use rustc_hir::PrimTy; use rustc_middle::middle::stability; -use rustc_middle::ty::{self, RegisteredTools}; -use rustc_session::lint::builtin::{LEGACY_DERIVE_HELPERS, PROC_MACRO_DERIVE_RESOLUTION_FALLBACK}; -use rustc_session::lint::builtin::{SOFT_UNSTABLE, UNUSED_MACROS}; +use rustc_middle::ty::RegisteredTools; +use rustc_session::lint::builtin::{LEGACY_DERIVE_HELPERS, SOFT_UNSTABLE, UNUSED_MACROS}; use rustc_session::lint::BuiltinLintDiagnostics; use rustc_session::parse::feature_err; use rustc_session::Session; @@ -35,7 +32,7 @@ use rustc_span::hygiene::{AstPass, MacroKind}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; use std::cell::Cell; -use std::{mem, ptr}; +use std::mem; type Res = def::Res; @@ -73,10 +70,10 @@ pub enum MacroRulesScope<'a> { /// in a module (including derives) and hurt performance. pub(crate) type MacroRulesScopeRef<'a> = Interned<'a, Cell>>; -// Macro namespace is separated into two sub-namespaces, one for bang macros and -// one for attribute-like macros (attributes, derives). -// We ignore resolutions from one sub-namespace when searching names in scope for another. -fn sub_namespace_match(candidate: Option, requirement: Option) -> bool { +/// Macro namespace is separated into two sub-namespaces, one for bang macros and +/// one for attribute-like macros (attributes, derives). +/// We ignore resolutions from one sub-namespace when searching names in scope for another. +crate fn sub_namespace_match(candidate: Option, requirement: Option) -> bool { #[derive(PartialEq)] enum SubNS { Bang, @@ -630,355 +627,6 @@ impl<'a> Resolver<'a> { res.map(|res| (self.get_macro(res), res)) } - // Resolve an identifier in lexical scope. - // This is a variation of `fn resolve_ident_in_lexical_scope` that can be run during - // expansion and import resolution (perhaps they can be merged in the future). - // The function is used for resolving initial segments of macro paths (e.g., `foo` in - // `foo::bar!(); or `foo!();`) and also for import paths on 2018 edition. - crate fn early_resolve_ident_in_lexical_scope( - &mut self, - orig_ident: Ident, - scope_set: ScopeSet<'a>, - parent_scope: &ParentScope<'a>, - finalize: Option, - force: bool, - ) -> Result<&'a NameBinding<'a>, Determinacy> { - bitflags::bitflags! { - struct Flags: u8 { - const MACRO_RULES = 1 << 0; - const MODULE = 1 << 1; - const MISC_SUGGEST_CRATE = 1 << 2; - const MISC_SUGGEST_SELF = 1 << 3; - const MISC_FROM_PRELUDE = 1 << 4; - } - } - - assert!(force || !finalize.is_some()); // `finalize` implies `force` - - // Make sure `self`, `super` etc produce an error when passed to here. - if orig_ident.is_path_segment_keyword() { - return Err(Determinacy::Determined); - } - - let (ns, macro_kind, is_import) = match scope_set { - ScopeSet::All(ns, is_import) => (ns, None, is_import), - ScopeSet::AbsolutePath(ns) => (ns, None, false), - ScopeSet::Macro(macro_kind) => (MacroNS, Some(macro_kind), false), - ScopeSet::Late(ns, ..) => (ns, None, false), - }; - - // This is *the* result, resolution from the scope closest to the resolved identifier. - // However, sometimes this result is "weak" because it comes from a glob import or - // a macro expansion, and in this case it cannot shadow names from outer scopes, e.g. - // mod m { ... } // solution in outer scope - // { - // use prefix::*; // imports another `m` - innermost solution - // // weak, cannot shadow the outer `m`, need to report ambiguity error - // m::mac!(); - // } - // So we have to save the innermost solution and continue searching in outer scopes - // to detect potential ambiguities. - let mut innermost_result: Option<(&NameBinding<'_>, Flags)> = None; - let mut determinacy = Determinacy::Determined; - - // Go through all the scopes and try to resolve the name. - let break_result = self.visit_scopes( - scope_set, - parent_scope, - orig_ident.span.ctxt(), - |this, scope, use_prelude, ctxt| { - let ident = Ident::new(orig_ident.name, orig_ident.span.with_ctxt(ctxt)); - let ok = |res, span, arenas| { - Ok(( - (res, ty::Visibility::Public, span, LocalExpnId::ROOT) - .to_name_binding(arenas), - Flags::empty(), - )) - }; - let result = match scope { - Scope::DeriveHelpers(expn_id) => { - if let Some(attr) = this - .helper_attrs - .get(&expn_id) - .and_then(|attrs| attrs.iter().rfind(|i| ident == **i)) - { - let binding = ( - Res::NonMacroAttr(NonMacroAttrKind::DeriveHelper), - ty::Visibility::Public, - attr.span, - expn_id, - ) - .to_name_binding(this.arenas); - Ok((binding, Flags::empty())) - } else { - Err(Determinacy::Determined) - } - } - Scope::DeriveHelpersCompat => { - let mut result = Err(Determinacy::Determined); - for derive in parent_scope.derives { - let parent_scope = &ParentScope { derives: &[], ..*parent_scope }; - match this.resolve_macro_path( - derive, - Some(MacroKind::Derive), - parent_scope, - true, - force, - ) { - Ok((Some(ext), _)) => { - if ext.helper_attrs.contains(&ident.name) { - result = ok( - Res::NonMacroAttr(NonMacroAttrKind::DeriveHelperCompat), - derive.span, - this.arenas, - ); - break; - } - } - Ok(_) | Err(Determinacy::Determined) => {} - Err(Determinacy::Undetermined) => { - result = Err(Determinacy::Undetermined) - } - } - } - result - } - Scope::MacroRules(macro_rules_scope) => match macro_rules_scope.get() { - MacroRulesScope::Binding(macro_rules_binding) - if ident == macro_rules_binding.ident => - { - Ok((macro_rules_binding.binding, Flags::MACRO_RULES)) - } - MacroRulesScope::Invocation(_) => Err(Determinacy::Undetermined), - _ => Err(Determinacy::Determined), - }, - Scope::CrateRoot => { - let root_ident = Ident::new(kw::PathRoot, ident.span); - let root_module = this.resolve_crate_root(root_ident); - let binding = this.resolve_ident_in_module_ext( - ModuleOrUniformRoot::Module(root_module), - ident, - ns, - parent_scope, - finalize, - ); - match binding { - Ok(binding) => Ok((binding, Flags::MODULE | Flags::MISC_SUGGEST_CRATE)), - Err((Determinacy::Undetermined, Weak::No)) => { - return Some(Err(Determinacy::determined(force))); - } - Err((Determinacy::Undetermined, Weak::Yes)) => { - Err(Determinacy::Undetermined) - } - Err((Determinacy::Determined, _)) => Err(Determinacy::Determined), - } - } - Scope::Module(module, derive_fallback_lint_id) => { - let adjusted_parent_scope = &ParentScope { module, ..*parent_scope }; - let binding = this.resolve_ident_in_module_unadjusted_ext( - ModuleOrUniformRoot::Module(module), - ident, - ns, - adjusted_parent_scope, - !matches!(scope_set, ScopeSet::Late(..)), - finalize, - ); - match binding { - Ok(binding) => { - if let Some(lint_id) = derive_fallback_lint_id { - this.lint_buffer.buffer_lint_with_diagnostic( - PROC_MACRO_DERIVE_RESOLUTION_FALLBACK, - lint_id, - orig_ident.span, - &format!( - "cannot find {} `{}` in this scope", - ns.descr(), - ident - ), - BuiltinLintDiagnostics::ProcMacroDeriveResolutionFallback( - orig_ident.span, - ), - ); - } - let misc_flags = if ptr::eq(module, this.graph_root) { - Flags::MISC_SUGGEST_CRATE - } else if module.is_normal() { - Flags::MISC_SUGGEST_SELF - } else { - Flags::empty() - }; - Ok((binding, Flags::MODULE | misc_flags)) - } - Err((Determinacy::Undetermined, Weak::No)) => { - return Some(Err(Determinacy::determined(force))); - } - Err((Determinacy::Undetermined, Weak::Yes)) => { - Err(Determinacy::Undetermined) - } - Err((Determinacy::Determined, _)) => Err(Determinacy::Determined), - } - } - Scope::RegisteredAttrs => match this.registered_attrs.get(&ident).cloned() { - Some(ident) => ok( - Res::NonMacroAttr(NonMacroAttrKind::Registered), - ident.span, - this.arenas, - ), - None => Err(Determinacy::Determined), - }, - Scope::MacroUsePrelude => { - match this.macro_use_prelude.get(&ident.name).cloned() { - Some(binding) => Ok((binding, Flags::MISC_FROM_PRELUDE)), - None => Err(Determinacy::determined( - this.graph_root.unexpanded_invocations.borrow().is_empty(), - )), - } - } - Scope::BuiltinAttrs => { - if is_builtin_attr_name(ident.name) { - ok( - Res::NonMacroAttr(NonMacroAttrKind::Builtin(ident.name)), - DUMMY_SP, - this.arenas, - ) - } else { - Err(Determinacy::Determined) - } - } - Scope::ExternPrelude => { - match this.extern_prelude_get(ident, finalize.is_some()) { - Some(binding) => Ok((binding, Flags::empty())), - None => Err(Determinacy::determined( - this.graph_root.unexpanded_invocations.borrow().is_empty(), - )), - } - } - Scope::ToolPrelude => match this.registered_tools.get(&ident).cloned() { - Some(ident) => ok(Res::ToolMod, ident.span, this.arenas), - None => Err(Determinacy::Determined), - }, - Scope::StdLibPrelude => { - let mut result = Err(Determinacy::Determined); - if let Some(prelude) = this.prelude { - if let Ok(binding) = this.resolve_ident_in_module_unadjusted( - ModuleOrUniformRoot::Module(prelude), - ident, - ns, - parent_scope, - None, - ) { - if use_prelude || this.is_builtin_macro(binding.res()) { - result = Ok((binding, Flags::MISC_FROM_PRELUDE)); - } - } - } - result - } - Scope::BuiltinTypes => match PrimTy::from_name(ident.name) { - Some(prim_ty) => ok(Res::PrimTy(prim_ty), DUMMY_SP, this.arenas), - None => Err(Determinacy::Determined), - }, - }; - - match result { - Ok((binding, flags)) - if sub_namespace_match(binding.macro_kind(), macro_kind) => - { - if finalize.is_none() || matches!(scope_set, ScopeSet::Late(..)) { - return Some(Ok(binding)); - } - - if let Some((innermost_binding, innermost_flags)) = innermost_result { - // Found another solution, if the first one was "weak", report an error. - let (res, innermost_res) = (binding.res(), innermost_binding.res()); - if res != innermost_res { - let is_builtin = |res| { - matches!(res, Res::NonMacroAttr(NonMacroAttrKind::Builtin(..))) - }; - let derive_helper = - Res::NonMacroAttr(NonMacroAttrKind::DeriveHelper); - let derive_helper_compat = - Res::NonMacroAttr(NonMacroAttrKind::DeriveHelperCompat); - - let ambiguity_error_kind = if is_import { - Some(AmbiguityKind::Import) - } else if is_builtin(innermost_res) || is_builtin(res) { - Some(AmbiguityKind::BuiltinAttr) - } else if innermost_res == derive_helper_compat - || res == derive_helper_compat && innermost_res != derive_helper - { - Some(AmbiguityKind::DeriveHelper) - } else if innermost_flags.contains(Flags::MACRO_RULES) - && flags.contains(Flags::MODULE) - && !this.disambiguate_macro_rules_vs_modularized( - innermost_binding, - binding, - ) - || flags.contains(Flags::MACRO_RULES) - && innermost_flags.contains(Flags::MODULE) - && !this.disambiguate_macro_rules_vs_modularized( - binding, - innermost_binding, - ) - { - Some(AmbiguityKind::MacroRulesVsModularized) - } else if innermost_binding.is_glob_import() { - Some(AmbiguityKind::GlobVsOuter) - } else if innermost_binding - .may_appear_after(parent_scope.expansion, binding) - { - Some(AmbiguityKind::MoreExpandedVsOuter) - } else { - None - }; - if let Some(kind) = ambiguity_error_kind { - let misc = |f: Flags| { - if f.contains(Flags::MISC_SUGGEST_CRATE) { - AmbiguityErrorMisc::SuggestCrate - } else if f.contains(Flags::MISC_SUGGEST_SELF) { - AmbiguityErrorMisc::SuggestSelf - } else if f.contains(Flags::MISC_FROM_PRELUDE) { - AmbiguityErrorMisc::FromPrelude - } else { - AmbiguityErrorMisc::None - } - }; - this.ambiguity_errors.push(AmbiguityError { - kind, - ident: orig_ident, - b1: innermost_binding, - b2: binding, - misc1: misc(innermost_flags), - misc2: misc(flags), - }); - return Some(Ok(innermost_binding)); - } - } - } else { - // Found the first solution. - innermost_result = Some((binding, flags)); - } - } - Ok(..) | Err(Determinacy::Determined) => {} - Err(Determinacy::Undetermined) => determinacy = Determinacy::Undetermined, - } - - None - }, - ); - - if let Some(break_result) = break_result { - return break_result; - } - - // The first found solution was the only one, return it. - if let Some((binding, _)) = innermost_result { - return Ok(binding); - } - - Err(Determinacy::determined(determinacy == Determinacy::Determined || force)) - } - crate fn finalize_macro_resolutions(&mut self) { let check_consistency = |this: &mut Self, path: &[Segment], From eb7f5673d9992b1dccc1bc97460267e630fc4ca0 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Fri, 8 Apr 2022 22:50:32 +0200 Subject: [PATCH 12/23] Simplify binding finalization. --- compiler/rustc_resolve/src/ident.rs | 107 ++++++++++++++-------------- 1 file changed, 54 insertions(+), 53 deletions(-) diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 0111ab506c54f..3232e66e8238a 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -850,12 +850,65 @@ impl<'a> Resolver<'a> { let resolution = self.resolution(module, key).try_borrow_mut().map_err(|_| (Determined, Weak::No))?; // This happens when there is a cycle of imports. - if let Some(binding) = resolution.binding && let Some(path_span) = finalize { + if let Some(path_span) = finalize { + let Some(mut binding) = resolution.binding else { + return Err((Determined, Weak::No)); + }; + if !restricted_shadowing && binding.expansion != LocalExpnId::ROOT { if let NameBindingKind::Res(_, true) = binding.kind { self.macro_expanded_macro_export_errors.insert((path_span, binding.span)); } } + + // If the primary binding is unusable, search further and return the shadowed glob + // binding if it exists. What we really want here is having two separate scopes in + // a module - one for non-globs and one for globs, but until that's done use this + // hack to avoid inconsistent resolution ICEs during import validation. + if let Some(unusable_binding) = self.unusable_binding + && ptr::eq(binding, unusable_binding) + { + let Some(shadowed) = resolution.shadowed_glob else { + return Err((Determined, Weak::No)); + }; + + if ptr::eq(shadowed, unusable_binding) { + return Err((Determined, Weak::No)); + } + + binding = shadowed; + } + + if !self.is_accessible_from(binding.vis, parent_scope.module) { + if self.last_import_segment { + return Err((Determined, Weak::No)); + } else { + self.privacy_errors.push(PrivacyError { + ident, + binding, + dedup_span: path_span, + }); + } + } + + // Forbid expanded shadowing to avoid time travel. + if let Some(shadowed_glob) = resolution.shadowed_glob + && restricted_shadowing + && binding.expansion != LocalExpnId::ROOT + && binding.res() != shadowed_glob.res() + { + self.ambiguity_errors.push(AmbiguityError { + kind: AmbiguityKind::GlobVsExpanded, + ident, + b1: binding, + b2: shadowed_glob, + misc1: AmbiguityErrorMisc::None, + misc2: AmbiguityErrorMisc::None, + }); + } + + self.record_use(ident, binding, restricted_shadowing); + return Ok(binding); } let check_usable = |this: &mut Self, binding: &'a NameBinding<'a>| { @@ -868,58 +921,6 @@ impl<'a> Resolver<'a> { if usable { Ok(binding) } else { Err((Determined, Weak::No)) } }; - if let Some(path_span) = finalize { - return resolution - .binding - .and_then(|binding| { - // If the primary binding is unusable, search further and return the shadowed glob - // binding if it exists. What we really want here is having two separate scopes in - // a module - one for non-globs and one for globs, but until that's done use this - // hack to avoid inconsistent resolution ICEs during import validation. - if let Some(unusable_binding) = self.unusable_binding { - if ptr::eq(binding, unusable_binding) { - return resolution.shadowed_glob; - } - } - Some(binding) - }) - .ok_or((Determined, Weak::No)) - .and_then(|binding| { - if self.last_import_segment && check_usable(self, binding).is_err() { - Err((Determined, Weak::No)) - } else { - self.record_use(ident, binding, restricted_shadowing); - - if let Some(shadowed_glob) = resolution.shadowed_glob { - // Forbid expanded shadowing to avoid time travel. - if restricted_shadowing - && binding.expansion != LocalExpnId::ROOT - && binding.res() != shadowed_glob.res() - { - self.ambiguity_errors.push(AmbiguityError { - kind: AmbiguityKind::GlobVsExpanded, - ident, - b1: binding, - b2: shadowed_glob, - misc1: AmbiguityErrorMisc::None, - misc2: AmbiguityErrorMisc::None, - }); - } - } - - if !self.is_accessible_from(binding.vis, parent_scope.module) { - self.privacy_errors.push(PrivacyError { - ident, - binding, - dedup_span: path_span, - }); - } - - Ok(binding) - } - }); - } - // Items and single imports are not shadowable, if we have one, then it's determined. if let Some(binding) = resolution.binding { if !binding.is_glob_import() { From 24b37a737467ccb4ef93a95f822b9933f3aa56b0 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Fri, 8 Apr 2022 22:50:56 +0200 Subject: [PATCH 13/23] Pass last_import_segment and unusable_binding as parameters. --- .../rustc_resolve/src/build_reduced_graph.rs | 4 +- compiler/rustc_resolve/src/diagnostics.rs | 12 +- compiler/rustc_resolve/src/ident.rs | 111 ++++++++++++++++-- compiler/rustc_resolve/src/imports.rs | 38 +++--- compiler/rustc_resolve/src/late.rs | 57 +++++---- .../rustc_resolve/src/late/diagnostics.rs | 3 +- compiler/rustc_resolve/src/lib.rs | 20 +--- compiler/rustc_resolve/src/macros.rs | 11 +- 8 files changed, 177 insertions(+), 79 deletions(-) diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 07d261da8132f..54e8c03156d55 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -297,6 +297,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { Some(TypeNS), parent_scope, if finalize { Finalize::SimplePath(id, path.span) } else { Finalize::No }, + None, ) { PathResult::Module(ModuleOrUniformRoot::Module(module)) => { let res = module.res().expect("visibility resolved to unnamed block"); @@ -1124,12 +1125,11 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { }); } else { for ident in single_imports.iter().cloned() { - let result = self.r.resolve_ident_in_module( + let result = self.r.maybe_resolve_ident_in_module( ModuleOrUniformRoot::Module(module), ident, MacroNS, &self.parent_scope, - None, ); if let Ok(binding) = result { let import = macro_use_import(self, ident.span); diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 609dbd1fe1b4e..fdcc045efd815 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -25,7 +25,7 @@ use crate::imports::{Import, ImportKind, ImportResolver}; use crate::path_names_to_string; use crate::{AmbiguityError, AmbiguityErrorMisc, AmbiguityKind}; use crate::{BindingError, HasGenericParams, MacroRulesScope, Module, ModuleOrUniformRoot}; -use crate::{Finalize, NameBinding, NameBindingKind, PrivacyError, VisResolutionError}; +use crate::{NameBinding, NameBindingKind, PrivacyError, VisResolutionError}; use crate::{ParentScope, PathResult, ResolutionError, Resolver, Scope, ScopeSet, Segment}; type Res = def::Res; @@ -1076,6 +1076,8 @@ impl<'a> Resolver<'a> { &parent_scope, None, false, + false, + None, ) { let desc = match binding.res() { Res::Def(DefKind::Macro(MacroKind::Bang), _) => { @@ -1422,7 +1424,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { ) -> Option<(Vec, Vec)> { // Replace first ident with `self` and check if that is valid. path[0].ident.name = kw::SelfLower; - let result = self.r.resolve_path(&path, None, parent_scope, Finalize::No); + let result = self.r.maybe_resolve_path(&path, None, parent_scope); debug!("make_missing_self_suggestion: path={:?} result={:?}", path, result); if let PathResult::Module(..) = result { Some((path, Vec::new())) } else { None } } @@ -1441,7 +1443,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { ) -> Option<(Vec, Vec)> { // Replace first ident with `crate` and check if that is valid. path[0].ident.name = kw::Crate; - let result = self.r.resolve_path(&path, None, parent_scope, Finalize::No); + let result = self.r.maybe_resolve_path(&path, None, parent_scope); debug!("make_missing_crate_suggestion: path={:?} result={:?}", path, result); if let PathResult::Module(..) = result { Some(( @@ -1472,7 +1474,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { ) -> Option<(Vec, Vec)> { // Replace first ident with `crate` and check if that is valid. path[0].ident.name = kw::Super; - let result = self.r.resolve_path(&path, None, parent_scope, Finalize::No); + let result = self.r.maybe_resolve_path(&path, None, parent_scope); debug!("make_missing_super_suggestion: path={:?} result={:?}", path, result); if let PathResult::Module(..) = result { Some((path, Vec::new())) } else { None } } @@ -1506,7 +1508,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { for name in extern_crate_names.into_iter() { // Replace first ident with a crate name and check if that is valid. path[0].ident.name = name; - let result = self.r.resolve_path(&path, None, parent_scope, Finalize::No); + let result = self.r.maybe_resolve_path(&path, None, parent_scope); debug!( "make_external_crate_suggestion: name={:?} path={:?} result={:?}", name, path, result diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 3232e66e8238a..e528a50d14754 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -280,6 +280,7 @@ impl<'a> Resolver<'a> { /// /// Invariant: This must only be called during main resolution, not during /// import resolution. + #[tracing::instrument(level = "debug", skip(self, ribs))] crate fn resolve_ident_in_lexical_scope( &mut self, mut ident: Ident, @@ -287,6 +288,7 @@ impl<'a> Resolver<'a> { parent_scope: &ParentScope<'a>, finalize_full: Finalize, ribs: &[Rib<'a>], + unusable_binding: Option<&'a NameBinding<'a>>, ) -> Option> { assert!(ns == TypeNS || ns == ValueNS); let orig_ident = ident; @@ -349,6 +351,8 @@ impl<'a> Resolver<'a> { ns, parent_scope, finalize, + false, + unusable_binding, ); if let Ok(binding) = item { // The ident resolves to an item. @@ -361,6 +365,8 @@ impl<'a> Resolver<'a> { parent_scope, finalize, finalize.is_some(), + false, + unusable_binding, ) .ok() .map(LexicalScopeBinding::Item) @@ -371,6 +377,7 @@ impl<'a> Resolver<'a> { /// expansion and import resolution (perhaps they can be merged in the future). /// The function is used for resolving initial segments of macro paths (e.g., `foo` in /// `foo::bar!(); or `foo!();`) and also for import paths on 2018 edition. + #[tracing::instrument(level = "debug", skip(self, scope_set))] crate fn early_resolve_ident_in_lexical_scope( &mut self, orig_ident: Ident, @@ -378,6 +385,8 @@ impl<'a> Resolver<'a> { parent_scope: &ParentScope<'a>, finalize: Option, force: bool, + last_import_segment: bool, + unusable_binding: Option<&'a NameBinding<'a>>, ) -> Result<&'a NameBinding<'a>, Determinacy> { bitflags::bitflags! { struct Flags: u8 { @@ -497,6 +506,8 @@ impl<'a> Resolver<'a> { ns, parent_scope, finalize, + last_import_segment, + unusable_binding, ); match binding { Ok(binding) => Ok((binding, Flags::MODULE | Flags::MISC_SUGGEST_CRATE)), @@ -518,6 +529,8 @@ impl<'a> Resolver<'a> { adjusted_parent_scope, !matches!(scope_set, ScopeSet::Late(..)), finalize, + last_import_segment, + unusable_binding, ); match binding { Ok(binding) => { @@ -602,6 +615,8 @@ impl<'a> Resolver<'a> { ns, parent_scope, None, + last_import_segment, + unusable_binding, ) { if use_prelude || this.is_builtin_macro(binding.res()) { result = Ok((binding, Flags::MISC_FROM_PRELUDE)); @@ -715,6 +730,19 @@ impl<'a> Resolver<'a> { Err(Determinacy::determined(determinacy == Determinacy::Determined || force)) } + #[tracing::instrument(level = "debug", skip(self))] + crate fn maybe_resolve_ident_in_module( + &mut self, + module: ModuleOrUniformRoot<'a>, + ident: Ident, + ns: Namespace, + parent_scope: &ParentScope<'a>, + ) -> Result<&'a NameBinding<'a>, Determinacy> { + self.resolve_ident_in_module_ext(module, ident, ns, parent_scope, None, false, None) + .map_err(|(determinacy, _)| determinacy) + } + + #[tracing::instrument(level = "debug", skip(self))] crate fn resolve_ident_in_module( &mut self, module: ModuleOrUniformRoot<'a>, @@ -722,11 +750,25 @@ impl<'a> Resolver<'a> { ns: Namespace, parent_scope: &ParentScope<'a>, finalize: Option, + // We are resolving a last import segment during import validation. + last_import_segment: bool, + // This binding should be ignored during in-module resolution, so that we don't get + // "self-confirming" import resolutions during import validation. + unusable_binding: Option<&'a NameBinding<'a>>, ) -> Result<&'a NameBinding<'a>, Determinacy> { - self.resolve_ident_in_module_ext(module, ident, ns, parent_scope, finalize) - .map_err(|(determinacy, _)| determinacy) + self.resolve_ident_in_module_ext( + module, + ident, + ns, + parent_scope, + finalize, + last_import_segment, + unusable_binding, + ) + .map_err(|(determinacy, _)| determinacy) } + #[tracing::instrument(level = "debug", skip(self))] fn resolve_ident_in_module_ext( &mut self, module: ModuleOrUniformRoot<'a>, @@ -734,6 +776,8 @@ impl<'a> Resolver<'a> { ns: Namespace, parent_scope: &ParentScope<'a>, finalize: Option, + last_import_segment: bool, + unusable_binding: Option<&'a NameBinding<'a>>, ) -> Result<&'a NameBinding<'a>, (Determinacy, Weak)> { let tmp_parent_scope; let mut adjusted_parent_scope = parent_scope; @@ -759,9 +803,12 @@ impl<'a> Resolver<'a> { adjusted_parent_scope, false, finalize, + last_import_segment, + unusable_binding, ) } + #[tracing::instrument(level = "debug", skip(self))] fn resolve_ident_in_module_unadjusted( &mut self, module: ModuleOrUniformRoot<'a>, @@ -769,6 +816,8 @@ impl<'a> Resolver<'a> { ns: Namespace, parent_scope: &ParentScope<'a>, finalize: Option, + last_import_segment: bool, + unusable_binding: Option<&'a NameBinding<'a>>, ) -> Result<&'a NameBinding<'a>, Determinacy> { self.resolve_ident_in_module_unadjusted_ext( module, @@ -777,12 +826,15 @@ impl<'a> Resolver<'a> { parent_scope, false, finalize, + last_import_segment, + unusable_binding, ) .map_err(|(determinacy, _)| determinacy) } /// Attempts to resolve `ident` in namespaces `ns` of `module`. /// Invariant: if `finalize` is `Some`, expansion and import resolution must be complete. + #[tracing::instrument(level = "debug", skip(self))] fn resolve_ident_in_module_unadjusted_ext( &mut self, module: ModuleOrUniformRoot<'a>, @@ -791,6 +843,8 @@ impl<'a> Resolver<'a> { parent_scope: &ParentScope<'a>, restricted_shadowing: bool, finalize: Option, + last_import_segment: bool, + unusable_binding: Option<&'a NameBinding<'a>>, ) -> Result<&'a NameBinding<'a>, (Determinacy, Weak)> { let module = match module { ModuleOrUniformRoot::Module(module) => module, @@ -802,6 +856,8 @@ impl<'a> Resolver<'a> { parent_scope, finalize, finalize.is_some(), + last_import_segment, + unusable_binding, ); return binding.map_err(|determinacy| (determinacy, Weak::No)); } @@ -841,6 +897,8 @@ impl<'a> Resolver<'a> { parent_scope, finalize, finalize.is_some(), + last_import_segment, + unusable_binding, ); return binding.map_err(|determinacy| (determinacy, Weak::No)); } @@ -865,7 +923,7 @@ impl<'a> Resolver<'a> { // binding if it exists. What we really want here is having two separate scopes in // a module - one for non-globs and one for globs, but until that's done use this // hack to avoid inconsistent resolution ICEs during import validation. - if let Some(unusable_binding) = self.unusable_binding + if let Some(unusable_binding) = unusable_binding && ptr::eq(binding, unusable_binding) { let Some(shadowed) = resolution.shadowed_glob else { @@ -880,7 +938,7 @@ impl<'a> Resolver<'a> { } if !self.is_accessible_from(binding.vis, parent_scope.module) { - if self.last_import_segment { + if last_import_segment { return Err((Determined, Weak::No)); } else { self.privacy_errors.push(PrivacyError { @@ -912,7 +970,7 @@ impl<'a> Resolver<'a> { } let check_usable = |this: &mut Self, binding: &'a NameBinding<'a>| { - if let Some(unusable_binding) = this.unusable_binding { + if let Some(unusable_binding) = unusable_binding { if ptr::eq(binding, unusable_binding) { return Err((Determined, Weak::No)); } @@ -942,8 +1000,15 @@ impl<'a> Resolver<'a> { let ImportKind::Single { source: ident, .. } = single_import.kind else { unreachable!(); }; - match self.resolve_ident_in_module(module, ident, ns, &single_import.parent_scope, None) - { + match self.resolve_ident_in_module( + module, + ident, + ns, + &single_import.parent_scope, + None, + last_import_segment, + unusable_binding, + ) { Err(Determined) => continue, Ok(binding) if !self.is_accessible_from(binding.vis, single_import.parent_scope.module) => @@ -1018,6 +1083,8 @@ impl<'a> Resolver<'a> { ns, adjusted_parent_scope, None, + last_import_segment, + unusable_binding, ); match result { @@ -1036,6 +1103,7 @@ impl<'a> Resolver<'a> { } /// Validate a local resolution (from ribs). + #[tracing::instrument(level = "debug", skip(self, all_ribs))] fn validate_res_from_ribs( &mut self, rib_index: usize, @@ -1268,14 +1336,26 @@ impl<'a> Resolver<'a> { res } + #[tracing::instrument(level = "debug", skip(self))] + crate fn maybe_resolve_path( + &mut self, + path: &[Segment], + opt_ns: Option, // `None` indicates a module path in import + parent_scope: &ParentScope<'a>, + ) -> PathResult<'a> { + self.resolve_path_with_ribs(path, opt_ns, parent_scope, Finalize::No, None, None) + } + + #[tracing::instrument(level = "debug", skip(self))] crate fn resolve_path( &mut self, path: &[Segment], opt_ns: Option, // `None` indicates a module path in import parent_scope: &ParentScope<'a>, finalize: Finalize, + unusable_binding: Option<&'a NameBinding<'a>>, ) -> PathResult<'a> { - self.resolve_path_with_ribs(path, opt_ns, parent_scope, finalize, None) + self.resolve_path_with_ribs(path, opt_ns, parent_scope, finalize, None, unusable_binding) } crate fn resolve_path_with_ribs( @@ -1285,6 +1365,7 @@ impl<'a> Resolver<'a> { parent_scope: &ParentScope<'a>, finalize_full: Finalize, ribs: Option<&PerNS>>>, + unusable_binding: Option<&'a NameBinding<'a>>, ) -> PathResult<'a> { debug!("resolve_path(path={:?}, opt_ns={:?}, finalize={:?})", path, opt_ns, finalize_full); @@ -1382,7 +1463,15 @@ impl<'a> Resolver<'a> { } let find_binding_in_ns = |this: &mut Self, ns| { let binding = if let Some(module) = module { - this.resolve_ident_in_module(module, ident, ns, parent_scope, finalize) + this.resolve_ident_in_module( + module, + ident, + ns, + parent_scope, + finalize, + false, + unusable_binding, + ) } else if ribs.is_none() || opt_ns.is_none() || opt_ns == Some(MacroNS) { let scopes = ScopeSet::All(ns, opt_ns.is_none()); this.early_resolve_ident_in_lexical_scope( @@ -1391,6 +1480,8 @@ impl<'a> Resolver<'a> { parent_scope, finalize, finalize.is_some(), + false, + unusable_binding, ) } else { match this.resolve_ident_in_lexical_scope( @@ -1399,6 +1490,7 @@ impl<'a> Resolver<'a> { parent_scope, finalize_full, &ribs.unwrap()[ns], + unusable_binding, ) { // we found a locally-imported or available item/module Some(LexicalScopeBinding::Item(binding)) => Ok(binding), @@ -1514,6 +1606,7 @@ impl<'a> Resolver<'a> { parent_scope, Finalize::No, &ribs.unwrap()[ValueNS], + unusable_binding, ) { // Name matches a local variable. For example: // ``` diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 3d199afb13d41..aab0c1f97717c 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -499,7 +499,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { // not define any names while resolving its module path. let orig_vis = import.vis.replace(ty::Visibility::Invisible); let path_res = - self.r.resolve_path(&import.module_path, None, &import.parent_scope, Finalize::No); + self.r.maybe_resolve_path(&import.module_path, None, &import.parent_scope); import.vis.set(orig_vis); match path_res { @@ -539,6 +539,8 @@ impl<'a, 'b> ImportResolver<'a, 'b> { ns, &import.parent_scope, None, + false, + None, ); import.vis.set(orig_vis); source_bindings[ns].set(binding); @@ -584,10 +586,8 @@ impl<'a, 'b> ImportResolver<'a, 'b> { /// consolidate multiple unresolved import errors into a single diagnostic. fn finalize_import(&mut self, import: &'b Import<'b>) -> Option { let orig_vis = import.vis.replace(ty::Visibility::Invisible); - let orig_unusable_binding = match &import.kind { - ImportKind::Single { target_bindings, .. } => { - Some(mem::replace(&mut self.r.unusable_binding, target_bindings[TypeNS].get())) - } + let unusable_binding = match &import.kind { + ImportKind::Single { target_bindings, .. } => target_bindings[TypeNS].get(), _ => None, }; let prev_ambiguity_errors_len = self.r.ambiguity_errors.len(); @@ -596,12 +596,14 @@ impl<'a, 'b> ImportResolver<'a, 'b> { root_span: import.root_span, path_span: import.span, }; - let path_res = - self.r.resolve_path(&import.module_path, None, &import.parent_scope, finalize); + let path_res = self.r.resolve_path( + &import.module_path, + None, + &import.parent_scope, + finalize, + unusable_binding, + ); let no_ambiguity = self.r.ambiguity_errors.len() == prev_ambiguity_errors_len; - if let Some(orig_unusable_binding) = orig_unusable_binding { - self.r.unusable_binding = orig_unusable_binding; - } import.vis.set(orig_vis); if let PathResult::Failed { .. } | PathResult::NonModule(..) = path_res { // Consider erroneous imports used to avoid duplicate diagnostics. @@ -714,18 +716,15 @@ impl<'a, 'b> ImportResolver<'a, 'b> { self.r.per_ns(|this, ns| { if !type_ns_only || ns == TypeNS { let orig_vis = import.vis.replace(ty::Visibility::Invisible); - let orig_unusable_binding = - mem::replace(&mut this.unusable_binding, target_bindings[ns].get()); - let orig_last_import_segment = mem::replace(&mut this.last_import_segment, true); let binding = this.resolve_ident_in_module( module, ident, ns, &import.parent_scope, Some(import.span), + true, + target_bindings[ns].get(), ); - this.last_import_segment = orig_last_import_segment; - this.unusable_binding = orig_unusable_binding; import.vis.set(orig_vis); match binding { @@ -784,6 +783,8 @@ impl<'a, 'b> ImportResolver<'a, 'b> { ns, &import.parent_scope, Some(import.span), + false, + None, ); if binding.is_ok() { all_ns_failed = false; @@ -998,15 +999,14 @@ impl<'a, 'b> ImportResolver<'a, 'b> { return; } - let orig_unusable_binding = - mem::replace(&mut this.unusable_binding, target_bindings[ns].get()); - match this.early_resolve_ident_in_lexical_scope( target, ScopeSet::All(ns, false), &import.parent_scope, None, false, + false, + target_bindings[ns].get(), ) { Ok(other_binding) => { is_redundant[ns] = Some( @@ -1016,8 +1016,6 @@ impl<'a, 'b> ImportResolver<'a, 'b> { } Err(_) => is_redundant[ns] = Some(false), } - - this.unusable_binding = orig_unusable_binding; } }); diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 6fedabc816cff..9084e659ae495 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -8,7 +8,7 @@ use RibKind::*; use crate::{path_names_to_string, BindingError, Finalize, LexicalScopeBinding}; -use crate::{Module, ModuleOrUniformRoot, ParentScope, PathResult}; +use crate::{Module, ModuleOrUniformRoot, NameBinding, ParentScope, PathResult}; use crate::{ResolutionError, Resolver, Segment, UseError}; use rustc_ast::ptr::P; @@ -487,6 +487,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { self_ty, TypeNS, Finalize::SimplePath(ty.id, ty.span), + None, ) .map_or(Res::Err, |d| d.res()); self.r.record_partial_res(ty.id, PartialRes::new(res)); @@ -676,12 +677,8 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { // checking. if path.segments.len() == 1 && path.segments[0].args.is_none() { let mut check_ns = |ns| { - self.resolve_ident_in_lexical_scope( - path.segments[0].ident, - ns, - Finalize::No, - ) - .is_some() + self.maybe_resolve_ident_in_lexical_scope(path.segments[0].ident, ns) + .is_some() }; if !check_ns(TypeNS) && check_ns(ValueNS) { // This must be equivalent to `visit_anon_const`, but we cannot call it @@ -750,11 +747,27 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { } } + fn maybe_resolve_ident_in_lexical_scope( + &mut self, + ident: Ident, + ns: Namespace, + ) -> Option> { + self.r.resolve_ident_in_lexical_scope( + ident, + ns, + &self.parent_scope, + Finalize::No, + &self.ribs[ns], + None, + ) + } + fn resolve_ident_in_lexical_scope( &mut self, ident: Ident, ns: Namespace, finalize: Finalize, + unusable_binding: Option<&'a NameBinding<'a>>, ) -> Option> { self.r.resolve_ident_in_lexical_scope( ident, @@ -762,6 +775,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { &self.parent_scope, finalize, &self.ribs[ns], + unusable_binding, ) } @@ -771,7 +785,14 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { opt_ns: Option, // `None` indicates a module path in import finalize: Finalize, ) -> PathResult<'a> { - self.r.resolve_path_with_ribs(path, opt_ns, &self.parent_scope, finalize, Some(&self.ribs)) + self.r.resolve_path_with_ribs( + path, + opt_ns, + &self.parent_scope, + finalize, + Some(&self.ribs), + None, + ) } // AST resolution @@ -934,19 +955,16 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { }; for &ns in nss { - match self.resolve_ident_in_lexical_scope(ident, ns, Finalize::No) { + match self.maybe_resolve_ident_in_lexical_scope(ident, ns) { Some(LexicalScopeBinding::Res(..)) => { report_error(self, ns); } Some(LexicalScopeBinding::Item(binding)) => { - let orig_unusable_binding = - replace(&mut self.r.unusable_binding, Some(binding)); - if let Some(LexicalScopeBinding::Res(..)) = - self.resolve_ident_in_lexical_scope(ident, ns, Finalize::No) + if let Some(LexicalScopeBinding::Res(..)) = self + .resolve_ident_in_lexical_scope(ident, ns, Finalize::No, Some(binding)) { report_error(self, ns); } - self.r.unusable_binding = orig_unusable_binding; } None => {} } @@ -1802,7 +1820,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // also be interpreted as a path to e.g. a constant, variant, etc. let is_syntactic_ambiguity = !has_sub && bm == BindingMode::ByValue(Mutability::Not); - let ls_binding = self.resolve_ident_in_lexical_scope(ident, ValueNS, Finalize::No)?; + let ls_binding = self.maybe_resolve_ident_in_lexical_scope(ident, ValueNS)?; let (res, binding) = match ls_binding { LexicalScopeBinding::Item(binding) if is_syntactic_ambiguity && binding.is_ambiguity() => @@ -2071,17 +2089,14 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { } fn self_type_is_available(&mut self) -> bool { - let binding = self.resolve_ident_in_lexical_scope( - Ident::with_dummy_span(kw::SelfUpper), - TypeNS, - Finalize::No, - ); + let binding = self + .maybe_resolve_ident_in_lexical_scope(Ident::with_dummy_span(kw::SelfUpper), TypeNS); if let Some(LexicalScopeBinding::Res(res)) = binding { res != Res::Err } else { false } } fn self_value_is_available(&mut self, self_span: Span) -> bool { let ident = Ident::new(kw::SelfLower, self_span); - let binding = self.resolve_ident_in_lexical_scope(ident, ValueNS, Finalize::No); + let binding = self.maybe_resolve_ident_in_lexical_scope(ident, ValueNS); if let Some(LexicalScopeBinding::Res(res)) = binding { res != Res::Err } else { false } } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 1e943f0e44abd..eae9e846c8b77 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1271,12 +1271,11 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { // Look for associated items in the current trait. if let Some((module, _)) = self.current_trait_ref { - if let Ok(binding) = self.r.resolve_ident_in_module( + if let Ok(binding) = self.r.maybe_resolve_ident_in_module( ModuleOrUniformRoot::Module(module), ident, ns, &self.parent_scope, - None, ) { let res = binding.res(); if filter_fn(res) { diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index f4a650e8d2678..c0875bf547a00 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -925,13 +925,6 @@ pub struct Resolver<'a> { /// All non-determined imports. indeterminate_imports: Vec<&'a Import<'a>>, - /// FIXME: Refactor things so that these fields are passed through arguments and not resolver. - /// We are resolving a last import segment during import validation. - last_import_segment: bool, - /// This binding should be ignored during in-module resolution, so that we don't get - /// "self-confirming" import resolutions during import validation. - unusable_binding: Option<&'a NameBinding<'a>>, - // Spans for local variables found during pattern resolution. // Used for suggestions during error reporting. pat_span_map: NodeMap, @@ -1339,9 +1332,6 @@ impl<'a> Resolver<'a> { determined_imports: Vec::new(), indeterminate_imports: Vec::new(), - last_import_segment: false, - unusable_binding: None, - pat_span_map: Default::default(), partial_res_map: Default::default(), import_res_map: Default::default(), @@ -2294,12 +2284,7 @@ impl<'a> Resolver<'a> { } let module = self.expect_module(module_id); - match self.resolve_path( - &segments, - Some(ns), - &ParentScope::module(module, self), - Finalize::No, - ) { + match self.maybe_resolve_path(&segments, Some(ns), &ParentScope::module(module, self)) { PathResult::Module(ModuleOrUniformRoot::Module(module)) => Some(module.res().unwrap()), PathResult::NonModule(path_res) if path_res.unresolved_segments() == 0 => { Some(path_res.base_res()) @@ -2389,12 +2374,11 @@ impl<'a> Resolver<'a> { let ident = Ident::with_dummy_span(sym::main); let parent_scope = &ParentScope::module(module, self); - let Ok(name_binding) = self.resolve_ident_in_module( + let Ok(name_binding) = self.maybe_resolve_ident_in_module( ModuleOrUniformRoot::Module(module), ident, ValueNS, parent_scope, - None ) else { return; }; diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 71795ef1633f2..01f0b11f1ac3b 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -412,7 +412,7 @@ impl<'a> ResolverExpand for Resolver<'a> { let mut indeterminate = false; for ns in [TypeNS, ValueNS, MacroNS].iter().copied() { - match self.resolve_path(path, Some(ns), &parent_scope, Finalize::No) { + match self.maybe_resolve_path(path, Some(ns), &parent_scope) { PathResult::Module(ModuleOrUniformRoot::Module(_)) => return Ok(true), PathResult::NonModule(partial_res) if partial_res.unresolved_segments() == 0 => { return Ok(true); @@ -572,7 +572,7 @@ impl<'a> Resolver<'a> { } let res = if path.len() > 1 { - let res = match self.resolve_path(&path, Some(MacroNS), parent_scope, Finalize::No) { + let res = match self.maybe_resolve_path(&path, Some(MacroNS), parent_scope) { PathResult::NonModule(path_res) if path_res.unresolved_segments() == 0 => { Ok(path_res.base_res()) } @@ -604,6 +604,8 @@ impl<'a> Resolver<'a> { parent_scope, None, force, + false, + None, ); if let Err(Determinacy::Undetermined) = binding { return Err(Determinacy::Undetermined); @@ -672,6 +674,7 @@ impl<'a> Resolver<'a> { Some(MacroNS), &parent_scope, Finalize::SimplePath(ast::CRATE_NODE_ID, path_span), + None, ) { PathResult::NonModule(path_res) if path_res.unresolved_segments() == 0 => { let res = path_res.base_res(); @@ -707,6 +710,8 @@ impl<'a> Resolver<'a> { &parent_scope, Some(ident.span), true, + false, + None, ) { Ok(binding) => { let initial_res = initial_binding.map(|initial_binding| { @@ -748,6 +753,8 @@ impl<'a> Resolver<'a> { &parent_scope, Some(ident.span), true, + false, + None, ); } } From 55ca03c0ac840d6f4f30f61bc381b02ca095b899 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Fri, 8 Apr 2022 22:51:13 +0200 Subject: [PATCH 14/23] Insert error after checking for binding usability. --- compiler/rustc_resolve/src/ident.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index e528a50d14754..776ad7047320d 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -913,12 +913,6 @@ impl<'a> Resolver<'a> { return Err((Determined, Weak::No)); }; - if !restricted_shadowing && binding.expansion != LocalExpnId::ROOT { - if let NameBindingKind::Res(_, true) = binding.kind { - self.macro_expanded_macro_export_errors.insert((path_span, binding.span)); - } - } - // If the primary binding is unusable, search further and return the shadowed glob // binding if it exists. What we really want here is having two separate scopes in // a module - one for non-globs and one for globs, but until that's done use this @@ -965,6 +959,12 @@ impl<'a> Resolver<'a> { }); } + if !restricted_shadowing && binding.expansion != LocalExpnId::ROOT { + if let NameBindingKind::Res(_, true) = binding.kind { + self.macro_expanded_macro_export_errors.insert((path_span, binding.span)); + } + } + self.record_use(ident, binding, restricted_shadowing); return Ok(binding); } From 3ee6f6e28a8d843df28b29bb70dd3dbb2267e39c Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Fri, 8 Apr 2022 22:51:27 +0200 Subject: [PATCH 15/23] Do not record Res when builing a suggestion. --- compiler/rustc_resolve/src/ident.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 776ad7047320d..2507bc30019e4 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -1459,7 +1459,7 @@ impl<'a> Resolver<'a> { enum FindBindingResult<'a> { Binding(Result<&'a NameBinding<'a>, Determinacy>), - PathResult(PathResult<'a>), + Res(Res), } let find_binding_in_ns = |this: &mut Self, ns| { let binding = if let Some(module) = module { @@ -1498,10 +1498,7 @@ impl<'a> Resolver<'a> { Some(LexicalScopeBinding::Res(res)) if opt_ns == Some(TypeNS) || opt_ns == Some(ValueNS) => { - record_segment_res(this, res); - return FindBindingResult::PathResult(PathResult::NonModule( - PartialRes::with_unresolved_segments(res, path.len() - 1), - )); + return FindBindingResult::Res(res); } _ => Err(Determinacy::determined(finalize.is_some())), } @@ -1509,7 +1506,13 @@ impl<'a> Resolver<'a> { FindBindingResult::Binding(binding) }; let binding = match find_binding_in_ns(self, ns) { - FindBindingResult::PathResult(x) => return x, + FindBindingResult::Res(res) => { + record_segment_res(self, res); + return PathResult::NonModule(PartialRes::with_unresolved_segments( + res, + path.len() - 1, + )); + } FindBindingResult::Binding(binding) => binding, }; match binding { From 886613c91627606c885733d5f44da88597004a0c Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Fri, 8 Apr 2022 22:51:44 +0200 Subject: [PATCH 16/23] Make the logic more explicit with let chains. --- compiler/rustc_resolve/src/ident.rs | 34 ++++++++++++++--------------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 2507bc30019e4..06b886aeca058 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -1472,36 +1472,34 @@ impl<'a> Resolver<'a> { false, unusable_binding, ) - } else if ribs.is_none() || opt_ns.is_none() || opt_ns == Some(MacroNS) { - let scopes = ScopeSet::All(ns, opt_ns.is_none()); - this.early_resolve_ident_in_lexical_scope( - ident, - scopes, - parent_scope, - finalize, - finalize.is_some(), - false, - unusable_binding, - ) - } else { + } else if let Some(ribs) = ribs + && let Some(TypeNS | ValueNS) = opt_ns + { match this.resolve_ident_in_lexical_scope( ident, ns, parent_scope, finalize_full, - &ribs.unwrap()[ns], + &ribs[ns], unusable_binding, ) { // we found a locally-imported or available item/module Some(LexicalScopeBinding::Item(binding)) => Ok(binding), // we found a local variable or type param - Some(LexicalScopeBinding::Res(res)) - if opt_ns == Some(TypeNS) || opt_ns == Some(ValueNS) => - { - return FindBindingResult::Res(res); - } + Some(LexicalScopeBinding::Res(res)) => return FindBindingResult::Res(res), _ => Err(Determinacy::determined(finalize.is_some())), } + } else { + let scopes = ScopeSet::All(ns, opt_ns.is_none()); + this.early_resolve_ident_in_lexical_scope( + ident, + scopes, + parent_scope, + finalize, + finalize.is_some(), + false, + unusable_binding, + ) }; FindBindingResult::Binding(binding) }; From d1c1bbe5f3a0b731450bb90aca15997293df397d Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Fri, 8 Apr 2022 22:51:55 +0200 Subject: [PATCH 17/23] Move path resolution error to rustc_resolve::diagnostics. --- compiler/rustc_resolve/src/diagnostics.rs | 198 +++++++++++++++++++++- compiler/rustc_resolve/src/ident.rs | 173 ++----------------- 2 files changed, 207 insertions(+), 164 deletions(-) diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index fdcc045efd815..bf4611f3870bb 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -8,12 +8,13 @@ use rustc_errors::{ }; use rustc_feature::BUILTIN_ATTRIBUTES; use rustc_hir::def::Namespace::{self, *}; -use rustc_hir::def::{self, CtorKind, CtorOf, DefKind, NonMacroAttrKind}; +use rustc_hir::def::{self, CtorKind, CtorOf, DefKind, NonMacroAttrKind, PerNS}; use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_hir::PrimTy; use rustc_middle::bug; use rustc_middle::ty::DefIdTree; use rustc_session::Session; +use rustc_span::edition::Edition; use rustc_span::hygiene::MacroKind; use rustc_span::lev_distance::find_best_match_for_name; use rustc_span::source_map::SourceMap; @@ -22,10 +23,11 @@ use rustc_span::{BytePos, Span}; use tracing::debug; use crate::imports::{Import, ImportKind, ImportResolver}; +use crate::late::Rib; use crate::path_names_to_string; -use crate::{AmbiguityError, AmbiguityErrorMisc, AmbiguityKind}; +use crate::{AmbiguityError, AmbiguityErrorMisc, AmbiguityKind, Finalize}; use crate::{BindingError, HasGenericParams, MacroRulesScope, Module, ModuleOrUniformRoot}; -use crate::{NameBinding, NameBindingKind, PrivacyError, VisResolutionError}; +use crate::{LexicalScopeBinding, NameBinding, NameBindingKind, PrivacyError, VisResolutionError}; use crate::{ParentScope, PathResult, ResolutionError, Resolver, Scope, ScopeSet, Segment}; type Res = def::Res; @@ -1377,6 +1379,196 @@ impl<'a> Resolver<'a> { sugg => sugg, } } + + crate fn report_path_resolution_error( + &mut self, + path: &[Segment], + opt_ns: Option, // `None` indicates a module path in import + parent_scope: &ParentScope<'a>, + finalize_full: Finalize, + ribs: Option<&PerNS>>>, + unusable_binding: Option<&'a NameBinding<'a>>, + module: Option>, + i: usize, + ident: Ident, + ) -> (String, Option) { + let finalize = finalize_full.path_span(); + let is_last = i == path.len() - 1; + let ns = if is_last { opt_ns.unwrap_or(TypeNS) } else { TypeNS }; + let module_res = match module { + Some(ModuleOrUniformRoot::Module(module)) => module.res(), + _ => None, + }; + if module_res == self.graph_root.res() { + let is_mod = |res| matches!(res, Res::Def(DefKind::Mod, _)); + let mut candidates = self.lookup_import_candidates(ident, TypeNS, parent_scope, is_mod); + candidates + .sort_by_cached_key(|c| (c.path.segments.len(), pprust::path_to_string(&c.path))); + if let Some(candidate) = candidates.get(0) { + ( + String::from("unresolved import"), + Some(( + vec![(ident.span, pprust::path_to_string(&candidate.path))], + String::from("a similar path exists"), + Applicability::MaybeIncorrect, + )), + ) + } else if self.session.edition() == Edition::Edition2015 { + (format!("maybe a missing crate `{}`?", ident), None) + } else { + (format!("could not find `{}` in the crate root", ident), None) + } + } else if i == 0 { + if ident.name.as_str().chars().next().map_or(false, |c| c.is_ascii_uppercase()) { + // Check whether the name refers to an item in the value namespace. + let suggestion = if ribs.is_some() { + let match_span = match self.resolve_ident_in_lexical_scope( + ident, + ValueNS, + parent_scope, + Finalize::No, + &ribs.unwrap()[ValueNS], + unusable_binding, + ) { + // Name matches a local variable. For example: + // ``` + // fn f() { + // let Foo: &str = ""; + // println!("{}", Foo::Bar); // Name refers to local + // // variable `Foo`. + // } + // ``` + Some(LexicalScopeBinding::Res(Res::Local(id))) => { + Some(*self.pat_span_map.get(&id).unwrap()) + } + + // Name matches item from a local name binding + // created by `use` declaration. For example: + // ``` + // pub Foo: &str = ""; + // + // mod submod { + // use super::Foo; + // println!("{}", Foo::Bar); // Name refers to local + // // binding `Foo`. + // } + // ``` + Some(LexicalScopeBinding::Item(name_binding)) => Some(name_binding.span), + _ => None, + }; + + if let Some(span) = match_span { + Some(( + vec![(span, String::from(""))], + format!("`{}` is defined here, but is not a type", ident), + Applicability::MaybeIncorrect, + )) + } else { + None + } + } else { + None + }; + + (format!("use of undeclared type `{}`", ident), suggestion) + } else { + ( + format!("use of undeclared crate or module `{}`", ident), + if ident.name == sym::alloc { + Some(( + vec![], + String::from("add `extern crate alloc` to use the `alloc` crate"), + Applicability::MaybeIncorrect, + )) + } else { + self.find_similarly_named_module_or_crate(ident.name, &parent_scope.module) + .map(|sugg| { + ( + vec![(ident.span, sugg.to_string())], + String::from("there is a crate or module with a similar name"), + Applicability::MaybeIncorrect, + ) + }) + }, + ) + } + } else { + let parent = path[i - 1].ident.name; + let parent = match parent { + // ::foo is mounted at the crate root for 2015, and is the extern + // prelude for 2018+ + kw::PathRoot if self.session.edition() > Edition::Edition2015 => { + "the list of imported crates".to_owned() + } + kw::PathRoot | kw::Crate => "the crate root".to_owned(), + _ => { + format!("`{}`", parent) + } + }; + + let mut msg = format!("could not find `{}` in {}", ident, parent); + if ns == TypeNS || ns == ValueNS { + let ns_to_try = if ns == TypeNS { ValueNS } else { TypeNS }; + let binding = if let Some(module) = module { + self.resolve_ident_in_module( + module, + ident, + ns_to_try, + parent_scope, + finalize, + false, + unusable_binding, + ).ok() + } else if let Some(ribs) = ribs + && let Some(TypeNS | ValueNS) = opt_ns + { + match self.resolve_ident_in_lexical_scope( + ident, + ns_to_try, + parent_scope, + finalize_full, + &ribs[ns_to_try], + unusable_binding, + ) { + // we found a locally-imported or available item/module + Some(LexicalScopeBinding::Item(binding)) => Some(binding), + _ => None, + } + } else { + let scopes = ScopeSet::All(ns_to_try, opt_ns.is_none()); + self.early_resolve_ident_in_lexical_scope( + ident, + scopes, + parent_scope, + finalize, + finalize.is_some(), + false, + unusable_binding, + ).ok() + }; + if let Some(binding) = binding { + let mut found = |what| { + msg = format!( + "expected {}, found {} `{}` in {}", + ns.descr(), + what, + ident, + parent + ) + }; + if binding.module().is_some() { + found("module") + } else { + match binding.res() { + Res::Def(kind, id) => found(kind.descr(id)), + _ => found(ns_to_try.descr()), + } + } + }; + } + (msg, None) + } + } } impl<'a, 'b> ImportResolver<'a, 'b> { diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 06b886aeca058..7f6832a4ec427 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -1,6 +1,4 @@ use rustc_ast::{self as ast, NodeId}; -use rustc_ast_pretty::pprust; -use rustc_errors::Applicability; use rustc_feature::is_builtin_attr_name; use rustc_hir::def::{DefKind, Namespace, NonMacroAttrKind, PartialRes, PerNS}; use rustc_hir::PrimTy; @@ -10,7 +8,7 @@ use rustc_session::lint::builtin::PROC_MACRO_DERIVE_RESOLUTION_FALLBACK; use rustc_session::lint::BuiltinLintDiagnostics; use rustc_span::edition::Edition; use rustc_span::hygiene::{ExpnId, ExpnKind, LocalExpnId, MacroKind, SyntaxContext}; -use rustc_span::symbol::{kw, sym, Ident}; +use rustc_span::symbol::{kw, Ident}; use rustc_span::{Span, DUMMY_SP}; use std::ptr; @@ -1566,164 +1564,17 @@ impl<'a> Resolver<'a> { } return PathResult::failed(ident.span, is_last, finalize.is_some(), || { - let module_res = match module { - Some(ModuleOrUniformRoot::Module(module)) => module.res(), - _ => None, - }; - if module_res == self.graph_root.res() { - let is_mod = |res| matches!(res, Res::Def(DefKind::Mod, _)); - let mut candidates = - self.lookup_import_candidates(ident, TypeNS, parent_scope, is_mod); - candidates.sort_by_cached_key(|c| { - (c.path.segments.len(), pprust::path_to_string(&c.path)) - }); - if let Some(candidate) = candidates.get(0) { - ( - String::from("unresolved import"), - Some(( - vec![(ident.span, pprust::path_to_string(&candidate.path))], - String::from("a similar path exists"), - Applicability::MaybeIncorrect, - )), - ) - } else if self.session.edition() == Edition::Edition2015 { - (format!("maybe a missing crate `{}`?", ident), None) - } else { - (format!("could not find `{}` in the crate root", ident), None) - } - } else if i == 0 { - if ident - .name - .as_str() - .chars() - .next() - .map_or(false, |c| c.is_ascii_uppercase()) - { - // Check whether the name refers to an item in the value namespace. - let suggestion = if ribs.is_some() { - let match_span = match self.resolve_ident_in_lexical_scope( - ident, - ValueNS, - parent_scope, - Finalize::No, - &ribs.unwrap()[ValueNS], - unusable_binding, - ) { - // Name matches a local variable. For example: - // ``` - // fn f() { - // let Foo: &str = ""; - // println!("{}", Foo::Bar); // Name refers to local - // // variable `Foo`. - // } - // ``` - Some(LexicalScopeBinding::Res(Res::Local(id))) => { - Some(*self.pat_span_map.get(&id).unwrap()) - } - - // Name matches item from a local name binding - // created by `use` declaration. For example: - // ``` - // pub Foo: &str = ""; - // - // mod submod { - // use super::Foo; - // println!("{}", Foo::Bar); // Name refers to local - // // binding `Foo`. - // } - // ``` - Some(LexicalScopeBinding::Item(name_binding)) => { - Some(name_binding.span) - } - _ => None, - }; - - if let Some(span) = match_span { - Some(( - vec![(span, String::from(""))], - format!( - "`{}` is defined here, but is not a type", - ident - ), - Applicability::MaybeIncorrect, - )) - } else { - None - } - } else { - None - }; - - (format!("use of undeclared type `{}`", ident), suggestion) - } else { - ( - format!("use of undeclared crate or module `{}`", ident), - if ident.name == sym::alloc { - Some(( - vec![], - String::from( - "add `extern crate alloc` to use the `alloc` crate", - ), - Applicability::MaybeIncorrect, - )) - } else { - self.find_similarly_named_module_or_crate( - ident.name, - &parent_scope.module, - ) - .map(|sugg| { - ( - vec![(ident.span, sugg.to_string())], - String::from( - "there is a crate or module with a similar name", - ), - Applicability::MaybeIncorrect, - ) - }) - }, - ) - } - } else { - let parent = path[i - 1].ident.name; - let parent = match parent { - // ::foo is mounted at the crate root for 2015, and is the extern - // prelude for 2018+ - kw::PathRoot if self.session.edition() > Edition::Edition2015 => { - "the list of imported crates".to_owned() - } - kw::PathRoot | kw::Crate => "the crate root".to_owned(), - _ => { - format!("`{}`", parent) - } - }; - - let mut msg = format!("could not find `{}` in {}", ident, parent); - if ns == TypeNS || ns == ValueNS { - let ns_to_try = if ns == TypeNS { ValueNS } else { TypeNS }; - if let FindBindingResult::Binding(Ok(binding)) = - find_binding_in_ns(self, ns_to_try) - { - let mut found = |what| { - msg = format!( - "expected {}, found {} `{}` in {}", - ns.descr(), - what, - ident, - parent - ) - }; - if binding.module().is_some() { - found("module") - } else { - match binding.res() { - Res::Def(kind, id) => found(kind.descr(id)), - _ => found(ns_to_try.descr()), - } - } - }; - } - (msg, None) - } + self.report_path_resolution_error( + path, + opt_ns, + parent_scope, + finalize_full, + ribs, + unusable_binding, + module, + i, + ident, + ) }); } } From 944d852afe6c27b6207984b907d902565cf550d6 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Fri, 8 Apr 2022 22:52:07 +0200 Subject: [PATCH 18/23] Simplify error reporting. --- compiler/rustc_resolve/src/diagnostics.rs | 160 ++++++++++------------ compiler/rustc_resolve/src/ident.rs | 1 - 2 files changed, 76 insertions(+), 85 deletions(-) diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index bf4611f3870bb..6684c463da0be 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1385,14 +1385,12 @@ impl<'a> Resolver<'a> { path: &[Segment], opt_ns: Option, // `None` indicates a module path in import parent_scope: &ParentScope<'a>, - finalize_full: Finalize, ribs: Option<&PerNS>>>, unusable_binding: Option<&'a NameBinding<'a>>, module: Option>, i: usize, ident: Ident, ) -> (String, Option) { - let finalize = finalize_full.path_span(); let is_last = i == path.len() - 1; let ns = if is_last { opt_ns.unwrap_or(TypeNS) } else { TypeNS }; let module_res = match module { @@ -1418,81 +1416,7 @@ impl<'a> Resolver<'a> { } else { (format!("could not find `{}` in the crate root", ident), None) } - } else if i == 0 { - if ident.name.as_str().chars().next().map_or(false, |c| c.is_ascii_uppercase()) { - // Check whether the name refers to an item in the value namespace. - let suggestion = if ribs.is_some() { - let match_span = match self.resolve_ident_in_lexical_scope( - ident, - ValueNS, - parent_scope, - Finalize::No, - &ribs.unwrap()[ValueNS], - unusable_binding, - ) { - // Name matches a local variable. For example: - // ``` - // fn f() { - // let Foo: &str = ""; - // println!("{}", Foo::Bar); // Name refers to local - // // variable `Foo`. - // } - // ``` - Some(LexicalScopeBinding::Res(Res::Local(id))) => { - Some(*self.pat_span_map.get(&id).unwrap()) - } - - // Name matches item from a local name binding - // created by `use` declaration. For example: - // ``` - // pub Foo: &str = ""; - // - // mod submod { - // use super::Foo; - // println!("{}", Foo::Bar); // Name refers to local - // // binding `Foo`. - // } - // ``` - Some(LexicalScopeBinding::Item(name_binding)) => Some(name_binding.span), - _ => None, - }; - - if let Some(span) = match_span { - Some(( - vec![(span, String::from(""))], - format!("`{}` is defined here, but is not a type", ident), - Applicability::MaybeIncorrect, - )) - } else { - None - } - } else { - None - }; - - (format!("use of undeclared type `{}`", ident), suggestion) - } else { - ( - format!("use of undeclared crate or module `{}`", ident), - if ident.name == sym::alloc { - Some(( - vec![], - String::from("add `extern crate alloc` to use the `alloc` crate"), - Applicability::MaybeIncorrect, - )) - } else { - self.find_similarly_named_module_or_crate(ident.name, &parent_scope.module) - .map(|sugg| { - ( - vec![(ident.span, sugg.to_string())], - String::from("there is a crate or module with a similar name"), - Applicability::MaybeIncorrect, - ) - }) - }, - ) - } - } else { + } else if i > 0 { let parent = path[i - 1].ident.name; let parent = match parent { // ::foo is mounted at the crate root for 2015, and is the extern @@ -1501,9 +1425,7 @@ impl<'a> Resolver<'a> { "the list of imported crates".to_owned() } kw::PathRoot | kw::Crate => "the crate root".to_owned(), - _ => { - format!("`{}`", parent) - } + _ => format!("`{}`", parent), }; let mut msg = format!("could not find `{}` in {}", ident, parent); @@ -1515,7 +1437,7 @@ impl<'a> Resolver<'a> { ident, ns_to_try, parent_scope, - finalize, + None, false, unusable_binding, ).ok() @@ -1526,7 +1448,7 @@ impl<'a> Resolver<'a> { ident, ns_to_try, parent_scope, - finalize_full, + Finalize::No, &ribs[ns_to_try], unusable_binding, ) { @@ -1540,8 +1462,8 @@ impl<'a> Resolver<'a> { ident, scopes, parent_scope, - finalize, - finalize.is_some(), + None, + false, false, unusable_binding, ).ok() @@ -1567,6 +1489,76 @@ impl<'a> Resolver<'a> { }; } (msg, None) + } else if ident.name.as_str().chars().next().map_or(false, |c| c.is_ascii_uppercase()) { + // Check whether the name refers to an item in the value namespace. + let binding = if let Some(ribs) = ribs { + self.resolve_ident_in_lexical_scope( + ident, + ValueNS, + parent_scope, + Finalize::No, + &ribs[ValueNS], + unusable_binding, + ) + } else { + None + }; + let match_span = match binding { + // Name matches a local variable. For example: + // ``` + // fn f() { + // let Foo: &str = ""; + // println!("{}", Foo::Bar); // Name refers to local + // // variable `Foo`. + // } + // ``` + Some(LexicalScopeBinding::Res(Res::Local(id))) => { + Some(*self.pat_span_map.get(&id).unwrap()) + } + // Name matches item from a local name binding + // created by `use` declaration. For example: + // ``` + // pub Foo: &str = ""; + // + // mod submod { + // use super::Foo; + // println!("{}", Foo::Bar); // Name refers to local + // // binding `Foo`. + // } + // ``` + Some(LexicalScopeBinding::Item(name_binding)) => Some(name_binding.span), + _ => None, + }; + let suggestion = if let Some(span) = match_span { + Some(( + vec![(span, String::from(""))], + format!("`{}` is defined here, but is not a type", ident), + Applicability::MaybeIncorrect, + )) + } else { + None + }; + + (format!("use of undeclared type `{}`", ident), suggestion) + } else { + let suggestion = if ident.name == sym::alloc { + Some(( + vec![], + String::from("add `extern crate alloc` to use the `alloc` crate"), + Applicability::MaybeIncorrect, + )) + } else { + self.find_similarly_named_module_or_crate(ident.name, &parent_scope.module).map( + |sugg| { + ( + vec![(ident.span, sugg.to_string())], + String::from("there is a crate or module with a similar name"), + Applicability::MaybeIncorrect, + ) + }, + ) + }; + (format!("use of undeclared crate or module `{}`", ident), suggestion) } } } diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 7f6832a4ec427..0260386cb735f 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -1568,7 +1568,6 @@ impl<'a> Resolver<'a> { path, opt_ns, parent_scope, - finalize_full, ribs, unusable_binding, module, From abbd0b85b26a539e16cc6334084bde1974728473 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Fri, 8 Apr 2022 22:52:18 +0200 Subject: [PATCH 19/23] Move diagnostic methods to the dedicated module. --- compiler/rustc_resolve/src/diagnostics.rs | 480 +++++++++++++++++++++- compiler/rustc_resolve/src/lib.rs | 469 +-------------------- 2 files changed, 472 insertions(+), 477 deletions(-) diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 6684c463da0be..1a03a65c18dcf 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1,11 +1,12 @@ use std::ptr; -use rustc_ast::{self as ast, Path}; +use rustc_ast::ptr::P; +use rustc_ast::visit::{self, Visitor}; +use rustc_ast::{self as ast, Crate, ItemKind, ModKind, NodeId, Path, CRATE_NODE_ID}; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; -use rustc_errors::{ - struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, -}; +use rustc_errors::struct_span_err; +use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan}; use rustc_feature::BUILTIN_ATTRIBUTES; use rustc_hir::def::Namespace::{self, *}; use rustc_hir::def::{self, CtorKind, CtorOf, DefKind, NonMacroAttrKind, PerNS}; @@ -13,6 +14,9 @@ use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_hir::PrimTy; use rustc_middle::bug; use rustc_middle::ty::DefIdTree; +use rustc_session::lint::builtin::ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE; +use rustc_session::lint::builtin::MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS; +use rustc_session::lint::BuiltinLintDiagnostics; use rustc_session::Session; use rustc_span::edition::Edition; use rustc_span::hygiene::MacroKind; @@ -25,10 +29,11 @@ use tracing::debug; use crate::imports::{Import, ImportKind, ImportResolver}; use crate::late::Rib; use crate::path_names_to_string; -use crate::{AmbiguityError, AmbiguityErrorMisc, AmbiguityKind, Finalize}; -use crate::{BindingError, HasGenericParams, MacroRulesScope, Module, ModuleOrUniformRoot}; +use crate::{AmbiguityError, AmbiguityErrorMisc, AmbiguityKind, BindingError, Finalize}; +use crate::{HasGenericParams, MacroRulesScope, Module, ModuleKind, ModuleOrUniformRoot}; use crate::{LexicalScopeBinding, NameBinding, NameBindingKind, PrivacyError, VisResolutionError}; -use crate::{ParentScope, PathResult, ResolutionError, Resolver, Scope, ScopeSet, Segment}; +use crate::{ParentScope, PathResult, ResolutionError, Resolver, Scope, ScopeSet}; +use crate::{Segment, UseError}; type Res = def::Res; @@ -84,6 +89,390 @@ fn reduce_impl_span_to_impl_keyword(sm: &SourceMap, impl_span: Span) -> Span { } impl<'a> Resolver<'a> { + crate fn report_errors(&mut self, krate: &Crate) { + self.report_with_use_injections(krate); + + for &(span_use, span_def) in &self.macro_expanded_macro_export_errors { + let msg = "macro-expanded `macro_export` macros from the current crate \ + cannot be referred to by absolute paths"; + self.lint_buffer.buffer_lint_with_diagnostic( + MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS, + CRATE_NODE_ID, + span_use, + msg, + BuiltinLintDiagnostics::MacroExpandedMacroExportsAccessedByAbsolutePaths(span_def), + ); + } + + for ambiguity_error in &self.ambiguity_errors { + self.report_ambiguity_error(ambiguity_error); + } + + let mut reported_spans = FxHashSet::default(); + for error in &self.privacy_errors { + if reported_spans.insert(error.dedup_span) { + self.report_privacy_error(error); + } + } + } + + fn report_with_use_injections(&mut self, krate: &Crate) { + for UseError { mut err, candidates, def_id, instead, suggestion } in + self.use_injections.drain(..) + { + let (span, found_use) = if let Some(def_id) = def_id.as_local() { + UsePlacementFinder::check(krate, self.def_id_to_node_id[def_id]) + } else { + (None, false) + }; + if !candidates.is_empty() { + show_candidates( + &self.definitions, + self.session, + &mut err, + span, + &candidates, + instead, + found_use, + ); + } else if let Some((span, msg, sugg, appl)) = suggestion { + err.span_suggestion(span, msg, sugg, appl); + } + err.emit(); + } + } + + crate fn report_conflict<'b>( + &mut self, + parent: Module<'_>, + ident: Ident, + ns: Namespace, + new_binding: &NameBinding<'b>, + old_binding: &NameBinding<'b>, + ) { + // Error on the second of two conflicting names + if old_binding.span.lo() > new_binding.span.lo() { + return self.report_conflict(parent, ident, ns, old_binding, new_binding); + } + + let container = match parent.kind { + ModuleKind::Def(kind, _, _) => kind.descr(parent.def_id()), + ModuleKind::Block(..) => "block", + }; + + let old_noun = match old_binding.is_import() { + true => "import", + false => "definition", + }; + + let new_participle = match new_binding.is_import() { + true => "imported", + false => "defined", + }; + + let (name, span) = + (ident.name, self.session.source_map().guess_head_span(new_binding.span)); + + if let Some(s) = self.name_already_seen.get(&name) { + if s == &span { + return; + } + } + + let old_kind = match (ns, old_binding.module()) { + (ValueNS, _) => "value", + (MacroNS, _) => "macro", + (TypeNS, _) if old_binding.is_extern_crate() => "extern crate", + (TypeNS, Some(module)) if module.is_normal() => "module", + (TypeNS, Some(module)) if module.is_trait() => "trait", + (TypeNS, _) => "type", + }; + + let msg = format!("the name `{}` is defined multiple times", name); + + let mut err = match (old_binding.is_extern_crate(), new_binding.is_extern_crate()) { + (true, true) => struct_span_err!(self.session, span, E0259, "{}", msg), + (true, _) | (_, true) => match new_binding.is_import() && old_binding.is_import() { + true => struct_span_err!(self.session, span, E0254, "{}", msg), + false => struct_span_err!(self.session, span, E0260, "{}", msg), + }, + _ => match (old_binding.is_import(), new_binding.is_import()) { + (false, false) => struct_span_err!(self.session, span, E0428, "{}", msg), + (true, true) => struct_span_err!(self.session, span, E0252, "{}", msg), + _ => struct_span_err!(self.session, span, E0255, "{}", msg), + }, + }; + + err.note(&format!( + "`{}` must be defined only once in the {} namespace of this {}", + name, + ns.descr(), + container + )); + + err.span_label(span, format!("`{}` re{} here", name, new_participle)); + err.span_label( + self.session.source_map().guess_head_span(old_binding.span), + format!("previous {} of the {} `{}` here", old_noun, old_kind, name), + ); + + // See https://github.com/rust-lang/rust/issues/32354 + use NameBindingKind::Import; + let import = match (&new_binding.kind, &old_binding.kind) { + // If there are two imports where one or both have attributes then prefer removing the + // import without attributes. + (Import { import: new, .. }, Import { import: old, .. }) + if { + !new_binding.span.is_dummy() + && !old_binding.span.is_dummy() + && (new.has_attributes || old.has_attributes) + } => + { + if old.has_attributes { + Some((new, new_binding.span, true)) + } else { + Some((old, old_binding.span, true)) + } + } + // Otherwise prioritize the new binding. + (Import { import, .. }, other) if !new_binding.span.is_dummy() => { + Some((import, new_binding.span, other.is_import())) + } + (other, Import { import, .. }) if !old_binding.span.is_dummy() => { + Some((import, old_binding.span, other.is_import())) + } + _ => None, + }; + + // Check if the target of the use for both bindings is the same. + let duplicate = new_binding.res().opt_def_id() == old_binding.res().opt_def_id(); + let has_dummy_span = new_binding.span.is_dummy() || old_binding.span.is_dummy(); + let from_item = + self.extern_prelude.get(&ident).map_or(true, |entry| entry.introduced_by_item); + // Only suggest removing an import if both bindings are to the same def, if both spans + // aren't dummy spans. Further, if both bindings are imports, then the ident must have + // been introduced by an item. + let should_remove_import = duplicate + && !has_dummy_span + && ((new_binding.is_extern_crate() || old_binding.is_extern_crate()) || from_item); + + match import { + Some((import, span, true)) if should_remove_import && import.is_nested() => { + self.add_suggestion_for_duplicate_nested_use(&mut err, import, span) + } + Some((import, _, true)) if should_remove_import && !import.is_glob() => { + // Simple case - remove the entire import. Due to the above match arm, this can + // only be a single use so just remove it entirely. + err.tool_only_span_suggestion( + import.use_span_with_attributes, + "remove unnecessary import", + String::new(), + Applicability::MaybeIncorrect, + ); + } + Some((import, span, _)) => { + self.add_suggestion_for_rename_of_use(&mut err, name, import, span) + } + _ => {} + } + + err.emit(); + self.name_already_seen.insert(name, span); + } + + /// This function adds a suggestion to change the binding name of a new import that conflicts + /// with an existing import. + /// + /// ```text,ignore (diagnostic) + /// help: you can use `as` to change the binding name of the import + /// | + /// LL | use foo::bar as other_bar; + /// | ^^^^^^^^^^^^^^^^^^^^^ + /// ``` + fn add_suggestion_for_rename_of_use( + &self, + err: &mut Diagnostic, + name: Symbol, + import: &Import<'_>, + binding_span: Span, + ) { + let suggested_name = if name.as_str().chars().next().unwrap().is_uppercase() { + format!("Other{}", name) + } else { + format!("other_{}", name) + }; + + let mut suggestion = None; + match import.kind { + ImportKind::Single { type_ns_only: true, .. } => { + suggestion = Some(format!("self as {}", suggested_name)) + } + ImportKind::Single { source, .. } => { + if let Some(pos) = + source.span.hi().0.checked_sub(binding_span.lo().0).map(|pos| pos as usize) + { + if let Ok(snippet) = self.session.source_map().span_to_snippet(binding_span) { + if pos <= snippet.len() { + suggestion = Some(format!( + "{} as {}{}", + &snippet[..pos], + suggested_name, + if snippet.ends_with(';') { ";" } else { "" } + )) + } + } + } + } + ImportKind::ExternCrate { source, target } => { + suggestion = Some(format!( + "extern crate {} as {};", + source.unwrap_or(target.name), + suggested_name, + )) + } + _ => unreachable!(), + } + + let rename_msg = "you can use `as` to change the binding name of the import"; + if let Some(suggestion) = suggestion { + err.span_suggestion( + binding_span, + rename_msg, + suggestion, + Applicability::MaybeIncorrect, + ); + } else { + err.span_label(binding_span, rename_msg); + } + } + + /// This function adds a suggestion to remove an unnecessary binding from an import that is + /// nested. In the following example, this function will be invoked to remove the `a` binding + /// in the second use statement: + /// + /// ```ignore (diagnostic) + /// use issue_52891::a; + /// use issue_52891::{d, a, e}; + /// ``` + /// + /// The following suggestion will be added: + /// + /// ```ignore (diagnostic) + /// use issue_52891::{d, a, e}; + /// ^-- help: remove unnecessary import + /// ``` + /// + /// If the nested use contains only one import then the suggestion will remove the entire + /// line. + /// + /// It is expected that the provided import is nested - this isn't checked by the + /// function. If this invariant is not upheld, this function's behaviour will be unexpected + /// as characters expected by span manipulations won't be present. + fn add_suggestion_for_duplicate_nested_use( + &self, + err: &mut Diagnostic, + import: &Import<'_>, + binding_span: Span, + ) { + assert!(import.is_nested()); + let message = "remove unnecessary import"; + + // Two examples will be used to illustrate the span manipulations we're doing: + // + // - Given `use issue_52891::{d, a, e};` where `a` is a duplicate then `binding_span` is + // `a` and `import.use_span` is `issue_52891::{d, a, e};`. + // - Given `use issue_52891::{d, e, a};` where `a` is a duplicate then `binding_span` is + // `a` and `import.use_span` is `issue_52891::{d, e, a};`. + + let (found_closing_brace, span) = + find_span_of_binding_until_next_binding(self.session, binding_span, import.use_span); + + // If there was a closing brace then identify the span to remove any trailing commas from + // previous imports. + if found_closing_brace { + if let Some(span) = extend_span_to_previous_binding(self.session, span) { + err.tool_only_span_suggestion( + span, + message, + String::new(), + Applicability::MaybeIncorrect, + ); + } else { + // Remove the entire line if we cannot extend the span back, this indicates an + // `issue_52891::{self}` case. + err.span_suggestion( + import.use_span_with_attributes, + message, + String::new(), + Applicability::MaybeIncorrect, + ); + } + + return; + } + + err.span_suggestion(span, message, String::new(), Applicability::MachineApplicable); + } + + crate fn lint_if_path_starts_with_module( + &mut self, + finalize: Finalize, + path: &[Segment], + second_binding: Option<&NameBinding<'_>>, + ) { + let (diag_id, diag_span) = match finalize { + Finalize::No => return, + Finalize::SimplePath(id, path_span) => (id, path_span), + Finalize::UsePath { root_id, root_span, .. } => (root_id, root_span), + Finalize::QPathTrait { qpath_id, qpath_span, .. } => (qpath_id, qpath_span), + }; + + let first_name = match path.get(0) { + // In the 2018 edition this lint is a hard error, so nothing to do + Some(seg) if seg.ident.span.rust_2015() && self.session.rust_2015() => seg.ident.name, + _ => return, + }; + + // We're only interested in `use` paths which should start with + // `{{root}}` currently. + if first_name != kw::PathRoot { + return; + } + + match path.get(1) { + // If this import looks like `crate::...` it's already good + Some(Segment { ident, .. }) if ident.name == kw::Crate => return, + // Otherwise go below to see if it's an extern crate + Some(_) => {} + // If the path has length one (and it's `PathRoot` most likely) + // then we don't know whether we're gonna be importing a crate or an + // item in our crate. Defer this lint to elsewhere + None => return, + } + + // If the first element of our path was actually resolved to an + // `ExternCrate` (also used for `crate::...`) then no need to issue a + // warning, this looks all good! + if let Some(binding) = second_binding { + if let NameBindingKind::Import { import, .. } = binding.kind { + // Careful: we still want to rewrite paths from renamed extern crates. + if let ImportKind::ExternCrate { source: None, .. } = import.kind { + return; + } + } + } + + let diag = BuiltinLintDiagnostics::AbsPathWithModule(diag_span); + self.lint_buffer.buffer_lint_with_diagnostic( + ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, + diag_id, + diag_span, + "absolute paths must start with `self`, `super`, \ + `crate`, or an external crate name in the 2018 edition", + diag, + ); + } + crate fn add_module_candidates( &mut self, module: Module<'a>, @@ -1227,7 +1616,7 @@ impl<'a> Resolver<'a> { } } - crate fn report_ambiguity_error(&self, ambiguity_error: &AmbiguityError<'_>) { + fn report_ambiguity_error(&self, ambiguity_error: &AmbiguityError<'_>) { let AmbiguityError { kind, ident, b1, b2, misc1, misc2 } = *ambiguity_error; let (b1, b2, misc1, misc2, swapped) = if b2.span.is_dummy() && !b1.span.is_dummy() { // We have to print the span-less alternative first, otherwise formatting looks bad. @@ -1293,7 +1682,7 @@ impl<'a> Resolver<'a> { None } - crate fn report_privacy_error(&self, privacy_error: &PrivacyError<'_>) { + fn report_privacy_error(&self, privacy_error: &PrivacyError<'_>) { let PrivacyError { ident, binding, .. } = *privacy_error; let res = binding.res(); @@ -1859,7 +2248,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { /// use foo::{a, b, c}; /// ^^^ /// ``` -pub(crate) fn find_span_of_binding_until_next_binding( +fn find_span_of_binding_until_next_binding( sess: &Session, binding_span: Span, use_span: Span, @@ -1910,7 +2299,7 @@ pub(crate) fn find_span_of_binding_until_next_binding( /// use foo::{a, b, c}; /// --- binding span /// ``` -pub(crate) fn extend_span_to_previous_binding(sess: &Session, binding_span: Span) -> Option { +fn extend_span_to_previous_binding(sess: &Session, binding_span: Span) -> Option { let source_map = sess.source_map(); // `prev_source` will contain all of the source that came before the span. @@ -1998,7 +2387,7 @@ fn find_span_immediately_after_crate_name( /// When an entity with a given name is not available in scope, we search for /// entities with that name in all crates. This method allows outputting the /// results of this search in a programmer-friendly way -crate fn show_candidates( +fn show_candidates( definitions: &rustc_hir::definitions::Definitions, session: &Session, err: &mut Diagnostic, @@ -2133,3 +2522,70 @@ crate fn show_candidates( } } } + +#[derive(Debug)] +struct UsePlacementFinder { + target_module: NodeId, + first_legal_span: Option, + first_use_span: Option, +} + +impl UsePlacementFinder { + fn check(krate: &Crate, target_module: NodeId) -> (Option, bool) { + let mut finder = + UsePlacementFinder { target_module, first_legal_span: None, first_use_span: None }; + finder.visit_crate(krate); + if let Some(use_span) = finder.first_use_span { + (Some(use_span), true) + } else { + (finder.first_legal_span, false) + } + } +} + +impl<'tcx> visit::Visitor<'tcx> for UsePlacementFinder { + fn visit_crate(&mut self, c: &Crate) { + if self.target_module == CRATE_NODE_ID { + let inject = c.spans.inject_use_span; + if is_span_suitable_for_use_injection(inject) { + self.first_legal_span = Some(inject); + } + self.first_use_span = search_for_any_use_in_items(&c.items); + return; + } else { + visit::walk_crate(self, c); + } + } + + fn visit_item(&mut self, item: &'tcx ast::Item) { + if self.target_module == item.id { + if let ItemKind::Mod(_, ModKind::Loaded(items, _inline, mod_spans)) = &item.kind { + let inject = mod_spans.inject_use_span; + if is_span_suitable_for_use_injection(inject) { + self.first_legal_span = Some(inject); + } + self.first_use_span = search_for_any_use_in_items(items); + return; + } + } else { + visit::walk_item(self, item); + } + } +} + +fn search_for_any_use_in_items(items: &[P]) -> Option { + for item in items { + if let ItemKind::Use(..) = item.kind { + if is_span_suitable_for_use_injection(item.span) { + return Some(item.span.shrink_to_lo()); + } + } + } + return None; +} + +fn is_span_suitable_for_use_injection(s: Span) -> bool { + // don't suggest placing a use before the prelude + // import or other generated ones + !s.from_expansion() +} diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index c0875bf547a00..b2c7a4d18de38 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1,5 +1,3 @@ -// ignore-tidy-filelength - //! This crate is responsible for the part of name resolution that doesn't require type checker. //! //! Module structure of the crate is built here. @@ -28,19 +26,13 @@ pub use rustc_hir::def::{Namespace, PerNS}; use rustc_arena::{DroplessArena, TypedArena}; use rustc_ast::node_id::NodeMap; -use rustc_ast::ptr::P; -use rustc_ast::visit::{self, Visitor}; -use rustc_ast::{self as ast, NodeId}; -use rustc_ast::{Crate, CRATE_NODE_ID}; -use rustc_ast::{Expr, ExprKind, LitKind}; -use rustc_ast::{ItemKind, ModKind, Path}; +use rustc_ast::{self as ast, NodeId, CRATE_NODE_ID}; +use rustc_ast::{Crate, Expr, ExprKind, LitKind, Path}; use rustc_ast_lowering::ResolverAstLowering; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_data_structures::intern::Interned; use rustc_data_structures::sync::Lrc; -use rustc_errors::{ - struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, -}; +use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed}; use rustc_expand::base::{DeriveResolutions, SyntaxExtension, SyntaxExtensionKind}; use rustc_hir::def::Namespace::*; use rustc_hir::def::{self, CtorOf, DefKind, PartialRes}; @@ -57,8 +49,7 @@ use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, DefIdTree, MainDefinition, RegisteredTools, ResolverOutputs}; use rustc_query_system::ich::StableHashingContext; use rustc_session::cstore::{CrateStore, MetadataLoaderDyn}; -use rustc_session::lint; -use rustc_session::lint::{BuiltinLintDiagnostics, LintBuffer}; +use rustc_session::lint::LintBuffer; use rustc_session::Session; use rustc_span::hygiene::{ExpnId, LocalExpnId, MacroKind, SyntaxContext, Transparency}; use rustc_span::source_map::Spanned; @@ -71,7 +62,6 @@ use std::collections::BTreeSet; use std::{cmp, fmt, mem, ptr}; use tracing::debug; -use diagnostics::{extend_span_to_previous_binding, find_span_of_binding_until_next_binding}; use diagnostics::{ImportSuggestion, LabelSuggestion, Suggestion}; use imports::{Import, ImportKind, ImportResolver, NameResolution}; use late::{HasGenericParams, PathSource}; @@ -312,73 +302,6 @@ impl<'a> From<&'a ast::PathSegment> for Segment { } } -#[derive(Debug)] -struct UsePlacementFinder { - target_module: NodeId, - first_legal_span: Option, - first_use_span: Option, -} - -impl UsePlacementFinder { - fn check(krate: &Crate, target_module: NodeId) -> (Option, bool) { - let mut finder = - UsePlacementFinder { target_module, first_legal_span: None, first_use_span: None }; - finder.visit_crate(krate); - if let Some(use_span) = finder.first_use_span { - (Some(use_span), true) - } else { - (finder.first_legal_span, false) - } - } -} - -fn is_span_suitable_for_use_injection(s: Span) -> bool { - // don't suggest placing a use before the prelude - // import or other generated ones - !s.from_expansion() -} - -fn search_for_any_use_in_items(items: &[P]) -> Option { - for item in items { - if let ItemKind::Use(..) = item.kind { - if is_span_suitable_for_use_injection(item.span) { - return Some(item.span.shrink_to_lo()); - } - } - } - return None; -} - -impl<'tcx> Visitor<'tcx> for UsePlacementFinder { - fn visit_crate(&mut self, c: &Crate) { - if self.target_module == CRATE_NODE_ID { - let inject = c.spans.inject_use_span; - if is_span_suitable_for_use_injection(inject) { - self.first_legal_span = Some(inject); - } - self.first_use_span = search_for_any_use_in_items(&c.items); - return; - } else { - visit::walk_crate(self, c); - } - } - - fn visit_item(&mut self, item: &'tcx ast::Item) { - if self.target_module == item.id { - if let ItemKind::Mod(_, ModKind::Loaded(items, _inline, mod_spans)) = &item.kind { - let inject = mod_spans.inject_use_span; - if is_span_suitable_for_use_injection(inject) { - self.first_legal_span = Some(inject); - } - self.first_use_span = search_for_any_use_in_items(items); - return; - } - } else { - visit::walk_item(self, item); - } - } -} - /// An intermediate resolution result. /// /// This refers to the thing referred by a name. The difference between `Res` and `Item` is that @@ -1800,65 +1723,6 @@ impl<'a> Resolver<'a> { module } - fn lint_if_path_starts_with_module( - &mut self, - finalize: Finalize, - path: &[Segment], - second_binding: Option<&NameBinding<'_>>, - ) { - let (diag_id, diag_span) = match finalize { - Finalize::No => return, - Finalize::SimplePath(id, path_span) => (id, path_span), - Finalize::UsePath { root_id, root_span, .. } => (root_id, root_span), - Finalize::QPathTrait { qpath_id, qpath_span, .. } => (qpath_id, qpath_span), - }; - - let first_name = match path.get(0) { - // In the 2018 edition this lint is a hard error, so nothing to do - Some(seg) if seg.ident.span.rust_2015() && self.session.rust_2015() => seg.ident.name, - _ => return, - }; - - // We're only interested in `use` paths which should start with - // `{{root}}` currently. - if first_name != kw::PathRoot { - return; - } - - match path.get(1) { - // If this import looks like `crate::...` it's already good - Some(Segment { ident, .. }) if ident.name == kw::Crate => return, - // Otherwise go below to see if it's an extern crate - Some(_) => {} - // If the path has length one (and it's `PathRoot` most likely) - // then we don't know whether we're gonna be importing a crate or an - // item in our crate. Defer this lint to elsewhere - None => return, - } - - // If the first element of our path was actually resolved to an - // `ExternCrate` (also used for `crate::...`) then no need to issue a - // warning, this looks all good! - if let Some(binding) = second_binding { - if let NameBindingKind::Import { import, .. } = binding.kind { - // Careful: we still want to rewrite paths from renamed extern crates. - if let ImportKind::ExternCrate { source: None, .. } = import.kind { - return; - } - } - } - - let diag = BuiltinLintDiagnostics::AbsPathWithModule(diag_span); - self.lint_buffer.buffer_lint_with_diagnostic( - lint::builtin::ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, - diag_id, - diag_span, - "absolute paths must start with `self`, `super`, \ - `crate`, or an external crate name in the 2018 edition", - diag, - ); - } - fn record_partial_res(&mut self, node_id: NodeId, resolution: PartialRes) { debug!("(recording res) recording {:?} for {}", resolution, node_id); if let Some(prev_res) = self.partial_res_map.insert(node_id, resolution) { @@ -1905,331 +1769,6 @@ impl<'a> Resolver<'a> { } } - fn report_errors(&mut self, krate: &Crate) { - self.report_with_use_injections(krate); - - for &(span_use, span_def) in &self.macro_expanded_macro_export_errors { - let msg = "macro-expanded `macro_export` macros from the current crate \ - cannot be referred to by absolute paths"; - self.lint_buffer.buffer_lint_with_diagnostic( - lint::builtin::MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS, - CRATE_NODE_ID, - span_use, - msg, - BuiltinLintDiagnostics::MacroExpandedMacroExportsAccessedByAbsolutePaths(span_def), - ); - } - - for ambiguity_error in &self.ambiguity_errors { - self.report_ambiguity_error(ambiguity_error); - } - - let mut reported_spans = FxHashSet::default(); - for error in &self.privacy_errors { - if reported_spans.insert(error.dedup_span) { - self.report_privacy_error(error); - } - } - } - - fn report_with_use_injections(&mut self, krate: &Crate) { - for UseError { mut err, candidates, def_id, instead, suggestion } in - self.use_injections.drain(..) - { - let (span, found_use) = if let Some(def_id) = def_id.as_local() { - UsePlacementFinder::check(krate, self.def_id_to_node_id[def_id]) - } else { - (None, false) - }; - if !candidates.is_empty() { - diagnostics::show_candidates( - &self.definitions, - self.session, - &mut err, - span, - &candidates, - instead, - found_use, - ); - } else if let Some((span, msg, sugg, appl)) = suggestion { - err.span_suggestion(span, msg, sugg, appl); - } - err.emit(); - } - } - - fn report_conflict<'b>( - &mut self, - parent: Module<'_>, - ident: Ident, - ns: Namespace, - new_binding: &NameBinding<'b>, - old_binding: &NameBinding<'b>, - ) { - // Error on the second of two conflicting names - if old_binding.span.lo() > new_binding.span.lo() { - return self.report_conflict(parent, ident, ns, old_binding, new_binding); - } - - let container = match parent.kind { - ModuleKind::Def(kind, _, _) => kind.descr(parent.def_id()), - ModuleKind::Block(..) => "block", - }; - - let old_noun = match old_binding.is_import() { - true => "import", - false => "definition", - }; - - let new_participle = match new_binding.is_import() { - true => "imported", - false => "defined", - }; - - let (name, span) = - (ident.name, self.session.source_map().guess_head_span(new_binding.span)); - - if let Some(s) = self.name_already_seen.get(&name) { - if s == &span { - return; - } - } - - let old_kind = match (ns, old_binding.module()) { - (ValueNS, _) => "value", - (MacroNS, _) => "macro", - (TypeNS, _) if old_binding.is_extern_crate() => "extern crate", - (TypeNS, Some(module)) if module.is_normal() => "module", - (TypeNS, Some(module)) if module.is_trait() => "trait", - (TypeNS, _) => "type", - }; - - let msg = format!("the name `{}` is defined multiple times", name); - - let mut err = match (old_binding.is_extern_crate(), new_binding.is_extern_crate()) { - (true, true) => struct_span_err!(self.session, span, E0259, "{}", msg), - (true, _) | (_, true) => match new_binding.is_import() && old_binding.is_import() { - true => struct_span_err!(self.session, span, E0254, "{}", msg), - false => struct_span_err!(self.session, span, E0260, "{}", msg), - }, - _ => match (old_binding.is_import(), new_binding.is_import()) { - (false, false) => struct_span_err!(self.session, span, E0428, "{}", msg), - (true, true) => struct_span_err!(self.session, span, E0252, "{}", msg), - _ => struct_span_err!(self.session, span, E0255, "{}", msg), - }, - }; - - err.note(&format!( - "`{}` must be defined only once in the {} namespace of this {}", - name, - ns.descr(), - container - )); - - err.span_label(span, format!("`{}` re{} here", name, new_participle)); - err.span_label( - self.session.source_map().guess_head_span(old_binding.span), - format!("previous {} of the {} `{}` here", old_noun, old_kind, name), - ); - - // See https://github.com/rust-lang/rust/issues/32354 - use NameBindingKind::Import; - let import = match (&new_binding.kind, &old_binding.kind) { - // If there are two imports where one or both have attributes then prefer removing the - // import without attributes. - (Import { import: new, .. }, Import { import: old, .. }) - if { - !new_binding.span.is_dummy() - && !old_binding.span.is_dummy() - && (new.has_attributes || old.has_attributes) - } => - { - if old.has_attributes { - Some((new, new_binding.span, true)) - } else { - Some((old, old_binding.span, true)) - } - } - // Otherwise prioritize the new binding. - (Import { import, .. }, other) if !new_binding.span.is_dummy() => { - Some((import, new_binding.span, other.is_import())) - } - (other, Import { import, .. }) if !old_binding.span.is_dummy() => { - Some((import, old_binding.span, other.is_import())) - } - _ => None, - }; - - // Check if the target of the use for both bindings is the same. - let duplicate = new_binding.res().opt_def_id() == old_binding.res().opt_def_id(); - let has_dummy_span = new_binding.span.is_dummy() || old_binding.span.is_dummy(); - let from_item = - self.extern_prelude.get(&ident).map_or(true, |entry| entry.introduced_by_item); - // Only suggest removing an import if both bindings are to the same def, if both spans - // aren't dummy spans. Further, if both bindings are imports, then the ident must have - // been introduced by an item. - let should_remove_import = duplicate - && !has_dummy_span - && ((new_binding.is_extern_crate() || old_binding.is_extern_crate()) || from_item); - - match import { - Some((import, span, true)) if should_remove_import && import.is_nested() => { - self.add_suggestion_for_duplicate_nested_use(&mut err, import, span) - } - Some((import, _, true)) if should_remove_import && !import.is_glob() => { - // Simple case - remove the entire import. Due to the above match arm, this can - // only be a single use so just remove it entirely. - err.tool_only_span_suggestion( - import.use_span_with_attributes, - "remove unnecessary import", - String::new(), - Applicability::MaybeIncorrect, - ); - } - Some((import, span, _)) => { - self.add_suggestion_for_rename_of_use(&mut err, name, import, span) - } - _ => {} - } - - err.emit(); - self.name_already_seen.insert(name, span); - } - - /// This function adds a suggestion to change the binding name of a new import that conflicts - /// with an existing import. - /// - /// ```text,ignore (diagnostic) - /// help: you can use `as` to change the binding name of the import - /// | - /// LL | use foo::bar as other_bar; - /// | ^^^^^^^^^^^^^^^^^^^^^ - /// ``` - fn add_suggestion_for_rename_of_use( - &self, - err: &mut Diagnostic, - name: Symbol, - import: &Import<'_>, - binding_span: Span, - ) { - let suggested_name = if name.as_str().chars().next().unwrap().is_uppercase() { - format!("Other{}", name) - } else { - format!("other_{}", name) - }; - - let mut suggestion = None; - match import.kind { - ImportKind::Single { type_ns_only: true, .. } => { - suggestion = Some(format!("self as {}", suggested_name)) - } - ImportKind::Single { source, .. } => { - if let Some(pos) = - source.span.hi().0.checked_sub(binding_span.lo().0).map(|pos| pos as usize) - { - if let Ok(snippet) = self.session.source_map().span_to_snippet(binding_span) { - if pos <= snippet.len() { - suggestion = Some(format!( - "{} as {}{}", - &snippet[..pos], - suggested_name, - if snippet.ends_with(';') { ";" } else { "" } - )) - } - } - } - } - ImportKind::ExternCrate { source, target } => { - suggestion = Some(format!( - "extern crate {} as {};", - source.unwrap_or(target.name), - suggested_name, - )) - } - _ => unreachable!(), - } - - let rename_msg = "you can use `as` to change the binding name of the import"; - if let Some(suggestion) = suggestion { - err.span_suggestion( - binding_span, - rename_msg, - suggestion, - Applicability::MaybeIncorrect, - ); - } else { - err.span_label(binding_span, rename_msg); - } - } - - /// This function adds a suggestion to remove an unnecessary binding from an import that is - /// nested. In the following example, this function will be invoked to remove the `a` binding - /// in the second use statement: - /// - /// ```ignore (diagnostic) - /// use issue_52891::a; - /// use issue_52891::{d, a, e}; - /// ``` - /// - /// The following suggestion will be added: - /// - /// ```ignore (diagnostic) - /// use issue_52891::{d, a, e}; - /// ^-- help: remove unnecessary import - /// ``` - /// - /// If the nested use contains only one import then the suggestion will remove the entire - /// line. - /// - /// It is expected that the provided import is nested - this isn't checked by the - /// function. If this invariant is not upheld, this function's behaviour will be unexpected - /// as characters expected by span manipulations won't be present. - fn add_suggestion_for_duplicate_nested_use( - &self, - err: &mut Diagnostic, - import: &Import<'_>, - binding_span: Span, - ) { - assert!(import.is_nested()); - let message = "remove unnecessary import"; - - // Two examples will be used to illustrate the span manipulations we're doing: - // - // - Given `use issue_52891::{d, a, e};` where `a` is a duplicate then `binding_span` is - // `a` and `import.use_span` is `issue_52891::{d, a, e};`. - // - Given `use issue_52891::{d, e, a};` where `a` is a duplicate then `binding_span` is - // `a` and `import.use_span` is `issue_52891::{d, e, a};`. - - let (found_closing_brace, span) = - find_span_of_binding_until_next_binding(self.session, binding_span, import.use_span); - - // If there was a closing brace then identify the span to remove any trailing commas from - // previous imports. - if found_closing_brace { - if let Some(span) = extend_span_to_previous_binding(self.session, span) { - err.tool_only_span_suggestion( - span, - message, - String::new(), - Applicability::MaybeIncorrect, - ); - } else { - // Remove the entire line if we cannot extend the span back, this indicates an - // `issue_52891::{self}` case. - err.span_suggestion( - import.use_span_with_attributes, - message, - String::new(), - Applicability::MaybeIncorrect, - ); - } - - return; - } - - err.span_suggestion(span, message, String::new(), Applicability::MachineApplicable); - } - fn extern_prelude_get(&mut self, ident: Ident, finalize: bool) -> Option<&'a NameBinding<'a>> { if ident.is_path_segment_keyword() { // Make sure `self`, `super` etc produce an error when passed to here. From b796d92da3a24326a4bd50a95f765331edef780b Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Tue, 12 Apr 2022 19:55:47 +0200 Subject: [PATCH 20/23] Fix imports. --- compiler/rustc_resolve/src/ident.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 0260386cb735f..b6cb960b517a6 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -16,14 +16,9 @@ use std::ptr; use crate::late::{ConstantItemKind, HasGenericParams, PathSource, Rib, RibKind}; use crate::macros::{sub_namespace_match, MacroRulesScope}; use crate::{AmbiguityError, AmbiguityErrorMisc, AmbiguityKind, Determinacy, Finalize}; -use crate::{ - ImportKind, LexicalScopeBinding, Module, ModuleKind, ModuleOrUniformRoot, NameBinding, -}; -use crate::{ - NameBindingKind, ParentScope, PathResult, PrivacyError, Res, ResolutionError, Resolver, Scope, - ScopeSet, Segment, -}; -use crate::{ToNameBinding, Weak}; +use crate::{ImportKind, LexicalScopeBinding, Module, ModuleKind, ModuleOrUniformRoot}; +use crate::{NameBinding, NameBindingKind, ParentScope, PathResult, PrivacyError, Res}; +use crate::{ResolutionError, Resolver, Scope, ScopeSet, Segment, ToNameBinding, Weak}; use Determinacy::*; use Namespace::*; From 23bf9777589f07ac4a30f7eccd818eecd8fa5b7d Mon Sep 17 00:00:00 2001 From: Caio Date: Tue, 12 Apr 2022 16:23:36 -0300 Subject: [PATCH 21/23] Implement tuples using recursion --- library/core/src/tuple.rs | 141 ++++++++++++++++++++------------------ 1 file changed, 74 insertions(+), 67 deletions(-) diff --git a/library/core/src/tuple.rs b/library/core/src/tuple.rs index f9a317f663c0b..181717f35bd25 100644 --- a/library/core/src/tuple.rs +++ b/library/core/src/tuple.rs @@ -3,68 +3,88 @@ use crate::cmp::Ordering::*; use crate::cmp::*; -// macro for implementing n-ary tuple functions and operations +// Recursive macro for implementing n-ary tuple functions and operations +// +// Also provides implementations for tuples with lesser arity. For example, tuple_impls!(A B C) +// will implement everything for (A, B, C), (A, B) and (A,). macro_rules! tuple_impls { - ( $( ( $( $T:ident )+ ) )+ ) => { - $( - #[stable(feature = "rust1", since = "1.0.0")] - impl<$($T:PartialEq),+> PartialEq for ($($T,)+) where last_type!($($T,)+): ?Sized { - #[inline] - fn eq(&self, other: &($($T,)+)) -> bool { - $( ${ignore(T)} self.${index()} == other.${index()} )&&+ - } - #[inline] - fn ne(&self, other: &($($T,)+)) -> bool { - $( ${ignore(T)} self.${index()} != other.${index()} )||+ - } + // Stopping criteria (1-ary tuple) + ($T:ident) => { + tuple_impls!(@impl $T); + }; + // Running criteria (n-ary tuple, with n >= 2) + ($T:ident $( $U:ident )+) => { + tuple_impls!($( $U )+); + tuple_impls!(@impl $T $( $U )+); + }; + // "Private" internal implementation + (@impl $( $T:ident )+) => { + #[stable(feature = "rust1", since = "1.0.0")] + impl<$($T:PartialEq),+> PartialEq for ($($T,)+) + where + last_type!($($T,)+): ?Sized + { + #[inline] + fn eq(&self, other: &($($T,)+)) -> bool { + $( ${ignore(T)} self.${index()} == other.${index()} )&&+ + } + #[inline] + fn ne(&self, other: &($($T,)+)) -> bool { + $( ${ignore(T)} self.${index()} != other.${index()} )||+ } + } - #[stable(feature = "rust1", since = "1.0.0")] - impl<$($T:Eq),+> Eq for ($($T,)+) where last_type!($($T,)+): ?Sized {} + #[stable(feature = "rust1", since = "1.0.0")] + impl<$($T:Eq),+> Eq for ($($T,)+) + where + last_type!($($T,)+): ?Sized + {} - #[stable(feature = "rust1", since = "1.0.0")] - impl<$($T:PartialOrd + PartialEq),+> PartialOrd for ($($T,)+) - where - last_type!($($T,)+): ?Sized - { - #[inline] - fn partial_cmp(&self, other: &($($T,)+)) -> Option { - lexical_partial_cmp!($( ${ignore(T)} self.${index()}, other.${index()} ),+) - } - #[inline] - fn lt(&self, other: &($($T,)+)) -> bool { - lexical_ord!(lt, $( ${ignore(T)} self.${index()}, other.${index()} ),+) - } - #[inline] - fn le(&self, other: &($($T,)+)) -> bool { - lexical_ord!(le, $( ${ignore(T)} self.${index()}, other.${index()} ),+) - } - #[inline] - fn ge(&self, other: &($($T,)+)) -> bool { - lexical_ord!(ge, $( ${ignore(T)} self.${index()}, other.${index()} ),+) - } - #[inline] - fn gt(&self, other: &($($T,)+)) -> bool { - lexical_ord!(gt, $( ${ignore(T)} self.${index()}, other.${index()} ),+) - } + #[stable(feature = "rust1", since = "1.0.0")] + impl<$($T:PartialOrd + PartialEq),+> PartialOrd for ($($T,)+) + where + last_type!($($T,)+): ?Sized + { + #[inline] + fn partial_cmp(&self, other: &($($T,)+)) -> Option { + lexical_partial_cmp!($( ${ignore(T)} self.${index()}, other.${index()} ),+) + } + #[inline] + fn lt(&self, other: &($($T,)+)) -> bool { + lexical_ord!(lt, $( ${ignore(T)} self.${index()}, other.${index()} ),+) + } + #[inline] + fn le(&self, other: &($($T,)+)) -> bool { + lexical_ord!(le, $( ${ignore(T)} self.${index()}, other.${index()} ),+) } + #[inline] + fn ge(&self, other: &($($T,)+)) -> bool { + lexical_ord!(ge, $( ${ignore(T)} self.${index()}, other.${index()} ),+) + } + #[inline] + fn gt(&self, other: &($($T,)+)) -> bool { + lexical_ord!(gt, $( ${ignore(T)} self.${index()}, other.${index()} ),+) + } + } - #[stable(feature = "rust1", since = "1.0.0")] - impl<$($T:Ord),+> Ord for ($($T,)+) where last_type!($($T,)+): ?Sized { - #[inline] - fn cmp(&self, other: &($($T,)+)) -> Ordering { - lexical_cmp!($( ${ignore(T)} self.${index()}, other.${index()} ),+) - } + #[stable(feature = "rust1", since = "1.0.0")] + impl<$($T:Ord),+> Ord for ($($T,)+) + where + last_type!($($T,)+): ?Sized + { + #[inline] + fn cmp(&self, other: &($($T,)+)) -> Ordering { + lexical_cmp!($( ${ignore(T)} self.${index()}, other.${index()} ),+) } + } - #[stable(feature = "rust1", since = "1.0.0")] - impl<$($T:Default),+> Default for ($($T,)+) { - #[inline] - fn default() -> ($($T,)+) { - ($({ let x: $T = Default::default(); x},)+) - } + #[stable(feature = "rust1", since = "1.0.0")] + impl<$($T:Default),+> Default for ($($T,)+) { + #[inline] + fn default() -> ($($T,)+) { + ($({ let x: $T = Default::default(); x},)+) } - )+ + } } } @@ -105,17 +125,4 @@ macro_rules! last_type { ($a:ident, $($rest_a:ident,)+) => { last_type!($($rest_a,)+) }; } -tuple_impls! { - (A) - (A B) - (A B C) - (A B C D) - (A B C D E) - (A B C D E F) - (A B C D E F G) - (A B C D E F G H) - (A B C D E F G H I) - (A B C D E F G H I J) - (A B C D E F G H I J K) - (A B C D E F G H I J K L) -} +tuple_impls!(A B C D E F G H I J K L); From 6d0349d2ea97e2a3e208e0342a6a95d6b90d92ea Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 12 Apr 2022 21:36:09 +0200 Subject: [PATCH 22/23] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Michael Goulet Co-authored-by: Rémy Rakic --- compiler/rustc_typeck/src/coherence/orphan.rs | 2 +- src/test/ui/type-alias-impl-trait/issue-84660-unsoundness.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_typeck/src/coherence/orphan.rs b/compiler/rustc_typeck/src/coherence/orphan.rs index f9e2e4c41aad9..77a5374482917 100644 --- a/compiler/rustc_typeck/src/coherence/orphan.rs +++ b/compiler/rustc_typeck/src/coherence/orphan.rs @@ -191,7 +191,7 @@ fn orphan_check_impl(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), ErrorGua return Err(reported); } } - span_bug!(sp, "opque type not found, but `has_opaque_types` is set") + span_bug!(sp, "opaque type not found, but `has_opaque_types` is set") } Ok(()) 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 ee6eb605c5be9..f12d1b6d953cd 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,4 +1,4 @@ -// Another example from issue #84660, this time weaponized as a safe transmut: an opaque type in an +// Another example from issue #84660, this time weaponized as a safe transmute: an opaque type in an // impl header being accepted was used to create unsoundness. #![feature(type_alias_impl_trait)] From 276b94601025152a2cf5668a53a96fae94f37731 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Tue, 12 Apr 2022 20:18:08 +0300 Subject: [PATCH 23/23] Handle `unusable_binding` more compactly. --- compiler/rustc_resolve/src/ident.rs | 31 +++++++++++++---------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index b6cb960b517a6..25ab3f7dacf90 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -902,27 +902,24 @@ impl<'a> Resolver<'a> { self.resolution(module, key).try_borrow_mut().map_err(|_| (Determined, Weak::No))?; // This happens when there is a cycle of imports. if let Some(path_span) = finalize { - let Some(mut binding) = resolution.binding else { - return Err((Determined, Weak::No)); - }; - // If the primary binding is unusable, search further and return the shadowed glob // binding if it exists. What we really want here is having two separate scopes in // a module - one for non-globs and one for globs, but until that's done use this // hack to avoid inconsistent resolution ICEs during import validation. - if let Some(unusable_binding) = unusable_binding - && ptr::eq(binding, unusable_binding) - { - let Some(shadowed) = resolution.shadowed_glob else { - return Err((Determined, Weak::No)); - }; - - if ptr::eq(shadowed, unusable_binding) { - return Err((Determined, Weak::No)); - } - - binding = shadowed; - } + let binding = [resolution.binding, resolution.shadowed_glob] + .into_iter() + .filter_map(|binding| match (binding, unusable_binding) { + (Some(binding), Some(unusable_binding)) + if ptr::eq(binding, unusable_binding) => + { + None + } + _ => binding, + }) + .next(); + let Some(binding) = binding else { + return Err((Determined, Weak::No)); + }; if !self.is_accessible_from(binding.vis, parent_scope.module) { if last_import_segment {