From a33668601b7da85cfbc3fd669c0acdc3a696a8d6 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 19 Jun 2022 20:49:07 -0700 Subject: [PATCH 01/16] Fix trait object reborrow suggestion --- compiler/rustc_middle/src/traits/mod.rs | 2 +- .../src/traits/error_reporting/mod.rs | 7 +++---- .../src/traits/error_reporting/suggestions.rs | 2 +- .../src/traits/select/confirmation.rs | 6 +++--- .../suggest-borrow-to-dyn-object.rs | 16 ++++++++++++++++ .../suggest-borrow-to-dyn-object.stderr | 19 +++++++++++++++++++ 6 files changed, 43 insertions(+), 9 deletions(-) create mode 100644 src/test/ui/suggestions/suggest-borrow-to-dyn-object.rs create mode 100644 src/test/ui/suggestions/suggest-borrow-to-dyn-object.stderr diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 912b09eeca861..278e00712e695 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -253,7 +253,7 @@ pub enum ObligationCauseCode<'tcx> { ObjectTypeBound(Ty<'tcx>, ty::Region<'tcx>), /// Obligation incurred due to an object cast. - ObjectCastObligation(/* Object type */ Ty<'tcx>), + ObjectCastObligation(/* Concrete type */ Ty<'tcx>, /* Object type */ Ty<'tcx>), /// Obligation incurred due to a coercion. Coercion { diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 2f999f5ffad85..442d463d79d47 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -484,10 +484,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { err.span_label(span, explanation); } - if let ObligationCauseCode::ObjectCastObligation(obj_ty) = obligation.cause.code().peel_derives() && - let Some(self_ty) = trait_predicate.self_ty().no_bound_vars() && + if let ObligationCauseCode::ObjectCastObligation(concrete_ty, obj_ty) = obligation.cause.code().peel_derives() && Some(trait_ref.def_id()) == self.tcx.lang_items().sized_trait() { - self.suggest_borrowing_for_object_cast(&mut err, &obligation, self_ty, *obj_ty); + self.suggest_borrowing_for_object_cast(&mut err, &root_obligation, *concrete_ty, *obj_ty); } if trait_predicate.is_const_if_const() && obligation.param_env.is_const() { @@ -1565,7 +1564,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { obligation.cause.code().peel_derives(), ObligationCauseCode::ItemObligation(_) | ObligationCauseCode::BindingObligation(_, _) - | ObligationCauseCode::ObjectCastObligation(_) + | ObligationCauseCode::ObjectCastObligation(..) | ObligationCauseCode::OpaqueType ); if let Err(error) = self.at(&obligation.cause, obligation.param_env).eq_exp( diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 09b73b982a0c1..167ecac226d11 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -2228,7 +2228,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { err.span_note(tcx.def_span(item_def_id), &descr); } } - ObligationCauseCode::ObjectCastObligation(object_ty) => { + ObligationCauseCode::ObjectCastObligation(_, object_ty) => { err.note(&format!( "required for the cast to the object type `{}`", self.ty_to_string(object_ty) diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 5942bb79d69e8..e1131140c39e8 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -813,7 +813,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let cause = ObligationCause::new( obligation.cause.span, obligation.cause.body_id, - ObjectCastObligation(target), + ObjectCastObligation(source, target), ); let outlives = ty::OutlivesPredicate(r_a, r_b); nested.push(Obligation::with_depth( @@ -910,7 +910,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let cause = ObligationCause::new( obligation.cause.span, obligation.cause.body_id, - ObjectCastObligation(target), + ObjectCastObligation(source, target), ); let outlives = ty::OutlivesPredicate(r_a, r_b); nested.push(Obligation::with_depth( @@ -931,7 +931,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let cause = ObligationCause::new( obligation.cause.span, obligation.cause.body_id, - ObjectCastObligation(target), + ObjectCastObligation(source, target), ); let predicate_to_obligation = |predicate| { diff --git a/src/test/ui/suggestions/suggest-borrow-to-dyn-object.rs b/src/test/ui/suggestions/suggest-borrow-to-dyn-object.rs new file mode 100644 index 0000000000000..120fc538307a7 --- /dev/null +++ b/src/test/ui/suggestions/suggest-borrow-to-dyn-object.rs @@ -0,0 +1,16 @@ +use std::ffi::{OsStr, OsString}; +use std::path::Path; + +fn check(p: &dyn AsRef) { + let m = std::fs::metadata(&p); + println!("{:?}", &m); +} + +fn main() { + let s: OsString = ".".into(); + let s: &OsStr = &s; + check(s); + //~^ ERROR the size for values of type `[u8]` cannot be known at compilation time + //~| HELP within `OsStr`, the trait `Sized` is not implemented for `[u8]` + //~| HELP consider borrowing the value, since `&OsStr` can be coerced into `dyn AsRef` +} diff --git a/src/test/ui/suggestions/suggest-borrow-to-dyn-object.stderr b/src/test/ui/suggestions/suggest-borrow-to-dyn-object.stderr new file mode 100644 index 0000000000000..8961f4275a283 --- /dev/null +++ b/src/test/ui/suggestions/suggest-borrow-to-dyn-object.stderr @@ -0,0 +1,19 @@ +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> $DIR/suggest-borrow-to-dyn-object.rs:12:11 + | +LL | check(s); + | ----- ^ doesn't have a size known at compile-time + | | + | required by a bound introduced by this call + | + = help: within `OsStr`, the trait `Sized` is not implemented for `[u8]` + = note: required because it appears within the type `OsStr` + = note: required for the cast to the object type `dyn AsRef` +help: consider borrowing the value, since `&OsStr` can be coerced into `dyn AsRef` + | +LL | check(&s); + | + + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. From 448e00b88979eb16d686fb17aa9a68404feab000 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 19 Jun 2022 21:04:06 -0700 Subject: [PATCH 02/16] Note concrete type being coerced into object --- .../src/traits/error_reporting/suggestions.rs | 5 +++-- .../assoc-type-eq-with-dyn-atb-fail.stderr | 2 +- .../ui/associated-types/associated-types-eq-3.stderr | 2 +- .../associated-types-overridden-binding-2.stderr | 2 +- src/test/ui/associated-types/issue-65774-1.stderr | 2 +- src/test/ui/associated-types/issue-65774-2.stderr | 2 +- .../async-block-control-flow-static-semantics.stderr | 4 ++-- src/test/ui/async-await/issue-86507.stderr | 2 +- .../coerce-issue-49593-box-never.nofallback.stderr | 4 ++-- .../defaults/trait_objects_fail.stderr | 4 ++-- src/test/ui/custom_test_frameworks/mismatch.stderr | 2 +- src/test/ui/dst/dst-bad-coerce1.stderr | 4 ++-- src/test/ui/dst/dst-object-from-unsized-type.stderr | 8 ++++---- .../issue-79422.extended.stderr | 2 +- src/test/ui/issues/issue-14366.stderr | 2 +- src/test/ui/issues/issue-22034.stderr | 2 +- src/test/ui/issues/issue-22872.stderr | 2 +- src/test/ui/issues/issue-7013.stderr | 2 +- src/test/ui/kindck/kindck-impl-type-params.stderr | 12 ++++++------ src/test/ui/mismatched_types/cast-rfc0401.stderr | 4 ++-- .../never_type/fallback-closure-wrap.fallback.stderr | 2 +- .../suggestions/derive-macro-missing-bounds.stderr | 8 ++++---- .../suggestions/suggest-borrow-to-dyn-object.stderr | 2 +- src/test/ui/traits/coercion-generic-bad.stderr | 2 +- src/test/ui/traits/map-types.stderr | 2 +- .../trait-upcasting/type-checking-test-1.stderr | 2 +- .../trait-upcasting/type-checking-test-2.stderr | 4 ++-- src/test/ui/unsized/unsized-fn-param.stderr | 8 ++++---- 28 files changed, 50 insertions(+), 49 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 167ecac226d11..abd49b69e690b 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -2228,9 +2228,10 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { err.span_note(tcx.def_span(item_def_id), &descr); } } - ObligationCauseCode::ObjectCastObligation(_, object_ty) => { + ObligationCauseCode::ObjectCastObligation(concrete_ty, object_ty) => { err.note(&format!( - "required for the cast to the object type `{}`", + "required for the cast from `{}` to the object type `{}`", + self.ty_to_string(concrete_ty), self.ty_to_string(object_ty) )); } diff --git a/src/test/ui/associated-type-bounds/assoc-type-eq-with-dyn-atb-fail.stderr b/src/test/ui/associated-type-bounds/assoc-type-eq-with-dyn-atb-fail.stderr index f1dcd34066dbc..f40e6585b38b1 100644 --- a/src/test/ui/associated-type-bounds/assoc-type-eq-with-dyn-atb-fail.stderr +++ b/src/test/ui/associated-type-bounds/assoc-type-eq-with-dyn-atb-fail.stderr @@ -4,7 +4,7 @@ error[E0277]: the trait bound `String: Copy` is not satisfied LL | Box::new(AssocNoCopy) | ^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String` | - = note: required for the cast to the object type `dyn Bar::Out::{opaque#0}>` + = note: required for the cast from `AssocNoCopy` to the object type `dyn Bar::Out::{opaque#0}>` error: aborting due to previous error diff --git a/src/test/ui/associated-types/associated-types-eq-3.stderr b/src/test/ui/associated-types/associated-types-eq-3.stderr index 521907a60445c..bed63a5e6df03 100644 --- a/src/test/ui/associated-types/associated-types-eq-3.stderr +++ b/src/test/ui/associated-types/associated-types-eq-3.stderr @@ -41,7 +41,7 @@ note: expected this to be `Bar` | LL | type A = usize; | ^^^^^ - = note: required for the cast to the object type `dyn Foo` + = note: required for the cast from `isize` to the object type `dyn Foo` error: aborting due to 3 previous errors diff --git a/src/test/ui/associated-types/associated-types-overridden-binding-2.stderr b/src/test/ui/associated-types/associated-types-overridden-binding-2.stderr index 9f1abf2a6c4b6..dbd9a44ed9774 100644 --- a/src/test/ui/associated-types/associated-types-overridden-binding-2.stderr +++ b/src/test/ui/associated-types/associated-types-overridden-binding-2.stderr @@ -4,7 +4,7 @@ error[E0271]: type mismatch resolving ` as Iterator>::It LL | let _: &dyn I32Iterator = &vec![42].into_iter(); | ^^^^^^^^^^^^^^^^^^^^^ expected `i32`, found `u32` | - = note: required for the cast to the object type `dyn Iterator` + = note: required for the cast from `std::vec::IntoIter` to the object type `dyn Iterator` error: aborting due to previous error diff --git a/src/test/ui/associated-types/issue-65774-1.stderr b/src/test/ui/associated-types/issue-65774-1.stderr index e468a1b3ba484..419de689c52da 100644 --- a/src/test/ui/associated-types/issue-65774-1.stderr +++ b/src/test/ui/associated-types/issue-65774-1.stderr @@ -23,7 +23,7 @@ note: required because of the requirements on the impl of `MyDisplay` for `&mut | LL | impl<'a, T: MyDisplay> MyDisplay for &'a mut T { } | ^^^^^^^^^ ^^^^^^^^^ - = note: required for the cast to the object type `dyn MyDisplay` + = note: required for the cast from `&mut T` to the object type `dyn MyDisplay` error: aborting due to 2 previous errors diff --git a/src/test/ui/associated-types/issue-65774-2.stderr b/src/test/ui/associated-types/issue-65774-2.stderr index 4cef4db4698a7..c22302cdc2626 100644 --- a/src/test/ui/associated-types/issue-65774-2.stderr +++ b/src/test/ui/associated-types/issue-65774-2.stderr @@ -18,7 +18,7 @@ LL | writer.my_write(valref) | ^^^^^^ the trait `MyDisplay` is not implemented for `T` | = help: the trait `MyDisplay` is implemented for `&'a mut T` - = note: required for the cast to the object type `dyn MyDisplay` + = note: required for the cast from `T` to the object type `dyn MyDisplay` error: aborting due to 2 previous errors diff --git a/src/test/ui/async-await/async-block-control-flow-static-semantics.stderr b/src/test/ui/async-await/async-block-control-flow-static-semantics.stderr index e0818337d2039..e5887689690e7 100644 --- a/src/test/ui/async-await/async-block-control-flow-static-semantics.stderr +++ b/src/test/ui/async-await/async-block-control-flow-static-semantics.stderr @@ -37,7 +37,7 @@ error[E0271]: type mismatch resolving ` as Future>::Out LL | let _: &dyn Future = █ | ^^^^^^ expected `()`, found `u8` | - = note: required for the cast to the object type `dyn Future` + = note: required for the cast from `impl Future` to the object type `dyn Future` error[E0308]: mismatched types --> $DIR/async-block-control-flow-static-semantics.rs:12:43 @@ -53,7 +53,7 @@ error[E0271]: type mismatch resolving ` as Future>::Out LL | let _: &dyn Future = █ | ^^^^^^ expected `()`, found `u8` | - = note: required for the cast to the object type `dyn Future` + = note: required for the cast from `impl Future` to the object type `dyn Future` error[E0308]: mismatched types --> $DIR/async-block-control-flow-static-semantics.rs:47:44 diff --git a/src/test/ui/async-await/issue-86507.stderr b/src/test/ui/async-await/issue-86507.stderr index 5bbc20359c64a..0e21dba980deb 100644 --- a/src/test/ui/async-await/issue-86507.stderr +++ b/src/test/ui/async-await/issue-86507.stderr @@ -13,7 +13,7 @@ note: captured value is not `Send` because `&` references cannot be sent unless | LL | let x = x; | ^ has type `&T` which is not `Send`, because `T` is not `Sync` - = note: required for the cast to the object type `dyn Future + Send` + = note: required for the cast from `impl Future` to the object type `dyn Future + Send` help: consider further restricting this bound | LL | fn bar<'me, 'async_trait, T: Send + std::marker::Sync>(x: &'me T) diff --git a/src/test/ui/coercion/coerce-issue-49593-box-never.nofallback.stderr b/src/test/ui/coercion/coerce-issue-49593-box-never.nofallback.stderr index fcd2d7f78ff75..322681b97bccb 100644 --- a/src/test/ui/coercion/coerce-issue-49593-box-never.nofallback.stderr +++ b/src/test/ui/coercion/coerce-issue-49593-box-never.nofallback.stderr @@ -4,7 +4,7 @@ error[E0277]: the trait bound `(): std::error::Error` is not satisfied LL | /* *mut $0 is coerced to Box here */ Box::<_ /* ! */>::new(x) | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::error::Error` is not implemented for `()` | - = note: required for the cast to the object type `dyn std::error::Error` + = note: required for the cast from `()` to the object type `dyn std::error::Error` error[E0277]: the trait bound `(): std::error::Error` is not satisfied --> $DIR/coerce-issue-49593-box-never.rs:23:49 @@ -12,7 +12,7 @@ error[E0277]: the trait bound `(): std::error::Error` is not satisfied LL | /* *mut $0 is coerced to *mut Error here */ raw_ptr_box::<_ /* ! */>(x) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::error::Error` is not implemented for `()` | - = note: required for the cast to the object type `(dyn std::error::Error + 'static)` + = note: required for the cast from `()` to the object type `(dyn std::error::Error + 'static)` error: aborting due to 2 previous errors diff --git a/src/test/ui/const-generics/defaults/trait_objects_fail.stderr b/src/test/ui/const-generics/defaults/trait_objects_fail.stderr index 60dc96f675a8b..da85b2059f0a3 100644 --- a/src/test/ui/const-generics/defaults/trait_objects_fail.stderr +++ b/src/test/ui/const-generics/defaults/trait_objects_fail.stderr @@ -7,7 +7,7 @@ LL | foo(&10_u32); | required by a bound introduced by this call | = help: the trait `Trait<2_u8>` is implemented for `u32` - = note: required for the cast to the object type `dyn Trait` + = note: required for the cast from `u32` to the object type `dyn Trait` error[E0277]: the trait bound `bool: Traitor<{_: u8}>` is not satisfied --> $DIR/trait_objects_fail.rs:28:9 @@ -18,7 +18,7 @@ LL | bar(&true); | required by a bound introduced by this call | = help: the trait `Traitor<2_u8, 3_u8>` is implemented for `bool` - = note: required for the cast to the object type `dyn Traitor<{_: u8}>` + = note: required for the cast from `bool` to the object type `dyn Traitor<{_: u8}>` error: aborting due to 2 previous errors diff --git a/src/test/ui/custom_test_frameworks/mismatch.stderr b/src/test/ui/custom_test_frameworks/mismatch.stderr index e848ddc55b7df..61061ae529d12 100644 --- a/src/test/ui/custom_test_frameworks/mismatch.stderr +++ b/src/test/ui/custom_test_frameworks/mismatch.stderr @@ -6,7 +6,7 @@ LL | #[test] LL | fn wrong_kind(){} | ^^^^^^^^^^^^^^^^^ the trait `Testable` is not implemented for `TestDescAndFn` | - = note: required for the cast to the object type `dyn Testable` + = note: required for the cast from `TestDescAndFn` to the object type `dyn Testable` = note: this error originates in the attribute macro `test` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to previous error diff --git a/src/test/ui/dst/dst-bad-coerce1.stderr b/src/test/ui/dst/dst-bad-coerce1.stderr index 121c76a01a5de..594acff853a0e 100644 --- a/src/test/ui/dst/dst-bad-coerce1.stderr +++ b/src/test/ui/dst/dst-bad-coerce1.stderr @@ -15,7 +15,7 @@ error[E0277]: the trait bound `Foo: Bar` is not satisfied LL | let f3: &Fat = f2; | ^^ the trait `Bar` is not implemented for `Foo` | - = note: required for the cast to the object type `dyn Bar` + = note: required for the cast from `Foo` to the object type `dyn Bar` error[E0308]: mismatched types --> $DIR/dst-bad-coerce1.rs:28:27 @@ -34,7 +34,7 @@ error[E0277]: the trait bound `Foo: Bar` is not satisfied LL | let f3: &(dyn Bar,) = f2; | ^^ the trait `Bar` is not implemented for `Foo` | - = note: required for the cast to the object type `dyn Bar` + = note: required for the cast from `Foo` to the object type `dyn Bar` error: aborting due to 4 previous errors diff --git a/src/test/ui/dst/dst-object-from-unsized-type.stderr b/src/test/ui/dst/dst-object-from-unsized-type.stderr index 5bd47736626da..e24c96ebed633 100644 --- a/src/test/ui/dst/dst-object-from-unsized-type.stderr +++ b/src/test/ui/dst/dst-object-from-unsized-type.stderr @@ -6,7 +6,7 @@ LL | fn test1(t: &T) { LL | let u: &dyn Foo = t; | ^ doesn't have a size known at compile-time | - = note: required for the cast to the object type `dyn Foo` + = note: required for the cast from `T` to the object type `dyn Foo` help: consider removing the `?Sized` bound to make the type parameter `Sized` | LL - fn test1(t: &T) { @@ -21,7 +21,7 @@ LL | fn test2(t: &T) { LL | let v: &dyn Foo = t as &dyn Foo; | ^ doesn't have a size known at compile-time | - = note: required for the cast to the object type `dyn Foo` + = note: required for the cast from `T` to the object type `dyn Foo` help: consider removing the `?Sized` bound to make the type parameter `Sized` | LL - fn test2(t: &T) { @@ -35,7 +35,7 @@ LL | let _: &[&dyn Foo] = &["hi"]; | ^^^^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `str` - = note: required for the cast to the object type `dyn Foo` + = note: required for the cast from `str` to the object type `dyn Foo` error[E0277]: the size for values of type `[u8]` cannot be known at compilation time --> $DIR/dst-object-from-unsized-type.rs:23:23 @@ -44,7 +44,7 @@ LL | let _: &dyn Foo = x as &dyn Foo; | ^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `[u8]` - = note: required for the cast to the object type `dyn Foo` + = note: required for the cast from `[u8]` to the object type `dyn Foo` error: aborting due to 4 previous errors diff --git a/src/test/ui/generic-associated-types/issue-79422.extended.stderr b/src/test/ui/generic-associated-types/issue-79422.extended.stderr index 9478fc8979211..9bcbd74716845 100644 --- a/src/test/ui/generic-associated-types/issue-79422.extended.stderr +++ b/src/test/ui/generic-associated-types/issue-79422.extended.stderr @@ -27,7 +27,7 @@ LL | type VRefCont<'a> = &'a V where Self: 'a; | ^^^^^ = note: expected trait object `(dyn RefCont<'_, u8> + 'static)` found reference `&u8` - = note: required for the cast to the object type `dyn MapLike + 'static)>` + = note: required for the cast from `BTreeMap` to the object type `dyn MapLike + 'static)>` error: aborting due to 2 previous errors diff --git a/src/test/ui/issues/issue-14366.stderr b/src/test/ui/issues/issue-14366.stderr index b96b07c91a1fe..10a73b245ac57 100644 --- a/src/test/ui/issues/issue-14366.stderr +++ b/src/test/ui/issues/issue-14366.stderr @@ -5,7 +5,7 @@ LL | let _x = "test" as &dyn (::std::any::Any); | ^^^^^^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `str` - = note: required for the cast to the object type `dyn Any` + = note: required for the cast from `str` to the object type `dyn Any` help: consider borrowing the value, since `&str` can be coerced into `dyn Any` | LL | let _x = &"test" as &dyn (::std::any::Any); diff --git a/src/test/ui/issues/issue-22034.stderr b/src/test/ui/issues/issue-22034.stderr index edcd21ebd6b9b..b32de5b24b924 100644 --- a/src/test/ui/issues/issue-22034.stderr +++ b/src/test/ui/issues/issue-22034.stderr @@ -6,7 +6,7 @@ LL | &mut *(ptr as *mut dyn Fn()) | = help: the trait `Fn<()>` is not implemented for `()` = note: wrap the `()` in a closure with no arguments: `|| { /* code */ }` - = note: required for the cast to the object type `dyn Fn()` + = note: required for the cast from `()` to the object type `dyn Fn()` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-22872.stderr b/src/test/ui/issues/issue-22872.stderr index cd96646d751f2..a84cb7d8c5922 100644 --- a/src/test/ui/issues/issue-22872.stderr +++ b/src/test/ui/issues/issue-22872.stderr @@ -10,7 +10,7 @@ note: required because of the requirements on the impl of `for<'b> Wrap<'b>` for | LL | impl<'b, P> Wrap<'b> for Wrapper

| ^^^^^^^^ ^^^^^^^^^^ - = note: required for the cast to the object type `dyn for<'b> Wrap<'b>` + = note: required for the cast from `Wrapper

` to the object type `dyn for<'b> Wrap<'b>` help: consider further restricting the associated type | LL | fn push_process

(process: P) where P: Process<'static>,

>::Item: Iterator { diff --git a/src/test/ui/issues/issue-7013.stderr b/src/test/ui/issues/issue-7013.stderr index 98ed67507b1d8..f6cb1cbdc11c6 100644 --- a/src/test/ui/issues/issue-7013.stderr +++ b/src/test/ui/issues/issue-7013.stderr @@ -11,7 +11,7 @@ note: required because it appears within the type `B` | LL | struct B { | ^ - = note: required for the cast to the object type `dyn Foo + Send` + = note: required for the cast from `B` to the object type `dyn Foo + Send` error: aborting due to previous error diff --git a/src/test/ui/kindck/kindck-impl-type-params.stderr b/src/test/ui/kindck/kindck-impl-type-params.stderr index 32759d2fa0ebd..902349135c549 100644 --- a/src/test/ui/kindck/kindck-impl-type-params.stderr +++ b/src/test/ui/kindck/kindck-impl-type-params.stderr @@ -9,7 +9,7 @@ note: required because of the requirements on the impl of `Gettable` for `S Gettable for S {} | ^^^^^^^^^^^ ^^^^ - = note: required for the cast to the object type `dyn Gettable` + = note: required for the cast from `S` to the object type `dyn Gettable` help: consider restricting type parameter `T` | LL | fn f(val: T) { @@ -26,7 +26,7 @@ note: required because of the requirements on the impl of `Gettable` for `S Gettable for S {} | ^^^^^^^^^^^ ^^^^ - = note: required for the cast to the object type `dyn Gettable` + = note: required for the cast from `S` to the object type `dyn Gettable` help: consider restricting type parameter `T` | LL | fn f(val: T) { @@ -43,7 +43,7 @@ note: required because of the requirements on the impl of `Gettable` for `S Gettable for S {} | ^^^^^^^^^^^ ^^^^ - = note: required for the cast to the object type `dyn Gettable` + = note: required for the cast from `S` to the object type `dyn Gettable` help: consider restricting type parameter `T` | LL | fn g(val: T) { @@ -60,7 +60,7 @@ note: required because of the requirements on the impl of `Gettable` for `S Gettable for S {} | ^^^^^^^^^^^ ^^^^ - = note: required for the cast to the object type `dyn Gettable` + = note: required for the cast from `S` to the object type `dyn Gettable` help: consider restricting type parameter `T` | LL | fn g(val: T) { @@ -78,7 +78,7 @@ note: required because of the requirements on the impl of `Gettable` for | LL | impl Gettable for S {} | ^^^^^^^^^^^ ^^^^ - = note: required for the cast to the object type `dyn Gettable` + = note: required for the cast from `S` to the object type `dyn Gettable` error[E0277]: the trait bound `Foo: Copy` is not satisfied --> $DIR/kindck-impl-type-params.rs:43:37 @@ -92,7 +92,7 @@ note: required because of the requirements on the impl of `Gettable` for `S | LL | impl Gettable for S {} | ^^^^^^^^^^^ ^^^^ - = note: required for the cast to the object type `dyn Gettable` + = note: required for the cast from `S` to the object type `dyn Gettable` help: consider annotating `Foo` with `#[derive(Copy)]` | LL | #[derive(Copy)] diff --git a/src/test/ui/mismatched_types/cast-rfc0401.stderr b/src/test/ui/mismatched_types/cast-rfc0401.stderr index e63ca6e11de59..eab8e8e80c424 100644 --- a/src/test/ui/mismatched_types/cast-rfc0401.stderr +++ b/src/test/ui/mismatched_types/cast-rfc0401.stderr @@ -220,7 +220,7 @@ LL | let _ = fat_v as *const dyn Foo; | ^^^^^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `[u8]` - = note: required for the cast to the object type `dyn Foo` + = note: required for the cast from `[u8]` to the object type `dyn Foo` help: consider borrowing the value, since `&[u8]` can be coerced into `dyn Foo` | LL | let _ = &fat_v as *const dyn Foo; @@ -233,7 +233,7 @@ LL | let _ = a as *const dyn Foo; | ^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `str` - = note: required for the cast to the object type `dyn Foo` + = note: required for the cast from `str` to the object type `dyn Foo` help: consider borrowing the value, since `&str` can be coerced into `dyn Foo` | LL | let _ = &a as *const dyn Foo; diff --git a/src/test/ui/never_type/fallback-closure-wrap.fallback.stderr b/src/test/ui/never_type/fallback-closure-wrap.fallback.stderr index 78d1a3caf4a30..6b9635d4a60bc 100644 --- a/src/test/ui/never_type/fallback-closure-wrap.fallback.stderr +++ b/src/test/ui/never_type/fallback-closure-wrap.fallback.stderr @@ -10,7 +10,7 @@ LL | | }) as Box); | = note: expected unit type `()` found type `!` - = note: required for the cast to the object type `dyn FnMut()` + = note: required for the cast from `[closure@$DIR/fallback-closure-wrap.rs:18:40: 21:6]` to the object type `dyn FnMut()` error: aborting due to previous error diff --git a/src/test/ui/suggestions/derive-macro-missing-bounds.stderr b/src/test/ui/suggestions/derive-macro-missing-bounds.stderr index 501d083e2bc60..4186dc7cb35ae 100644 --- a/src/test/ui/suggestions/derive-macro-missing-bounds.stderr +++ b/src/test/ui/suggestions/derive-macro-missing-bounds.stderr @@ -33,7 +33,7 @@ LL | impl Debug for Inner { | ^^^^^ ^^^^^^^^ = note: 1 redundant requirement hidden = note: required because of the requirements on the impl of `Debug` for `&c::Inner` - = note: required for the cast to the object type `dyn Debug` + = note: required for the cast from `&c::Inner` to the object type `dyn Debug` = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider restricting type parameter `T` | @@ -55,7 +55,7 @@ LL | impl Debug for Inner where T: Debug, T: Trait { | ^^^^^ ^^^^^^^^ = note: 1 redundant requirement hidden = note: required because of the requirements on the impl of `Debug` for `&d::Inner` - = note: required for the cast to the object type `dyn Debug` + = note: required for the cast from `&d::Inner` to the object type `dyn Debug` = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider restricting type parameter `T` | @@ -77,7 +77,7 @@ LL | impl Debug for Inner where T: Debug + Trait { | ^^^^^ ^^^^^^^^ = note: 1 redundant requirement hidden = note: required because of the requirements on the impl of `Debug` for `&e::Inner` - = note: required for the cast to the object type `dyn Debug` + = note: required for the cast from `&e::Inner` to the object type `dyn Debug` = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider restricting type parameter `T` | @@ -99,7 +99,7 @@ LL | impl Debug for Inner where T: Trait { | ^^^^^ ^^^^^^^^ = note: 1 redundant requirement hidden = note: required because of the requirements on the impl of `Debug` for `&f::Inner` - = note: required for the cast to the object type `dyn Debug` + = note: required for the cast from `&f::Inner` to the object type `dyn Debug` = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider restricting type parameter `T` | diff --git a/src/test/ui/suggestions/suggest-borrow-to-dyn-object.stderr b/src/test/ui/suggestions/suggest-borrow-to-dyn-object.stderr index 8961f4275a283..6b6e406130ec2 100644 --- a/src/test/ui/suggestions/suggest-borrow-to-dyn-object.stderr +++ b/src/test/ui/suggestions/suggest-borrow-to-dyn-object.stderr @@ -8,7 +8,7 @@ LL | check(s); | = help: within `OsStr`, the trait `Sized` is not implemented for `[u8]` = note: required because it appears within the type `OsStr` - = note: required for the cast to the object type `dyn AsRef` + = note: required for the cast from `OsStr` to the object type `dyn AsRef` help: consider borrowing the value, since `&OsStr` can be coerced into `dyn AsRef` | LL | check(&s); diff --git a/src/test/ui/traits/coercion-generic-bad.stderr b/src/test/ui/traits/coercion-generic-bad.stderr index b213ee635df59..93d6770eb47d1 100644 --- a/src/test/ui/traits/coercion-generic-bad.stderr +++ b/src/test/ui/traits/coercion-generic-bad.stderr @@ -5,7 +5,7 @@ LL | let s: Box> = Box::new(Struct { person: "Fred" }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `Struct` | = help: the trait `Trait<&'static str>` is implemented for `Struct` - = note: required for the cast to the object type `dyn Trait` + = note: required for the cast from `Struct` to the object type `dyn Trait` error: aborting due to previous error diff --git a/src/test/ui/traits/map-types.stderr b/src/test/ui/traits/map-types.stderr index a4686edb71757..f685c50b07d5b 100644 --- a/src/test/ui/traits/map-types.stderr +++ b/src/test/ui/traits/map-types.stderr @@ -5,7 +5,7 @@ LL | let y: Box> = Box::new(x); | ^^^^^^^^^^^ the trait `Map` is not implemented for `Box>` | = help: the trait `Map` is implemented for `HashMap` - = note: required for the cast to the object type `dyn Map` + = note: required for the cast from `Box>` to the object type `dyn Map` error: aborting due to previous error diff --git a/src/test/ui/traits/trait-upcasting/type-checking-test-1.stderr b/src/test/ui/traits/trait-upcasting/type-checking-test-1.stderr index 44f32e0cec91c..3985372119e88 100644 --- a/src/test/ui/traits/trait-upcasting/type-checking-test-1.stderr +++ b/src/test/ui/traits/trait-upcasting/type-checking-test-1.stderr @@ -15,7 +15,7 @@ error[E0277]: the trait bound `&dyn Foo: Bar<_>` is not satisfied LL | let _ = x as &dyn Bar<_>; // Ambiguous | ^ the trait `Bar<_>` is not implemented for `&dyn Foo` | - = note: required for the cast to the object type `dyn Bar<_>` + = note: required for the cast from `&dyn Foo` to the object type `dyn Bar<_>` error: aborting due to 2 previous errors diff --git a/src/test/ui/traits/trait-upcasting/type-checking-test-2.stderr b/src/test/ui/traits/trait-upcasting/type-checking-test-2.stderr index 4ae4c8552c161..93c71f54eb53a 100644 --- a/src/test/ui/traits/trait-upcasting/type-checking-test-2.stderr +++ b/src/test/ui/traits/trait-upcasting/type-checking-test-2.stderr @@ -15,7 +15,7 @@ error[E0277]: the trait bound `&dyn Foo: Bar` is not satisfied LL | let _ = x as &dyn Bar; // Error | ^ the trait `Bar` is not implemented for `&dyn Foo` | - = note: required for the cast to the object type `dyn Bar` + = note: required for the cast from `&dyn Foo` to the object type `dyn Bar` error[E0605]: non-primitive cast: `&dyn Foo` as `&dyn Bar<_>` --> $DIR/type-checking-test-2.rs:26:13 @@ -34,7 +34,7 @@ error[E0277]: the trait bound `&dyn Foo: Bar<_>` is not satisfied LL | let a = x as &dyn Bar<_>; // Ambiguous | ^ the trait `Bar<_>` is not implemented for `&dyn Foo` | - = note: required for the cast to the object type `dyn Bar<_>` + = note: required for the cast from `&dyn Foo` to the object type `dyn Bar<_>` error: aborting due to 4 previous errors diff --git a/src/test/ui/unsized/unsized-fn-param.stderr b/src/test/ui/unsized/unsized-fn-param.stderr index 3eecca0fa09d9..b477260543258 100644 --- a/src/test/ui/unsized/unsized-fn-param.stderr +++ b/src/test/ui/unsized/unsized-fn-param.stderr @@ -5,7 +5,7 @@ LL | foo11("bar", &"baz"); | ^^^^^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `str` - = note: required for the cast to the object type `dyn AsRef` + = note: required for the cast from `str` to the object type `dyn AsRef` help: consider borrowing the value, since `&str` can be coerced into `dyn AsRef` | LL | foo11(&"bar", &"baz"); @@ -18,7 +18,7 @@ LL | foo12(&"bar", "baz"); | ^^^^^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `str` - = note: required for the cast to the object type `dyn AsRef` + = note: required for the cast from `str` to the object type `dyn AsRef` help: consider borrowing the value, since `&str` can be coerced into `dyn AsRef` | LL | foo12(&"bar", &"baz"); @@ -31,7 +31,7 @@ LL | foo21("bar", &"baz"); | ^^^^^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `str` - = note: required for the cast to the object type `dyn AsRef` + = note: required for the cast from `str` to the object type `dyn AsRef` help: consider borrowing the value, since `&str` can be coerced into `dyn AsRef` | LL | foo21(&"bar", &"baz"); @@ -44,7 +44,7 @@ LL | foo22(&"bar", "baz"); | ^^^^^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `str` - = note: required for the cast to the object type `dyn AsRef` + = note: required for the cast from `str` to the object type `dyn AsRef` help: consider borrowing the value, since `&str` can be coerced into `dyn AsRef` | LL | foo22(&"bar", &"baz"); From 1e7ab0bbd772d4dab2a66a72af20f8b207c252ab Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Mon, 20 Jun 2022 16:29:05 +0900 Subject: [PATCH 03/16] point at private fields in struct literal --- Cargo.lock | 1 + compiler/rustc_typeck/Cargo.toml | 1 + compiler/rustc_typeck/src/check/expr.rs | 80 ++++++++++++++++--- src/test/ui/issues/issue-76077.rs | 2 +- src/test/ui/issues/issue-76077.stderr | 8 +- src/test/ui/privacy/issue-79593.rs | 2 +- src/test/ui/privacy/issue-79593.stderr | 8 +- ...7872-missing-inaccessible-field-literal.rs | 2 +- ...-missing-inaccessible-field-literal.stderr | 8 +- ...issing-private-fields-in-struct-literal.rs | 18 +++++ ...ng-private-fields-in-struct-literal.stderr | 23 ++++++ 11 files changed, 134 insertions(+), 19 deletions(-) create mode 100644 src/test/ui/typeck/missing-private-fields-in-struct-literal.rs create mode 100644 src/test/ui/typeck/missing-private-fields-in-struct-literal.stderr diff --git a/Cargo.lock b/Cargo.lock index df6f46f26cf0d..cc0f4cc53e656 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4590,6 +4590,7 @@ dependencies = [ name = "rustc_typeck" version = "0.0.0" dependencies = [ + "itertools", "rustc_arena", "rustc_ast", "rustc_attr", diff --git a/compiler/rustc_typeck/Cargo.toml b/compiler/rustc_typeck/Cargo.toml index c08023ee6a70a..b3dd695508094 100644 --- a/compiler/rustc_typeck/Cargo.toml +++ b/compiler/rustc_typeck/Cargo.toml @@ -10,6 +10,7 @@ doctest = false [dependencies] rustc_arena = { path = "../rustc_arena" } tracing = "0.1" +itertools = "0.10.1" rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } rustc_attr = { path = "../rustc_attr" } diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index dc9d76160c4e9..0a017de80f248 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -23,13 +23,14 @@ use crate::type_error_struct; use super::suggest_call_constructor; use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive}; +use itertools::{Either, Itertools}; use rustc_ast as ast; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_errors::Diagnostic; -use rustc_errors::EmissionGuarantee; -use rustc_errors::ErrorGuaranteed; -use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, DiagnosticId}; +use rustc_errors::{ + pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, DiagnosticId, + EmissionGuarantee, ErrorGuaranteed, MultiSpan, +}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; @@ -1672,12 +1673,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; self.typeck_results.borrow_mut().fru_field_types_mut().insert(expr_id, fru_tys); } else if adt_kind != AdtKind::Union && !remaining_fields.is_empty() { - let inaccessible_remaining_fields = remaining_fields.iter().any(|(_, (_, field))| { - !field.vis.is_accessible_from(tcx.parent_module(expr_id).to_def_id(), tcx) - }); + debug!(?remaining_fields); + let private_fields: Vec<&ty::FieldDef> = variant + .fields + .iter() + .filter(|field| { + !field.vis.is_accessible_from(tcx.parent_module(expr_id).to_def_id(), tcx) + }) + .collect(); - if inaccessible_remaining_fields { - self.report_inaccessible_fields(adt_ty, span); + if !private_fields.is_empty() + && tcx + .visibility(variant.def_id) + .is_accessible_from(tcx.parent_module(expr_id).to_def_id(), tcx) + { + self.report_private_fields(adt_ty, span, private_fields, ast_fields); } else { self.report_missing_fields( adt_ty, @@ -1801,7 +1811,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Report an error for a struct field expression when there are invisible fields. /// /// ```text - /// error: cannot construct `Foo` with struct literal syntax due to inaccessible fields + /// error: cannot construct `Foo` with struct literal syntax due to private fields /// --> src/main.rs:8:5 /// | /// 8 | foo::Foo {}; @@ -1809,13 +1819,57 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// error: aborting due to previous error /// ``` - fn report_inaccessible_fields(&self, adt_ty: Ty<'tcx>, span: Span) { - self.tcx.sess.span_err( + fn report_private_fields( + &self, + adt_ty: Ty<'tcx>, + span: Span, + private_fields: Vec<&ty::FieldDef>, + used_fields: &'tcx [hir::ExprField<'tcx>], + ) { + let field_names = |fields: Vec, len: usize| match &fields + .iter() + .map(|field| field.to_string()) + .collect::>()[..] + { + _ if len > 6 => String::new(), + [name] => format!("`{name}` "), + [names @ .., last] => { + let names = names.iter().map(|name| format!("`{name}`")).collect::>(); + format!("{} and `{last}` ", names.join(", ")) + } + [] => unreachable!(), + }; + + let mut err = self.tcx.sess.struct_span_err( span, &format!( - "cannot construct `{adt_ty}` with struct literal syntax due to inaccessible fields", + "cannot construct `{adt_ty}` with struct literal syntax due to private fields", ), ); + let (used_private_fields, remaining_private_fields): ( + Vec<(Symbol, Span)>, + Vec<(Symbol, Span)>, + ) = private_fields.iter().partition_map(|field| { + match used_fields.iter().find(|used_field| field.name == used_field.ident.name) { + Some(used_field) => Either::Left((field.name, used_field.span)), + None => Either::Right((field.name, self.tcx.def_span(field.did))), + } + }); + let remaining_private_fields_len = remaining_private_fields.len(); + err.span_labels(used_private_fields.iter().map(|(_, span)| *span), "private field"); + err.span_note( + MultiSpan::from_spans(remaining_private_fields.iter().map(|(_, span)| *span).collect()), + format!( + "missing field{s} {names}{are} private", + s = pluralize!(remaining_private_fields_len), + are = pluralize!("is", remaining_private_fields_len), + names = field_names( + remaining_private_fields.iter().map(|(name, _)| *name).collect(), + remaining_private_fields_len + ) + ), + ); + err.emit(); } fn report_unknown_field( diff --git a/src/test/ui/issues/issue-76077.rs b/src/test/ui/issues/issue-76077.rs index 1ecd37de2e14a..2d29093b01b02 100644 --- a/src/test/ui/issues/issue-76077.rs +++ b/src/test/ui/issues/issue-76077.rs @@ -6,5 +6,5 @@ pub mod foo { fn main() { foo::Foo {}; - //~^ ERROR cannot construct `Foo` with struct literal syntax due to inaccessible fields + //~^ ERROR cannot construct `Foo` with struct literal syntax due to private fields } diff --git a/src/test/ui/issues/issue-76077.stderr b/src/test/ui/issues/issue-76077.stderr index d834ec5e0edd2..c70a928f6475a 100644 --- a/src/test/ui/issues/issue-76077.stderr +++ b/src/test/ui/issues/issue-76077.stderr @@ -1,8 +1,14 @@ -error: cannot construct `Foo` with struct literal syntax due to inaccessible fields +error: cannot construct `Foo` with struct literal syntax due to private fields --> $DIR/issue-76077.rs:8:5 | LL | foo::Foo {}; | ^^^^^^^^ + | +note: missing field `you_cant_use_this_field` is private + --> $DIR/issue-76077.rs:3:9 + | +LL | you_cant_use_this_field: bool, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/privacy/issue-79593.rs b/src/test/ui/privacy/issue-79593.rs index b94278bfdd221..39c222f7c3414 100644 --- a/src/test/ui/privacy/issue-79593.rs +++ b/src/test/ui/privacy/issue-79593.rs @@ -16,7 +16,7 @@ mod foo { fn correct() { foo::Pub {}; - //~^ ERROR cannot construct `Pub` with struct literal syntax due to inaccessible fields + //~^ ERROR cannot construct `Pub` with struct literal syntax due to private fields } fn wrong() { diff --git a/src/test/ui/privacy/issue-79593.stderr b/src/test/ui/privacy/issue-79593.stderr index b8c7d4f23a28f..435d4cbf73595 100644 --- a/src/test/ui/privacy/issue-79593.stderr +++ b/src/test/ui/privacy/issue-79593.stderr @@ -10,11 +10,17 @@ error[E0063]: missing field `y` in initializer of `Enum` LL | Enum::Variant { x: () }; | ^^^^^^^^^^^^^ missing `y` -error: cannot construct `Pub` with struct literal syntax due to inaccessible fields +error: cannot construct `Pub` with struct literal syntax due to private fields --> $DIR/issue-79593.rs:18:5 | LL | foo::Pub {}; | ^^^^^^^^ + | +note: missing field `private` is private + --> $DIR/issue-79593.rs:2:22 + | +LL | pub struct Pub { private: () } + | ^^^^^^^^^^^ error[E0063]: missing field `y` in initializer of `Enum` --> $DIR/issue-79593.rs:23:5 diff --git a/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.rs b/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.rs index 3176144133760..326e958aaa94f 100644 --- a/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.rs +++ b/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.rs @@ -7,5 +7,5 @@ pub mod foo { fn main() { foo::Foo {}; - //~^ ERROR cannot construct `Foo` with struct literal syntax due to inaccessible fields + //~^ ERROR cannot construct `Foo` with struct literal syntax due to private fields } diff --git a/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.stderr b/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.stderr index 81b73c00e8600..2ade7aea57b3d 100644 --- a/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.stderr +++ b/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.stderr @@ -1,8 +1,14 @@ -error: cannot construct `Foo` with struct literal syntax due to inaccessible fields +error: cannot construct `Foo` with struct literal syntax due to private fields --> $DIR/issue-87872-missing-inaccessible-field-literal.rs:9:5 | LL | foo::Foo {}; | ^^^^^^^^ + | +note: missing field `you_cant_use_this_field` is private + --> $DIR/issue-87872-missing-inaccessible-field-literal.rs:4:9 + | +LL | you_cant_use_this_field: bool, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/typeck/missing-private-fields-in-struct-literal.rs b/src/test/ui/typeck/missing-private-fields-in-struct-literal.rs new file mode 100644 index 0000000000000..9f1560bfb8dfe --- /dev/null +++ b/src/test/ui/typeck/missing-private-fields-in-struct-literal.rs @@ -0,0 +1,18 @@ +pub mod m { + pub struct S { + pub visible: bool, + a: (), + b: (), + c: (), + d: (), + e: (), + } +} + +fn main() { + let _ = m::S { //~ ERROR cannot construct `S` with struct literal syntax due to private fields + visible: true, + a: (), + b: (), + }; +} diff --git a/src/test/ui/typeck/missing-private-fields-in-struct-literal.stderr b/src/test/ui/typeck/missing-private-fields-in-struct-literal.stderr new file mode 100644 index 0000000000000..eb5f460f868e1 --- /dev/null +++ b/src/test/ui/typeck/missing-private-fields-in-struct-literal.stderr @@ -0,0 +1,23 @@ +error: cannot construct `S` with struct literal syntax due to private fields + --> $DIR/missing-private-fields-in-struct-literal.rs:13:13 + | +LL | let _ = m::S { + | ^^^^ +LL | visible: true, +LL | a: (), + | ----- private field +LL | b: (), + | ----- private field + | +note: missing fields `c`, `d` and `e` are private + --> $DIR/missing-private-fields-in-struct-literal.rs:6:9 + | +LL | c: (), + | ^^^^^ +LL | d: (), + | ^^^^^ +LL | e: (), + | ^^^^^ + +error: aborting due to previous error + From 3f12fa7fda96de6687cdd281affcee4a61c35b80 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 26 Nov 2021 21:20:24 +0100 Subject: [PATCH 04/16] Add support for macro in "jump to def" feature --- src/librustdoc/html/format.rs | 1 + src/librustdoc/html/highlight.rs | 126 +++++++++++++++++++++---- src/librustdoc/html/render/span_map.rs | 71 ++++++++++---- 3 files changed, 162 insertions(+), 36 deletions(-) diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 5baa53d55545f..29a58810036c6 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -519,6 +519,7 @@ impl clean::GenericArgs { } // Possible errors when computing href link source for a `DefId` +#[derive(PartialEq, Eq)] pub(crate) enum HrefError { /// This item is known to rustdoc, but from a crate that does not have documentation generated. /// diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 480728b179790..209172bb98e67 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -5,15 +5,19 @@ //! //! Use the `render_with_highlighting` to highlight some rust code. -use crate::clean::PrimitiveType; +use crate::clean::{ExternalLocation, PrimitiveType}; use crate::html::escape::Escape; use crate::html::render::Context; use std::collections::VecDeque; use std::fmt::{Display, Write}; +use std::iter::once; +use rustc_ast as ast; use rustc_data_structures::fx::FxHashMap; +use rustc_hir::def_id::DefId; use rustc_lexer::{LiteralKind, TokenKind}; +use rustc_metadata::creader::{CStore, LoadedMacro}; use rustc_span::edition::Edition; use rustc_span::symbol::Symbol; use rustc_span::{BytePos, Span, DUMMY_SP}; @@ -99,6 +103,7 @@ fn write_code( ) { // This replace allows to fix how the code source with DOS backline characters is displayed. let src = src.replace("\r\n", "\n"); + let mut closing_tag = ""; Classifier::new( &src, edition, @@ -108,8 +113,8 @@ fn write_code( .highlight(&mut |highlight| { match highlight { Highlight::Token { text, class } => string(out, Escape(text), class, &context_info), - Highlight::EnterSpan { class } => enter_span(out, class), - Highlight::ExitSpan => exit_span(out), + Highlight::EnterSpan { class } => closing_tag = enter_span(out, class, &context_info), + Highlight::ExitSpan => exit_span(out, &closing_tag), }; }); } @@ -129,7 +134,7 @@ enum Class { RefKeyWord, Self_(Span), Op, - Macro, + Macro(Span), MacroNonTerminal, String, Number, @@ -153,7 +158,7 @@ impl Class { Class::RefKeyWord => "kw-2", Class::Self_(_) => "self", Class::Op => "op", - Class::Macro => "macro", + Class::Macro(_) => "macro", Class::MacroNonTerminal => "macro-nonterminal", Class::String => "string", Class::Number => "number", @@ -171,8 +176,22 @@ impl Class { /// a "span" (a tuple representing `(lo, hi)` equivalent of `Span`). fn get_span(self) -> Option { match self { - Self::Ident(sp) | Self::Self_(sp) => Some(sp), - _ => None, + Self::Ident(sp) | Self::Self_(sp) | Self::Macro(sp) => Some(sp), + Self::Comment + | Self::DocComment + | Self::Attribute + | Self::KeyWord + | Self::RefKeyWord + | Self::Op + | Self::MacroNonTerminal + | Self::String + | Self::Number + | Self::Bool + | Self::Lifetime + | Self::PreludeTy + | Self::PreludeVal + | Self::QuestionMark + | Self::Decoration(_) => None, } } } @@ -611,7 +630,7 @@ impl<'a> Classifier<'a> { }, TokenKind::Ident | TokenKind::RawIdent if lookahead == Some(TokenKind::Bang) => { self.in_macro = true; - sink(Highlight::EnterSpan { class: Class::Macro }); + sink(Highlight::EnterSpan { class: Class::Macro(self.new_span(before, text)) }); sink(Highlight::Token { text, class: None }); return; } @@ -658,13 +677,18 @@ impl<'a> Classifier<'a> { /// Called when we start processing a span of text that should be highlighted. /// The `Class` argument specifies how it should be highlighted. -fn enter_span(out: &mut Buffer, klass: Class) { - write!(out, "", klass.as_html()); +fn enter_span( + out: &mut Buffer, + klass: Class, + context_info: &Option>, +) -> &'static str { + string_without_closing_tag(out, "", Some(klass), context_info) + .expect("no closing tag to close wrapper...") } /// Called at the end of a span of highlighted text. -fn exit_span(out: &mut Buffer) { - out.write_str(""); +fn exit_span(out: &mut Buffer, closing_tag: &str) { + out.write_str(closing_tag); } /// Called for a span of text. If the text should be highlighted differently @@ -689,13 +713,28 @@ fn string( klass: Option, context_info: &Option>, ) { + if let Some(closing_tag) = string_without_closing_tag(out, text, klass, context_info) { + out.write_str(closing_tag); + } +} + +fn string_without_closing_tag( + out: &mut Buffer, + text: T, + klass: Option, + context_info: &Option>, +) -> Option<&'static str> { let Some(klass) = klass - else { return write!(out, "{}", text) }; + else { + write!(out, "{}", text); + return None; + }; let Some(def_span) = klass.get_span() else { - write!(out, "{}", klass.as_html(), text); - return; + write!(out, "{}", klass.as_html(), text); + return Some(""); }; + let mut text_s = text.to_string(); if text_s.contains("::") { text_s = text_s.split("::").intersperse("::").fold(String::new(), |mut path, t| { @@ -730,8 +769,17 @@ fn string( .map(|s| format!("{}{}", context_info.root_path, s)), LinkFromSrc::External(def_id) => { format::href_with_root_path(*def_id, context, Some(context_info.root_path)) - .ok() .map(|(url, _, _)| url) + .or_else(|e| { + if e == format::HrefError::NotInExternalCache + && matches!(klass, Class::Macro(_)) + { + Ok(generate_macro_def_id_path(context_info, *def_id)) + } else { + Err(e) + } + }) + .ok() } LinkFromSrc::Primitive(prim) => format::href_with_root_path( PrimitiveType::primitive_locations(context.tcx())[prim], @@ -743,11 +791,51 @@ fn string( } }) { - write!(out, "{}", klass.as_html(), href, text_s); - return; + write!(out, "{}", klass.as_html(), href, text_s); + return Some(""); } } - write!(out, "{}", klass.as_html(), text_s); + write!(out, "{}", klass.as_html(), text_s); + Some("") +} + +/// This function is to get the external macro path because they are not in the cache used n +/// `href_with_root_path`. +fn generate_macro_def_id_path(context_info: &ContextInfo<'_, '_, '_>, def_id: DefId) -> String { + let tcx = context_info.context.shared.tcx; + let crate_name = tcx.crate_name(def_id.krate).to_string(); + let cache = &context_info.context.cache(); + + let relative = tcx.def_path(def_id).data.into_iter().filter_map(|elem| { + // extern blocks have an empty name + let s = elem.data.to_string(); + if !s.is_empty() { Some(s) } else { None } + }); + // Check to see if it is a macro 2.0 or built-in macro + let mut path = if matches!( + CStore::from_tcx(tcx).load_macro_untracked(def_id, tcx.sess), + LoadedMacro::MacroDef(def, _) + if matches!(&def.kind, ast::ItemKind::MacroDef(ast_def) + if !ast_def.macro_rules) + ) { + once(crate_name.clone()).chain(relative).collect() + } else { + vec![crate_name.clone(), relative.last().expect("relative was empty")] + }; + + let url_parts = match cache.extern_locations[&def_id.krate] { + ExternalLocation::Remote(ref s) => vec![s.trim_end_matches('/')], + ExternalLocation::Local => vec![context_info.root_path.trim_end_matches('/'), &crate_name], + ExternalLocation::Unknown => panic!("unknown crate"), + }; + + let last = path.pop().unwrap(); + let last = format!("macro.{}.html", last); + if path.is_empty() { + format!("{}/{}", url_parts.join("/"), last) + } else { + format!("{}/{}/{}", url_parts.join("/"), path.join("/"), last) + } } #[cfg(test)] diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs index 86961dc3bf149..0c60278a82dd9 100644 --- a/src/librustdoc/html/render/span_map.rs +++ b/src/librustdoc/html/render/span_map.rs @@ -8,7 +8,8 @@ use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{ExprKind, HirId, Mod, Node}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::TyCtxt; -use rustc_span::Span; +use rustc_span::hygiene::MacroKind; +use rustc_span::{BytePos, ExpnKind, Span}; use std::path::{Path, PathBuf}; @@ -63,32 +64,59 @@ struct SpanMapVisitor<'tcx> { impl<'tcx> SpanMapVisitor<'tcx> { /// This function is where we handle `hir::Path` elements and add them into the "span map". - fn handle_path(&mut self, path: &rustc_hir::Path<'_>, path_span: Option) { + fn handle_path(&mut self, path: &rustc_hir::Path<'_>) { let info = match path.res { - // FIXME: For now, we only handle `DefKind` if it's not `DefKind::TyParam` or - // `DefKind::Macro`. Would be nice to support them too alongside the other `DefKind` + // FIXME: For now, we handle `DefKind` if it's not a `DefKind::TyParam`. + // Would be nice to support them too alongside the other `DefKind` // (such as primitive types!). - Res::Def(kind, def_id) if kind != DefKind::TyParam => { - if matches!(kind, DefKind::Macro(_)) { - return; - } - Some(def_id) - } + Res::Def(kind, def_id) if kind != DefKind::TyParam => Some(def_id), Res::Local(_) => None, Res::PrimTy(p) => { // FIXME: Doesn't handle "path-like" primitives like arrays or tuples. - let span = path_span.unwrap_or(path.span); - self.matches.insert(span, LinkFromSrc::Primitive(PrimitiveType::from(p))); + self.matches.insert(path.span, LinkFromSrc::Primitive(PrimitiveType::from(p))); return; } Res::Err => return, _ => return, }; if let Some(span) = self.tcx.hir().res_span(path.res) { - self.matches - .insert(path_span.unwrap_or(path.span), LinkFromSrc::Local(clean::Span::new(span))); + self.matches.insert(path.span, LinkFromSrc::Local(clean::Span::new(span))); } else if let Some(def_id) = info { - self.matches.insert(path_span.unwrap_or(path.span), LinkFromSrc::External(def_id)); + self.matches.insert(path.span, LinkFromSrc::External(def_id)); + } + } + + /// Adds the macro call into the span map. Returns `true` if the `span` was inside a macro + /// expansion, whether or not it was added to the span map. + fn handle_macro(&mut self, span: Span) -> bool { + if span.from_expansion() { + let mut data = span.ctxt().outer_expn_data(); + let mut call_site = data.call_site; + while call_site.from_expansion() { + data = call_site.ctxt().outer_expn_data(); + call_site = data.call_site; + } + + if let ExpnKind::Macro(MacroKind::Bang, macro_name) = data.kind { + let link_from_src = if let Some(macro_def_id) = data.macro_def_id { + if macro_def_id.is_local() { + LinkFromSrc::Local(clean::Span::new(data.def_site)) + } else { + LinkFromSrc::External(macro_def_id) + } + } else { + return true; + }; + let new_span = data.call_site; + let macro_name = macro_name.as_str(); + // The "call_site" includes the whole macro with its "arguments". We only want + // the macro name. + let new_span = new_span.with_hi(new_span.lo() + BytePos(macro_name.len() as u32)); + self.matches.insert(new_span, link_from_src); + } + true + } else { + false } } } @@ -101,7 +129,10 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> { } fn visit_path(&mut self, path: &'tcx rustc_hir::Path<'tcx>, _id: HirId) { - self.handle_path(path, None); + if self.handle_macro(path.span) { + return; + } + self.handle_path(path); intravisit::walk_path(self, path); } @@ -143,12 +174,18 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> { ); } } + } else if self.handle_macro(expr.span) { + // We don't want to deeper into the macro. + return; } intravisit::walk_expr(self, expr); } fn visit_use(&mut self, path: &'tcx rustc_hir::Path<'tcx>, id: HirId) { - self.handle_path(path, None); + if self.handle_macro(path.span) { + return; + } + self.handle_path(path); intravisit::walk_use(self, path, id); } } From dda980dec07fb7093a153180f19f967ce55fe1f9 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 18 Jun 2022 15:32:26 +0200 Subject: [PATCH 05/16] Rename ContextInfo into HrefContext --- src/librustdoc/html/highlight.rs | 50 ++++++++++++++++---------------- src/librustdoc/html/sources.rs | 2 +- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 209172bb98e67..4c54b569762c6 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -26,7 +26,7 @@ use super::format::{self, Buffer}; use super::render::LinkFromSrc; /// This type is needed in case we want to render links on items to allow to go to their definition. -pub(crate) struct ContextInfo<'a, 'b, 'c> { +pub(crate) struct HrefContext<'a, 'b, 'c> { pub(crate) context: &'a Context<'b>, /// This span contains the current file we're going through. pub(crate) file_span: Span, @@ -48,7 +48,7 @@ pub(crate) fn render_with_highlighting( tooltip: Option<(Option, &str)>, edition: Edition, extra_content: Option, - context_info: Option>, + href_context: Option>, decoration_info: Option, ) { debug!("highlighting: ================\n{}\n==============", src); @@ -66,7 +66,7 @@ pub(crate) fn render_with_highlighting( } write_header(out, class, extra_content); - write_code(out, src, edition, context_info, decoration_info); + write_code(out, src, edition, href_context, decoration_info); write_footer(out, playground_button); } @@ -89,8 +89,8 @@ fn write_header(out: &mut Buffer, class: Option<&str>, extra_content: Option>, + href_context: Option>, decoration_info: Option, ) { // This replace allows to fix how the code source with DOS backline characters is displayed. @@ -107,13 +107,13 @@ fn write_code( Classifier::new( &src, edition, - context_info.as_ref().map(|c| c.file_span).unwrap_or(DUMMY_SP), + href_context.as_ref().map(|c| c.file_span).unwrap_or(DUMMY_SP), decoration_info, ) .highlight(&mut |highlight| { match highlight { - Highlight::Token { text, class } => string(out, Escape(text), class, &context_info), - Highlight::EnterSpan { class } => closing_tag = enter_span(out, class, &context_info), + Highlight::Token { text, class } => string(out, Escape(text), class, &href_context), + Highlight::EnterSpan { class } => closing_tag = enter_span(out, class, &href_context), Highlight::ExitSpan => exit_span(out, &closing_tag), }; }); @@ -680,9 +680,9 @@ impl<'a> Classifier<'a> { fn enter_span( out: &mut Buffer, klass: Class, - context_info: &Option>, + href_context: &Option>, ) -> &'static str { - string_without_closing_tag(out, "", Some(klass), context_info) + string_without_closing_tag(out, "", Some(klass), href_context) .expect("no closing tag to close wrapper...") } @@ -711,9 +711,9 @@ fn string( out: &mut Buffer, text: T, klass: Option, - context_info: &Option>, + href_context: &Option>, ) { - if let Some(closing_tag) = string_without_closing_tag(out, text, klass, context_info) { + if let Some(closing_tag) = string_without_closing_tag(out, text, klass, href_context) { out.write_str(closing_tag); } } @@ -722,7 +722,7 @@ fn string_without_closing_tag( out: &mut Buffer, text: T, klass: Option, - context_info: &Option>, + href_context: &Option>, ) -> Option<&'static str> { let Some(klass) = klass else { @@ -754,10 +754,10 @@ fn string_without_closing_tag( path }); } - if let Some(context_info) = context_info { + if let Some(href_context) = href_context { if let Some(href) = - context_info.context.shared.span_correspondance_map.get(&def_span).and_then(|href| { - let context = context_info.context; + href_context.context.shared.span_correspondance_map.get(&def_span).and_then(|href| { + let context = href_context.context; // FIXME: later on, it'd be nice to provide two links (if possible) for all items: // one to the documentation page and one to the source definition. // FIXME: currently, external items only generate a link to their documentation, @@ -766,15 +766,15 @@ fn string_without_closing_tag( match href { LinkFromSrc::Local(span) => context .href_from_span(*span, true) - .map(|s| format!("{}{}", context_info.root_path, s)), + .map(|s| format!("{}{}", href_context.root_path, s)), LinkFromSrc::External(def_id) => { - format::href_with_root_path(*def_id, context, Some(context_info.root_path)) + format::href_with_root_path(*def_id, context, Some(href_context.root_path)) .map(|(url, _, _)| url) .or_else(|e| { if e == format::HrefError::NotInExternalCache && matches!(klass, Class::Macro(_)) { - Ok(generate_macro_def_id_path(context_info, *def_id)) + Ok(generate_macro_def_id_path(href_context, *def_id)) } else { Err(e) } @@ -784,7 +784,7 @@ fn string_without_closing_tag( LinkFromSrc::Primitive(prim) => format::href_with_root_path( PrimitiveType::primitive_locations(context.tcx())[prim], context, - Some(context_info.root_path), + Some(href_context.root_path), ) .ok() .map(|(url, _, _)| url), @@ -801,10 +801,10 @@ fn string_without_closing_tag( /// This function is to get the external macro path because they are not in the cache used n /// `href_with_root_path`. -fn generate_macro_def_id_path(context_info: &ContextInfo<'_, '_, '_>, def_id: DefId) -> String { - let tcx = context_info.context.shared.tcx; +fn generate_macro_def_id_path(href_context: &HrefContext<'_, '_, '_>, def_id: DefId) -> String { + let tcx = href_context.context.shared.tcx; let crate_name = tcx.crate_name(def_id.krate).to_string(); - let cache = &context_info.context.cache(); + let cache = &href_context.context.cache(); let relative = tcx.def_path(def_id).data.into_iter().filter_map(|elem| { // extern blocks have an empty name @@ -825,7 +825,7 @@ fn generate_macro_def_id_path(context_info: &ContextInfo<'_, '_, '_>, def_id: De let url_parts = match cache.extern_locations[&def_id.krate] { ExternalLocation::Remote(ref s) => vec![s.trim_end_matches('/')], - ExternalLocation::Local => vec![context_info.root_path.trim_end_matches('/'), &crate_name], + ExternalLocation::Local => vec![href_context.root_path.trim_end_matches('/'), &crate_name], ExternalLocation::Unknown => panic!("unknown crate"), }; diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs index 524c90e1f4d64..bc0a56f2b052f 100644 --- a/src/librustdoc/html/sources.rs +++ b/src/librustdoc/html/sources.rs @@ -299,7 +299,7 @@ pub(crate) fn print_src( None, edition, Some(line_numbers), - Some(highlight::ContextInfo { context, file_span, root_path }), + Some(highlight::HrefContext { context, file_span, root_path }), decoration_info, ); } From f00179521d10357d099b419a73a22f50295220f1 Mon Sep 17 00:00:00 2001 From: klensy Date: Mon, 20 Jun 2022 20:42:58 +0300 Subject: [PATCH 06/16] don't alloc error string if no error emitted --- compiler/rustc_typeck/src/astconv/mod.rs | 65 ++++++++++++------------ 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs index 4641b36aad1d7..1c53618dd7c30 100644 --- a/compiler/rustc_typeck/src/astconv/mod.rs +++ b/compiler/rustc_typeck/src/astconv/mod.rs @@ -2111,14 +2111,24 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { extend: impl Fn(&mut DiagnosticBuilder<'tcx, ErrorGuaranteed>), ) -> bool { let args = segments.clone().flat_map(|segment| segment.args().args); - let types_and_spans: Vec<_> = segments - .clone() - .flat_map(|segment| { - segment.res.and_then(|res| { - if segment.args().args.is_empty() { - None - } else { - Some(( + + let (lt, ty, ct, inf) = + args.clone().fold((false, false, false, false), |(lt, ty, ct, inf), arg| match arg { + hir::GenericArg::Lifetime(_) => (true, ty, ct, inf), + hir::GenericArg::Type(_) => (lt, true, ct, inf), + hir::GenericArg::Const(_) => (lt, ty, true, inf), + hir::GenericArg::Infer(_) => (lt, ty, ct, true), + }); + let mut emitted = false; + if lt || ty || ct || inf { + let types_and_spans: Vec<_> = segments + .clone() + .flat_map(|segment| { + segment.res.and_then(|res| { + if segment.args().args.is_empty() { + None + } else { + Some(( match res { Res::PrimTy(ty) => format!("{} `{}`", res.descr(), ty.name()), Res::Def(_, def_id) @@ -2130,32 +2140,23 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { }, segment.ident.span, )) - } + } + }) }) - }) - .collect(); - let this_type = match &types_and_spans[..] { - [.., _, (last, _)] => format!( - "{} and {last}", - types_and_spans[..types_and_spans.len() - 1] - .iter() - .map(|(x, _)| x.as_str()) - .intersperse(&", ") - .collect::() - ), - [(only, _)] => only.to_string(), - [] => "this type".to_string(), - }; + .collect(); + let this_type = match &types_and_spans[..] { + [.., _, (last, _)] => format!( + "{} and {last}", + types_and_spans[..types_and_spans.len() - 1] + .iter() + .map(|(x, _)| x.as_str()) + .intersperse(&", ") + .collect::() + ), + [(only, _)] => only.to_string(), + [] => "this type".to_string(), + }; - let (lt, ty, ct, inf) = - args.clone().fold((false, false, false, false), |(lt, ty, ct, inf), arg| match arg { - hir::GenericArg::Lifetime(_) => (true, ty, ct, inf), - hir::GenericArg::Type(_) => (lt, true, ct, inf), - hir::GenericArg::Const(_) => (lt, ty, true, inf), - hir::GenericArg::Infer(_) => (lt, ty, ct, true), - }); - let mut emitted = false; - if lt || ty || ct || inf { let arg_spans: Vec = args.map(|arg| arg.span()).collect(); let mut kinds = Vec::with_capacity(4); From 810254b31e0135d1f2dfe9e3717c3c0f69676a86 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 18 Jun 2022 15:50:37 +0200 Subject: [PATCH 07/16] Improve code readability and documentation --- src/librustdoc/html/highlight.rs | 80 +++++++++++++++++--------- src/librustdoc/html/render/span_map.rs | 68 +++++++++++++--------- 2 files changed, 94 insertions(+), 54 deletions(-) diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 4c54b569762c6..11ab3a3f931c3 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -103,7 +103,7 @@ fn write_code( ) { // This replace allows to fix how the code source with DOS backline characters is displayed. let src = src.replace("\r\n", "\n"); - let mut closing_tag = ""; + let mut closing_tags: Vec<&'static str> = Vec::new(); Classifier::new( &src, edition, @@ -113,8 +113,12 @@ fn write_code( .highlight(&mut |highlight| { match highlight { Highlight::Token { text, class } => string(out, Escape(text), class, &href_context), - Highlight::EnterSpan { class } => closing_tag = enter_span(out, class, &href_context), - Highlight::ExitSpan => exit_span(out, &closing_tag), + Highlight::EnterSpan { class } => { + closing_tags.push(enter_span(out, class, &href_context)) + } + Highlight::ExitSpan => { + exit_span(out, closing_tags.pop().expect("ExitSpan without EnterSpan")) + } }; }); } @@ -682,8 +686,10 @@ fn enter_span( klass: Class, href_context: &Option>, ) -> &'static str { - string_without_closing_tag(out, "", Some(klass), href_context) - .expect("no closing tag to close wrapper...") + string_without_closing_tag(out, "", Some(klass), href_context).expect( + "internal error: enter_span was called with Some(klass) but did not return a \ + closing HTML tag", + ) } /// Called at the end of a span of highlighted text. @@ -718,6 +724,15 @@ fn string( } } +/// This function writes `text` into `out` with some modifications depending on `klass`: +/// +/// * If `klass` is `None`, `text` is written into `out` with no modification. +/// * If `klass` is `Some` but `klass.get_span()` is `None`, it writes the text wrapped in a +/// `` with the provided `klass`. +/// * If `klass` is `Some` and has a [`rustc_span::Span`], it then tries to generate a link (`` +/// element) by retrieving the link information from the `span_correspondance_map` that was filled +/// in `span_map.rs::collect_spans_and_sources`. If it cannot retrieve the information, then it's +/// the same as the second point (`klass` is `Some` but doesn't have a [`rustc_span::Span`]). fn string_without_closing_tag( out: &mut Buffer, text: T, @@ -799,42 +814,55 @@ fn string_without_closing_tag( Some("") } -/// This function is to get the external macro path because they are not in the cache used n +/// This function is to get the external macro path because they are not in the cache used in /// `href_with_root_path`. fn generate_macro_def_id_path(href_context: &HrefContext<'_, '_, '_>, def_id: DefId) -> String { let tcx = href_context.context.shared.tcx; let crate_name = tcx.crate_name(def_id.krate).to_string(); - let cache = &href_context.context.cache(); + let cache = href_context.context.cache(); let relative = tcx.def_path(def_id).data.into_iter().filter_map(|elem| { // extern blocks have an empty name let s = elem.data.to_string(); if !s.is_empty() { Some(s) } else { None } }); - // Check to see if it is a macro 2.0 or built-in macro - let mut path = if matches!( - CStore::from_tcx(tcx).load_macro_untracked(def_id, tcx.sess), - LoadedMacro::MacroDef(def, _) - if matches!(&def.kind, ast::ItemKind::MacroDef(ast_def) - if !ast_def.macro_rules) - ) { + // Check to see if it is a macro 2.0 or built-in macro. + // More information in . + let is_macro_2 = match CStore::from_tcx(tcx).load_macro_untracked(def_id, tcx.sess) { + LoadedMacro::MacroDef(def, _) => { + // If `ast_def.macro_rules` is `true`, then it's not a macro 2.0. + matches!(&def.kind, ast::ItemKind::MacroDef(ast_def) if !ast_def.macro_rules) + } + _ => false, + }; + + let mut path = if is_macro_2 { once(crate_name.clone()).chain(relative).collect() } else { - vec![crate_name.clone(), relative.last().expect("relative was empty")] + vec![crate_name.clone(), relative.last().unwrap()] }; + if path.len() < 2 { + // The minimum we can have is the crate name followed by the macro name. If shorter, then + // it means that that `relative` was empty, which is an error. + panic!("macro path cannot be empty!"); + } - let url_parts = match cache.extern_locations[&def_id.krate] { - ExternalLocation::Remote(ref s) => vec![s.trim_end_matches('/')], - ExternalLocation::Local => vec![href_context.root_path.trim_end_matches('/'), &crate_name], - ExternalLocation::Unknown => panic!("unknown crate"), - }; + if let Some(last) = path.last_mut() { + *last = format!("macro.{}.html", last); + } - let last = path.pop().unwrap(); - let last = format!("macro.{}.html", last); - if path.is_empty() { - format!("{}/{}", url_parts.join("/"), last) - } else { - format!("{}/{}/{}", url_parts.join("/"), path.join("/"), last) + match cache.extern_locations[&def_id.krate] { + ExternalLocation::Remote(ref s) => { + // `ExternalLocation::Remote` always end with a `/`. + format!("{}{}", s, path.join("/")) + } + ExternalLocation::Local => { + // `href_context.root_path` always end with a `/`. + format!("{}{}/{}", href_context.root_path, crate_name, path.join("/")) + } + ExternalLocation::Unknown => { + panic!("crate {} not in cache when linkifying macros", crate_name) + } } } diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs index 0c60278a82dd9..34d590fb2448c 100644 --- a/src/librustdoc/html/render/span_map.rs +++ b/src/librustdoc/html/render/span_map.rs @@ -88,36 +88,48 @@ impl<'tcx> SpanMapVisitor<'tcx> { /// Adds the macro call into the span map. Returns `true` if the `span` was inside a macro /// expansion, whether or not it was added to the span map. + /// + /// The idea for the macro support is to check if the current `Span` comes from expansion. If + /// so, we loop until we find the macro definition by using `outer_expn_data` in a loop. + /// Finally, we get the information about the macro itself (`span` if "local", `DefId` + /// otherwise) and store it inside the span map. fn handle_macro(&mut self, span: Span) -> bool { - if span.from_expansion() { - let mut data = span.ctxt().outer_expn_data(); - let mut call_site = data.call_site; - while call_site.from_expansion() { - data = call_site.ctxt().outer_expn_data(); - call_site = data.call_site; - } + if !span.from_expansion() { + return false; + } + // So if the `span` comes from a macro expansion, we need to get the original + // macro's `DefId`. + let mut data = span.ctxt().outer_expn_data(); + let mut call_site = data.call_site; + // Macros can expand to code containing macros, which will in turn be expanded, etc. + // So the idea here is to "go up" until we're back to code that was generated from + // macro expansion so that we can get the `DefId` of the original macro that was at the + // origin of this expansion. + while call_site.from_expansion() { + data = call_site.ctxt().outer_expn_data(); + call_site = data.call_site; + } - if let ExpnKind::Macro(MacroKind::Bang, macro_name) = data.kind { - let link_from_src = if let Some(macro_def_id) = data.macro_def_id { - if macro_def_id.is_local() { - LinkFromSrc::Local(clean::Span::new(data.def_site)) - } else { - LinkFromSrc::External(macro_def_id) - } - } else { - return true; - }; - let new_span = data.call_site; - let macro_name = macro_name.as_str(); - // The "call_site" includes the whole macro with its "arguments". We only want - // the macro name. - let new_span = new_span.with_hi(new_span.lo() + BytePos(macro_name.len() as u32)); - self.matches.insert(new_span, link_from_src); + let macro_name = match data.kind { + ExpnKind::Macro(MacroKind::Bang, macro_name) => macro_name, + // Even though we don't handle this kind of macro, this `data` still comes from + // expansion so we return `true` so we don't go any deeper in this code. + _ => return true, + }; + let link_from_src = match data.macro_def_id { + Some(macro_def_id) if macro_def_id.is_local() => { + LinkFromSrc::Local(clean::Span::new(data.def_site)) } - true - } else { - false - } + Some(macro_def_id) => LinkFromSrc::External(macro_def_id), + None => return true, + }; + let new_span = data.call_site; + let macro_name = macro_name.as_str(); + // The "call_site" includes the whole macro with its "arguments". We only want + // the macro name. + let new_span = new_span.with_hi(new_span.lo() + BytePos(macro_name.len() as u32)); + self.matches.insert(new_span, link_from_src); + true } } @@ -175,7 +187,7 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> { } } } else if self.handle_macro(expr.span) { - // We don't want to deeper into the macro. + // We don't want to go deeper into the macro. return; } intravisit::walk_expr(self, expr); From f4db07ed4c1ab8a0f7961efc60ec32193e971f5c Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 26 Nov 2021 21:20:45 +0100 Subject: [PATCH 08/16] Add test for macro support in "jump to def" feature --- .../check-source-code-urls-to-def-std.rs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/test/rustdoc/check-source-code-urls-to-def-std.rs b/src/test/rustdoc/check-source-code-urls-to-def-std.rs index b129ceb5b7302..3396b234a77b1 100644 --- a/src/test/rustdoc/check-source-code-urls-to-def-std.rs +++ b/src/test/rustdoc/check-source-code-urls-to-def-std.rs @@ -15,3 +15,28 @@ pub fn foo(a: u32, b: &str, c: String) { let y: bool = true; babar(); } + +macro_rules! yolo { () => {}} + +fn bar(a: i32) {} + +macro_rules! bar { + ($a:ident) => { bar($a) } +} + +macro_rules! data { + ($x:expr) => { $x * 2 } +} + +pub fn another_foo() { + // This is known limitation: if the macro doesn't generate anything, the visitor + // can't find any item or anything that could tell us that it comes from expansion. + // @!has - '//a[@href="../../src/foo/check-source-code-urls-to-def-std.rs.html#19"]' 'yolo!' + yolo!(); + // @has - '//a[@href="{{channel}}/std/macro.eprintln.html"]' 'eprintln!' + eprintln!(); + // @has - '//a[@href="../../src/foo/check-source-code-urls-to-def-std.rs.html#27-29"]' 'data!' + let x = data!(4); + // @has - '//a[@href="../../src/foo/check-source-code-urls-to-def-std.rs.html#23-25"]' 'bar!' + bar!(x); +} From 987c73158e2120ef75b4b7fc46dcd88a621106d8 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 20 Jun 2022 22:32:49 +0200 Subject: [PATCH 09/16] Integrate `generate_macro_def_id_path` into `href_with_root_path` --- src/librustdoc/html/format.rs | 71 +++++++++++++++++++++++++++++++- src/librustdoc/html/highlight.rs | 69 +------------------------------ 2 files changed, 72 insertions(+), 68 deletions(-) diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 29a58810036c6..67b01245ef7ad 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -8,14 +8,16 @@ use std::borrow::Cow; use std::cell::Cell; use std::fmt; -use std::iter; +use std::iter::{self, once}; +use rustc_ast as ast; use rustc_attr::{ConstStability, StabilityLevel}; use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; +use rustc_metadata::creader::{CStore, LoadedMacro}; use rustc_middle::ty; use rustc_middle::ty::DefIdTree; use rustc_middle::ty::TyCtxt; @@ -557,6 +559,71 @@ pub(crate) fn join_with_double_colon(syms: &[Symbol]) -> String { s } +/// This function is to get the external macro path because they are not in the cache used in +/// `href_with_root_path`. +fn generate_macro_def_id_path( + def_id: DefId, + cx: &Context<'_>, + root_path: Option<&str>, +) -> (String, ItemType, Vec) { + let tcx = cx.shared.tcx; + let crate_name = tcx.crate_name(def_id.krate).to_string(); + let cache = cx.cache(); + + let fqp: Vec = tcx + .def_path(def_id) + .data + .into_iter() + .filter_map(|elem| { + // extern blocks (and a few others things) have an empty name. + match elem.data.get_opt_name() { + Some(s) if !s.is_empty() => Some(s), + _ => None, + } + }) + .collect(); + let relative = fqp.iter().map(|elem| elem.to_string()); + // Check to see if it is a macro 2.0 or built-in macro. + // More information in . + let is_macro_2 = match CStore::from_tcx(tcx).load_macro_untracked(def_id, tcx.sess) { + LoadedMacro::MacroDef(def, _) => { + // If `ast_def.macro_rules` is `true`, then it's not a macro 2.0. + matches!(&def.kind, ast::ItemKind::MacroDef(ast_def) if !ast_def.macro_rules) + } + _ => false, + }; + + let mut path = if is_macro_2 { + once(crate_name.clone()).chain(relative).collect() + } else { + vec![crate_name.clone(), relative.last().unwrap()] + }; + if path.len() < 2 { + // The minimum we can have is the crate name followed by the macro name. If shorter, then + // it means that that `relative` was empty, which is an error. + panic!("macro path cannot be empty!"); + } + + if let Some(last) = path.last_mut() { + *last = format!("macro.{}.html", last); + } + + let url = match cache.extern_locations[&def_id.krate] { + ExternalLocation::Remote(ref s) => { + // `ExternalLocation::Remote` always end with a `/`. + format!("{}{}", s, path.join("/")) + } + ExternalLocation::Local => { + // `root_path` always end with a `/`. + format!("{}{}/{}", root_path.unwrap_or(""), crate_name, path.join("/")) + } + ExternalLocation::Unknown => { + panic!("crate {} not in cache when linkifying macros", crate_name) + } + }; + (url, ItemType::Macro, fqp) +} + pub(crate) fn href_with_root_path( did: DefId, cx: &Context<'_>, @@ -612,6 +679,8 @@ pub(crate) fn href_with_root_path( ExternalLocation::Unknown => return Err(HrefError::DocumentationNotBuilt), }, ) + } else if matches!(def_kind, DefKind::Macro(_)) { + return Ok(generate_macro_def_id_path(did, cx, root_path)); } else { return Err(HrefError::NotInExternalCache); } diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 11ab3a3f931c3..d2ef89078bf6d 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -5,19 +5,15 @@ //! //! Use the `render_with_highlighting` to highlight some rust code. -use crate::clean::{ExternalLocation, PrimitiveType}; +use crate::clean::PrimitiveType; use crate::html::escape::Escape; use crate::html::render::Context; use std::collections::VecDeque; use std::fmt::{Display, Write}; -use std::iter::once; -use rustc_ast as ast; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::def_id::DefId; use rustc_lexer::{LiteralKind, TokenKind}; -use rustc_metadata::creader::{CStore, LoadedMacro}; use rustc_span::edition::Edition; use rustc_span::symbol::Symbol; use rustc_span::{BytePos, Span, DUMMY_SP}; @@ -784,17 +780,8 @@ fn string_without_closing_tag( .map(|s| format!("{}{}", href_context.root_path, s)), LinkFromSrc::External(def_id) => { format::href_with_root_path(*def_id, context, Some(href_context.root_path)) - .map(|(url, _, _)| url) - .or_else(|e| { - if e == format::HrefError::NotInExternalCache - && matches!(klass, Class::Macro(_)) - { - Ok(generate_macro_def_id_path(href_context, *def_id)) - } else { - Err(e) - } - }) .ok() + .map(|(url, _, _)| url) } LinkFromSrc::Primitive(prim) => format::href_with_root_path( PrimitiveType::primitive_locations(context.tcx())[prim], @@ -814,57 +801,5 @@ fn string_without_closing_tag( Some("") } -/// This function is to get the external macro path because they are not in the cache used in -/// `href_with_root_path`. -fn generate_macro_def_id_path(href_context: &HrefContext<'_, '_, '_>, def_id: DefId) -> String { - let tcx = href_context.context.shared.tcx; - let crate_name = tcx.crate_name(def_id.krate).to_string(); - let cache = href_context.context.cache(); - - let relative = tcx.def_path(def_id).data.into_iter().filter_map(|elem| { - // extern blocks have an empty name - let s = elem.data.to_string(); - if !s.is_empty() { Some(s) } else { None } - }); - // Check to see if it is a macro 2.0 or built-in macro. - // More information in . - let is_macro_2 = match CStore::from_tcx(tcx).load_macro_untracked(def_id, tcx.sess) { - LoadedMacro::MacroDef(def, _) => { - // If `ast_def.macro_rules` is `true`, then it's not a macro 2.0. - matches!(&def.kind, ast::ItemKind::MacroDef(ast_def) if !ast_def.macro_rules) - } - _ => false, - }; - - let mut path = if is_macro_2 { - once(crate_name.clone()).chain(relative).collect() - } else { - vec![crate_name.clone(), relative.last().unwrap()] - }; - if path.len() < 2 { - // The minimum we can have is the crate name followed by the macro name. If shorter, then - // it means that that `relative` was empty, which is an error. - panic!("macro path cannot be empty!"); - } - - if let Some(last) = path.last_mut() { - *last = format!("macro.{}.html", last); - } - - match cache.extern_locations[&def_id.krate] { - ExternalLocation::Remote(ref s) => { - // `ExternalLocation::Remote` always end with a `/`. - format!("{}{}", s, path.join("/")) - } - ExternalLocation::Local => { - // `href_context.root_path` always end with a `/`. - format!("{}{}/{}", href_context.root_path, crate_name, path.join("/")) - } - ExternalLocation::Unknown => { - panic!("crate {} not in cache when linkifying macros", crate_name) - } - } -} - #[cfg(test)] mod tests; From beb2f364cc85b4408da1d043f875d159003558e4 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 20 Jun 2022 23:31:40 +0200 Subject: [PATCH 10/16] Fix panic by checking if `CStore` has the crate data we want before actually querying it --- compiler/rustc_metadata/src/creader.rs | 4 ++++ src/librustdoc/html/format.rs | 20 ++++++++++++++------ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index 947d563ae3cd1..555db5846edd0 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -133,6 +133,10 @@ impl CStore { CrateNum::new(self.metas.len() - 1) } + pub fn has_crate_data(&self, cnum: CrateNum) -> bool { + self.metas[cnum].is_some() + } + pub(crate) fn get_crate_data(&self, cnum: CrateNum) -> CrateMetadataRef<'_> { let cdata = self.metas[cnum] .as_ref() diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 67b01245ef7ad..056eda089c1de 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -565,7 +565,7 @@ fn generate_macro_def_id_path( def_id: DefId, cx: &Context<'_>, root_path: Option<&str>, -) -> (String, ItemType, Vec) { +) -> Result<(String, ItemType, Vec), HrefError> { let tcx = cx.shared.tcx; let crate_name = tcx.crate_name(def_id.krate).to_string(); let cache = cx.cache(); @@ -583,9 +583,15 @@ fn generate_macro_def_id_path( }) .collect(); let relative = fqp.iter().map(|elem| elem.to_string()); + let cstore = CStore::from_tcx(tcx); + // We need this to prevent a `panic` when this function is used from intra doc links... + if !cstore.has_crate_data(def_id.krate) { + debug!("No data for crate {}", crate_name); + return Err(HrefError::NotInExternalCache); + } // Check to see if it is a macro 2.0 or built-in macro. // More information in . - let is_macro_2 = match CStore::from_tcx(tcx).load_macro_untracked(def_id, tcx.sess) { + let is_macro_2 = match cstore.load_macro_untracked(def_id, tcx.sess) { LoadedMacro::MacroDef(def, _) => { // If `ast_def.macro_rules` is `true`, then it's not a macro 2.0. matches!(&def.kind, ast::ItemKind::MacroDef(ast_def) if !ast_def.macro_rules) @@ -601,7 +607,8 @@ fn generate_macro_def_id_path( if path.len() < 2 { // The minimum we can have is the crate name followed by the macro name. If shorter, then // it means that that `relative` was empty, which is an error. - panic!("macro path cannot be empty!"); + debug!("macro path cannot be empty!"); + return Err(HrefError::NotInExternalCache); } if let Some(last) = path.last_mut() { @@ -618,10 +625,11 @@ fn generate_macro_def_id_path( format!("{}{}/{}", root_path.unwrap_or(""), crate_name, path.join("/")) } ExternalLocation::Unknown => { - panic!("crate {} not in cache when linkifying macros", crate_name) + debug!("crate {} not in cache when linkifying macros", crate_name); + return Err(HrefError::NotInExternalCache); } }; - (url, ItemType::Macro, fqp) + Ok((url, ItemType::Macro, fqp)) } pub(crate) fn href_with_root_path( @@ -680,7 +688,7 @@ pub(crate) fn href_with_root_path( }, ) } else if matches!(def_kind, DefKind::Macro(_)) { - return Ok(generate_macro_def_id_path(did, cx, root_path)); + return generate_macro_def_id_path(did, cx, root_path); } else { return Err(HrefError::NotInExternalCache); } From b37a05bd01a4f1fdcdfda77df4a9008d04236528 Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Mon, 20 Jun 2022 12:41:11 -0700 Subject: [PATCH 11/16] rustdoc: optimize loading of source sidebar The source sidebar has a setting to remember whether it should be open or closed. Previously, this setting was handled in source-script.js, which is loaded with `defer`, meaning it is often run after the document is rendered. Since CSS renders the source sidebar as closed by default, changing this after the initial render results in a relayout. Instead, handle the setting in storage.js, which is the first script to load and is the only script that blocks render. This avoids a relayout and means navigating between files with the sidebar open is faster. --- src/librustdoc/html/static/css/rustdoc.css | 35 ++++++++++++------- .../html/static/js/source-script.js | 10 ++---- src/librustdoc/html/static/js/storage.js | 11 ++++++ src/test/rustdoc-gui/sidebar-source-code.goml | 10 +++--- src/test/rustdoc-gui/source-code-page.goml | 2 +- 5 files changed, 42 insertions(+), 26 deletions(-) diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index d0229bdb5f23c..b4b7790eebba1 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -387,16 +387,20 @@ nav.sub { overflow-y: hidden; } +.rustdoc.source .sidebar .sidebar-logo { + display: none; +} + .source .sidebar > *:not(#sidebar-toggle) { opacity: 0; visibility: hidden; } -.source .sidebar.expanded { +.source-sidebar-expanded .source .sidebar { overflow-y: auto; } -.source .sidebar.expanded > *:not(#sidebar-toggle) { +.source-sidebar-expanded .source .sidebar > *:not(#sidebar-toggle) { opacity: 1; visibility: visible; } @@ -1682,11 +1686,11 @@ details.rustdoc-toggle[open] > summary.hideme::after { /* When we expand the sidebar on the source code page, we hide the logo on the left of the search bar to have more space. */ - .sidebar.expanded + main .width-limiter .sub-logo-container.rust-logo { + .source-sidebar-expanded .source .sidebar + main .width-limiter .sub-logo-container.rust-logo { display: none; } - .source .sidebar.expanded { + .source-sidebar-expanded .source .sidebar { width: 300px; } } @@ -1766,7 +1770,7 @@ details.rustdoc-toggle[open] > summary.hideme::after { } .sidebar.shown, - .sidebar.expanded, + .source-sidebar-expanded .source .sidebar, .sidebar:focus-within { left: 0; } @@ -1889,11 +1893,7 @@ details.rustdoc-toggle[open] > summary.hideme::after { left: -11px; } - .sidebar.expanded #sidebar-toggle { - font-size: 1.5rem; - } - - .sidebar:not(.expanded) #sidebar-toggle { + #sidebar-toggle { position: fixed; left: 1px; top: 100px; @@ -1910,6 +1910,14 @@ details.rustdoc-toggle[open] > summary.hideme::after { border-left: 0; } + .source-sidebar-expanded #sidebar-toggle { + left: unset; + top: unset; + width: unset; + border-top-right-radius: unset; + border-bottom-right-radius: unset; + } + #source-sidebar { z-index: 11; } @@ -1952,7 +1960,7 @@ details.rustdoc-toggle[open] > summary.hideme::after { padding-left: 2em; } - .source .sidebar.expanded { + .source-sidebar-expanded .source .sidebar { max-width: 100vw; width: 100vw; } @@ -2010,9 +2018,12 @@ details.rustdoc-toggle[open] > summary.hideme::after { width: 35px; } - .sidebar:not(.expanded) #sidebar-toggle { + #sidebar-toggle { top: 10px; } + .source-sidebar-expanded #sidebar-toggle { + top: unset; + } } .method-toggle summary, diff --git a/src/librustdoc/html/static/js/source-script.js b/src/librustdoc/html/static/js/source-script.js index 14d8a942977d6..45955c6dd052c 100644 --- a/src/librustdoc/html/static/js/source-script.js +++ b/src/librustdoc/html/static/js/source-script.js @@ -76,14 +76,13 @@ function createDirEntry(elem, parent, fullPath, currentFile, hasFoundFile) { } function toggleSidebar() { - const sidebar = document.querySelector("nav.sidebar"); const child = this.children[0]; if (child.innerText === ">") { - sidebar.classList.add("expanded"); + addClass(document.documentElement, "source-sidebar-expanded"); child.innerText = "<"; updateLocalStorage("source-sidebar-show", "true"); } else { - sidebar.classList.remove("expanded"); + removeClass(document.documentElement, "source-sidebar-expanded"); child.innerText = ">"; updateLocalStorage("source-sidebar-show", "false"); } @@ -119,11 +118,6 @@ function createSourceSidebar() { const sidebar = document.createElement("div"); sidebar.id = "source-sidebar"; - if (getCurrentValue("source-sidebar-show") !== "true") { - container.classList.remove("expanded"); - } else { - container.classList.add("expanded"); - } const currentFile = getCurrentFilePath(); let hasFoundFile = false; diff --git a/src/librustdoc/html/static/js/storage.js b/src/librustdoc/html/static/js/storage.js index 4fcf049923491..1c4c88344888c 100644 --- a/src/librustdoc/html/static/js/storage.js +++ b/src/librustdoc/html/static/js/storage.js @@ -1,3 +1,8 @@ +// storage.js is loaded in the `` of all rustdoc pages and doesn't +// use `async` or `defer`. That means it blocks further parsing and rendering +// of the page: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script. +// This makes it the correct place to act on settings that affect the display of +// the page, so we don't see major layout changes during the load of the page. "use strict"; const darkThemes = ["dark", "ayu"]; @@ -236,6 +241,12 @@ if (getSettingValue("use-system-theme") !== "false" && window.matchMedia) { switchToSavedTheme(); } +if (getSettingValue("source-sidebar-show") === "true") { + // At this point in page load, `document.body` is not available yet. + // Set a class on the `` element instead. + addClass(document.documentElement, "source-sidebar-expanded"); +} + // If we navigate away (for example to a settings page), and then use the back or // forward button to get back to a page, the theme may have changed in the meantime. // But scripts may not be re-loaded in such a case due to the bfcache diff --git a/src/test/rustdoc-gui/sidebar-source-code.goml b/src/test/rustdoc-gui/sidebar-source-code.goml index 8b4a8bd40dd7e..724520bc399d0 100644 --- a/src/test/rustdoc-gui/sidebar-source-code.goml +++ b/src/test/rustdoc-gui/sidebar-source-code.goml @@ -8,12 +8,12 @@ assert-css: ("nav.sidebar", {"width": "50px"}) // We now click on the button to expand the sidebar. click: (10, 10) // We wait for the sidebar to be expanded. -wait-for-css: ("nav.sidebar.expanded", {"width": "300px"}) -assert-css: ("nav.sidebar.expanded a", {"font-size": "14px"}) +wait-for-css: (".source-sidebar-expanded nav.sidebar", {"width": "300px"}) +assert-css: (".source-sidebar-expanded nav.sidebar a", {"font-size": "14px"}) // We collapse the sidebar. click: (10, 10) // We ensure that the class has been removed. -wait-for: "nav.sidebar:not(.expanded)" +wait-for: "html:not(.expanded)" assert: "nav.sidebar" // We now switch to mobile mode. @@ -22,11 +22,11 @@ size: (600, 600) assert-css: ("nav.sidebar", {"width": "1px"}) // We expand the sidebar. click: "#sidebar-toggle" -assert-css: ("nav.sidebar.expanded", {"width": "600px"}) +assert-css: (".source-sidebar-expanded nav.sidebar", {"width": "600px"}) // We collapse the sidebar. click: (10, 10) // We ensure that the class has been removed. -assert-false: "nav.sidebar.expanded" +assert-false: ".source-sidebar-expanded" assert: "nav.sidebar" // Check that the topbar is not visible diff --git a/src/test/rustdoc-gui/source-code-page.goml b/src/test/rustdoc-gui/source-code-page.goml index 188b2605f0f13..b45512601f208 100644 --- a/src/test/rustdoc-gui/source-code-page.goml +++ b/src/test/rustdoc-gui/source-code-page.goml @@ -32,7 +32,7 @@ assert-document-property: ({"URL": "/lib.rs.html"}, ENDS_WITH) // First we "open" it. click: "#sidebar-toggle" -assert: ".sidebar.expanded" +assert: ".source-sidebar-expanded" // We check that the first entry of the sidebar is collapsed (which, for whatever reason, // is number 2 and not 1...). From e900a3549624860a466c33c04742ccfa026bdc6d Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 21 Jun 2022 03:47:25 +0000 Subject: [PATCH 12/16] Give name if anonymous region appears in impl signature --- .../src/diagnostics/outlives_suggestion.rs | 3 +- .../src/diagnostics/region_name.rs | 58 +++++++++++++++++-- src/test/ui/nll/issue-98170.rs | 25 ++++++++ src/test/ui/nll/issue-98170.stderr | 44 ++++++++++++++ 4 files changed, 125 insertions(+), 5 deletions(-) create mode 100644 src/test/ui/nll/issue-98170.rs create mode 100644 src/test/ui/nll/issue-98170.stderr diff --git a/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs b/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs index 9d81330745fe2..d359d7efb6268 100644 --- a/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs +++ b/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs @@ -62,7 +62,8 @@ impl OutlivesSuggestionBuilder { | RegionNameSource::AnonRegionFromUpvar(..) | RegionNameSource::AnonRegionFromOutput(..) | RegionNameSource::AnonRegionFromYieldTy(..) - | RegionNameSource::AnonRegionFromAsyncFn(..) => { + | RegionNameSource::AnonRegionFromAsyncFn(..) + | RegionNameSource::AnonRegionFromImplSignature(..) => { debug!("Region {:?} is NOT suggestable", name); false } diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index d6b5089712ab4..b5ae85fd042c2 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -6,7 +6,7 @@ use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_middle::ty::print::RegionHighlightMode; use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; -use rustc_middle::ty::{self, RegionVid, Ty}; +use rustc_middle::ty::{self, DefIdTree, RegionVid, Ty}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; @@ -45,6 +45,8 @@ pub(crate) enum RegionNameSource { AnonRegionFromYieldTy(Span, String), /// An anonymous region from an async fn. AnonRegionFromAsyncFn(Span), + /// An anonymous region from an impl self type or trait + AnonRegionFromImplSignature(Span, &'static str), } /// Describes what to highlight to explain to the user that we're giving an anonymous region a @@ -75,7 +77,8 @@ impl RegionName { | RegionNameSource::AnonRegionFromUpvar(..) | RegionNameSource::AnonRegionFromOutput(..) | RegionNameSource::AnonRegionFromYieldTy(..) - | RegionNameSource::AnonRegionFromAsyncFn(..) => false, + | RegionNameSource::AnonRegionFromAsyncFn(..) + | RegionNameSource::AnonRegionFromImplSignature(..) => false, } } @@ -87,7 +90,8 @@ impl RegionName { | RegionNameSource::SynthesizedFreeEnvRegion(span, _) | RegionNameSource::AnonRegionFromUpvar(span, _) | RegionNameSource::AnonRegionFromYieldTy(span, _) - | RegionNameSource::AnonRegionFromAsyncFn(span) => Some(span), + | RegionNameSource::AnonRegionFromAsyncFn(span) + | RegionNameSource::AnonRegionFromImplSignature(span, _) => Some(span), RegionNameSource::AnonRegionFromArgument(ref highlight) | RegionNameSource::AnonRegionFromOutput(ref highlight, _) => match *highlight { RegionNameHighlight::MatchedHirTy(span) @@ -166,6 +170,12 @@ impl RegionName { RegionNameSource::AnonRegionFromYieldTy(span, type_name) => { diag.span_label(*span, format!("yield type is {type_name}")); } + RegionNameSource::AnonRegionFromImplSignature(span, location) => { + diag.span_label( + *span, + format!("lifetime `{self}` appears in the `impl`'s {location}"), + ); + } RegionNameSource::Static => {} } } @@ -240,7 +250,8 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { .or_else(|| self.give_name_if_anonymous_region_appears_in_arguments(fr)) .or_else(|| self.give_name_if_anonymous_region_appears_in_upvars(fr)) .or_else(|| self.give_name_if_anonymous_region_appears_in_output(fr)) - .or_else(|| self.give_name_if_anonymous_region_appears_in_yield_ty(fr)); + .or_else(|| self.give_name_if_anonymous_region_appears_in_yield_ty(fr)) + .or_else(|| self.give_name_if_anonymous_region_appears_in_impl_signature(fr)); if let Some(ref value) = value { self.region_names.try_borrow_mut().unwrap().insert(fr, value.clone()); @@ -840,4 +851,43 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { source: RegionNameSource::AnonRegionFromYieldTy(yield_span, type_name), }) } + + fn give_name_if_anonymous_region_appears_in_impl_signature( + &self, + fr: RegionVid, + ) -> Option { + let ty::ReEarlyBound(region) = *self.to_error_region(fr)? else { + return None; + }; + if region.has_name() { + return None; + }; + + let tcx = self.infcx.tcx; + let body_parent_did = tcx.opt_parent(self.mir_def_id().to_def_id())?; + if tcx.parent(region.def_id) != body_parent_did + || tcx.def_kind(body_parent_did) != DefKind::Impl + { + return None; + } + + let mut found = false; + tcx.fold_regions(tcx.type_of(body_parent_did), &mut true, |r: ty::Region<'tcx>, _| { + if *r == ty::ReEarlyBound(region) { + found = true; + } + r + }); + + Some(RegionName { + name: self.synthesize_region_name(), + source: RegionNameSource::AnonRegionFromImplSignature( + tcx.def_span(region.def_id), + // FIXME(compiler-errors): Does this ever actually show up + // anywhere other than the self type? I couldn't create an + // example of a `'_` in the impl's trait being referenceable. + if found { "self type" } else { "header" }, + ), + }) + } } diff --git a/src/test/ui/nll/issue-98170.rs b/src/test/ui/nll/issue-98170.rs new file mode 100644 index 0000000000000..6bb12f52d3f3e --- /dev/null +++ b/src/test/ui/nll/issue-98170.rs @@ -0,0 +1,25 @@ +pub struct MyStruct<'a> { + field: &'a [u32], +} + +impl MyStruct<'_> { + pub fn new<'a>(field: &'a [u32]) -> MyStruct<'a> { + Self { field } + //~^ ERROR lifetime may not live long enough + //~| ERROR lifetime may not live long enough + } +} + +trait Trait<'a> { + fn new(field: &'a [u32]) -> MyStruct<'a>; +} + +impl<'a> Trait<'a> for MyStruct<'_> { + fn new(field: &'a [u32]) -> MyStruct<'a> { + Self { field } + //~^ ERROR lifetime may not live long enough + //~| ERROR lifetime may not live long enough + } +} + +fn main() {} diff --git a/src/test/ui/nll/issue-98170.stderr b/src/test/ui/nll/issue-98170.stderr new file mode 100644 index 0000000000000..0d17365e71b4a --- /dev/null +++ b/src/test/ui/nll/issue-98170.stderr @@ -0,0 +1,44 @@ +error: lifetime may not live long enough + --> $DIR/issue-98170.rs:7:9 + | +LL | impl MyStruct<'_> { + | -- lifetime `'1` appears in the `impl`'s self type +LL | pub fn new<'a>(field: &'a [u32]) -> MyStruct<'a> { + | -- lifetime `'a` defined here +LL | Self { field } + | ^^^^^^^^^^^^^^ associated function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'1` + +error: lifetime may not live long enough + --> $DIR/issue-98170.rs:7:16 + | +LL | impl MyStruct<'_> { + | -- lifetime `'1` appears in the `impl`'s self type +LL | pub fn new<'a>(field: &'a [u32]) -> MyStruct<'a> { + | -- lifetime `'a` defined here +LL | Self { field } + | ^^^^^ this usage requires that `'a` must outlive `'1` + +error: lifetime may not live long enough + --> $DIR/issue-98170.rs:19:9 + | +LL | impl<'a> Trait<'a> for MyStruct<'_> { + | -- -- lifetime `'1` appears in the `impl`'s self type + | | + | lifetime `'a` defined here +LL | fn new(field: &'a [u32]) -> MyStruct<'a> { +LL | Self { field } + | ^^^^^^^^^^^^^^ associated function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'1` + +error: lifetime may not live long enough + --> $DIR/issue-98170.rs:19:16 + | +LL | impl<'a> Trait<'a> for MyStruct<'_> { + | -- -- lifetime `'1` appears in the `impl`'s self type + | | + | lifetime `'a` defined here +LL | fn new(field: &'a [u32]) -> MyStruct<'a> { +LL | Self { field } + | ^^^^^ this usage requires that `'a` must outlive `'1` + +error: aborting due to 4 previous errors + From f924e74fb1c238b0e6c06c23b51c087f1e6aa0d5 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 19 Jun 2022 18:21:01 -0700 Subject: [PATCH 13/16] Provide a segment res in more cases --- compiler/rustc_resolve/src/ident.rs | 1 + src/test/ui/error-codes/E0109.stderr | 4 +- src/test/ui/error-codes/E0110.stderr | 4 +- .../cannot-infer-closure-circular.rs | 4 +- .../cannot-infer-closure-circular.stderr | 12 +-- .../enum-variant-generic-args.rs | 6 +- .../enum-variant-generic-args.stderr | 12 +-- src/test/ui/type/issue-91268.rs | 2 +- src/test/ui/type/issue-91268.stderr | 4 +- src/test/ui/typeck/prim-with-args.fixed | 44 +++++----- src/test/ui/typeck/prim-with-args.rs | 44 +++++----- src/test/ui/typeck/prim-with-args.stderr | 88 +++++++++---------- src/test/ui/usize-generic-argument-parent.rs | 2 +- .../ui/usize-generic-argument-parent.stderr | 4 +- 14 files changed, 116 insertions(+), 115 deletions(-) diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index e934e189f05f3..0cc6d05d1d086 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -1502,6 +1502,7 @@ impl<'a> Resolver<'a> { 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, path, second_binding); + record_segment_res(self, res); return PathResult::NonModule(PartialRes::with_unresolved_segments( res, path.len() - i - 1, diff --git a/src/test/ui/error-codes/E0109.stderr b/src/test/ui/error-codes/E0109.stderr index da00fdde6bd0a..8f4cb86de99a0 100644 --- a/src/test/ui/error-codes/E0109.stderr +++ b/src/test/ui/error-codes/E0109.stderr @@ -1,10 +1,10 @@ -error[E0109]: type arguments are not allowed on this type +error[E0109]: type arguments are not allowed on builtin type `u32` --> $DIR/E0109.rs:1:14 | LL | type X = u32; | --- ^^^ type argument not allowed | | - | not allowed on this type + | not allowed on builtin type `u32` | help: primitive type `u32` doesn't have generic parameters | diff --git a/src/test/ui/error-codes/E0110.stderr b/src/test/ui/error-codes/E0110.stderr index 5babb5c2961b7..4ce2a0a410ce4 100644 --- a/src/test/ui/error-codes/E0110.stderr +++ b/src/test/ui/error-codes/E0110.stderr @@ -1,10 +1,10 @@ -error[E0109]: lifetime arguments are not allowed on this type +error[E0109]: lifetime arguments are not allowed on builtin type `u32` --> $DIR/E0110.rs:1:14 | LL | type X = u32<'static>; | --- ^^^^^^^ lifetime argument not allowed | | - | not allowed on this type + | not allowed on builtin type `u32` | help: primitive type `u32` doesn't have generic parameters | diff --git a/src/test/ui/inference/cannot-infer-closure-circular.rs b/src/test/ui/inference/cannot-infer-closure-circular.rs index ae879db68ec13..affb481496d02 100644 --- a/src/test/ui/inference/cannot-infer-closure-circular.rs +++ b/src/test/ui/inference/cannot-infer-closure-circular.rs @@ -4,10 +4,10 @@ fn main() { // error handles this gracefully, and in particular doesn't generate an extra // note about the `?` operator in the closure body, which isn't relevant to // the inference. - let x = |r| { + let x = |r| { //~ ERROR type annotations needed for `Result<(), E>` let v = r?; Ok(v) }; - let _ = x(x(Ok(()))); //~ ERROR type annotations needed for `Result<(), E>` + let _ = x(x(Ok(()))); } diff --git a/src/test/ui/inference/cannot-infer-closure-circular.stderr b/src/test/ui/inference/cannot-infer-closure-circular.stderr index 3ad8e3cda16e5..b706cd2bc36e8 100644 --- a/src/test/ui/inference/cannot-infer-closure-circular.stderr +++ b/src/test/ui/inference/cannot-infer-closure-circular.stderr @@ -1,13 +1,13 @@ error[E0282]: type annotations needed for `Result<(), E>` - --> $DIR/cannot-infer-closure-circular.rs:12:9 + --> $DIR/cannot-infer-closure-circular.rs:7:14 | -LL | let _ = x(x(Ok(()))); - | ^ +LL | let x = |r| { + | ^ | -help: consider giving this pattern a type, where the type for type parameter `E` is specified +help: consider giving this closure parameter an explicit type, where the type for type parameter `E` is specified | -LL | let _: Result<(), E> = x(x(Ok(()))); - | +++++++++++++++ +LL | let x = |r: Result<(), E>| { + | +++++++++++++++ error: aborting due to previous error diff --git a/src/test/ui/type-alias-enum-variants/enum-variant-generic-args.rs b/src/test/ui/type-alias-enum-variants/enum-variant-generic-args.rs index e6f45036f8514..0031a4665c814 100644 --- a/src/test/ui/type-alias-enum-variants/enum-variant-generic-args.rs +++ b/src/test/ui/type-alias-enum-variants/enum-variant-generic-args.rs @@ -52,7 +52,7 @@ fn main() { // Tuple struct variant Enum::<()>::TSVariant::<()>(()); - //~^ ERROR type arguments are not allowed on this type [E0109] + //~^ ERROR type arguments are not allowed on tuple variant `TSVariant` [E0109] Alias::TSVariant::<()>(()); //~^ ERROR type arguments are not allowed on this type [E0109] @@ -70,7 +70,7 @@ fn main() { // Struct variant Enum::<()>::SVariant::<()> { v: () }; - //~^ ERROR type arguments are not allowed on this type [E0109] + //~^ ERROR type arguments are not allowed on variant `SVariant` [E0109] Alias::SVariant::<()> { v: () }; //~^ ERROR type arguments are not allowed on this type [E0109] @@ -88,7 +88,7 @@ fn main() { // Unit variant Enum::<()>::UVariant::<()>; - //~^ ERROR type arguments are not allowed on this type [E0109] + //~^ ERROR type arguments are not allowed on unit variant `UVariant` [E0109] Alias::UVariant::<()>; //~^ ERROR type arguments are not allowed on this type [E0109] diff --git a/src/test/ui/type-alias-enum-variants/enum-variant-generic-args.stderr b/src/test/ui/type-alias-enum-variants/enum-variant-generic-args.stderr index 9601bdce4c503..5467f61bee40f 100644 --- a/src/test/ui/type-alias-enum-variants/enum-variant-generic-args.stderr +++ b/src/test/ui/type-alias-enum-variants/enum-variant-generic-args.stderr @@ -272,13 +272,13 @@ LL | Self::<()>::UVariant::<()>; | | | not allowed on this type -error[E0109]: type arguments are not allowed on this type +error[E0109]: type arguments are not allowed on tuple variant `TSVariant` --> $DIR/enum-variant-generic-args.rs:54:29 | LL | Enum::<()>::TSVariant::<()>(()); | --------- ^^ type argument not allowed | | - | not allowed on this type + | not allowed on tuple variant `TSVariant` error[E0109]: type arguments are not allowed on this type --> $DIR/enum-variant-generic-args.rs:57:24 @@ -340,13 +340,13 @@ LL | AliasFixed::<()>::TSVariant::<()>(()); | | | not allowed on this type -error[E0109]: type arguments are not allowed on this type +error[E0109]: type arguments are not allowed on variant `SVariant` --> $DIR/enum-variant-generic-args.rs:72:28 | LL | Enum::<()>::SVariant::<()> { v: () }; | -------- ^^ type argument not allowed | | - | not allowed on this type + | not allowed on variant `SVariant` | = note: enum variants can't have type parameters @@ -438,13 +438,13 @@ LL - AliasFixed::<()>::SVariant::<()> { v: () }; LL + AliasFixed::<()>::SVariant { v: () }; | -error[E0109]: type arguments are not allowed on this type +error[E0109]: type arguments are not allowed on unit variant `UVariant` --> $DIR/enum-variant-generic-args.rs:90:28 | LL | Enum::<()>::UVariant::<()>; | -------- ^^ type argument not allowed | | - | not allowed on this type + | not allowed on unit variant `UVariant` error[E0109]: type arguments are not allowed on this type --> $DIR/enum-variant-generic-args.rs:93:23 diff --git a/src/test/ui/type/issue-91268.rs b/src/test/ui/type/issue-91268.rs index 01ed9ea9e231f..f1e16bc7bd3c1 100644 --- a/src/test/ui/type/issue-91268.rs +++ b/src/test/ui/type/issue-91268.rs @@ -1,7 +1,7 @@ // error-pattern: this file contains an unclosed delimiter // error-pattern: cannot find type `ţ` in this scope // error-pattern: parenthesized type parameters may only be used with a `Fn` trait -// error-pattern: type arguments are not allowed on this type +// error-pattern: type arguments are not allowed on builtin type `u8` // error-pattern: mismatched types // ignore-tidy-trailing-newlines // `ţ` must be the last character in this file, it cannot be followed by a newline diff --git a/src/test/ui/type/issue-91268.stderr b/src/test/ui/type/issue-91268.stderr index e426f450b11bb..6c9ee9945844d 100644 --- a/src/test/ui/type/issue-91268.stderr +++ b/src/test/ui/type/issue-91268.stderr @@ -35,13 +35,13 @@ help: use angle brackets instead LL | 0: u8<ţ> | ~ + -error[E0109]: type arguments are not allowed on this type +error[E0109]: type arguments are not allowed on builtin type `u8` --> $DIR/issue-91268.rs:9:11 | LL | 0: u8(ţ | -- ^ type argument not allowed | | - | not allowed on this type + | not allowed on builtin type `u8` | help: primitive type `u8` doesn't have generic parameters | diff --git a/src/test/ui/typeck/prim-with-args.fixed b/src/test/ui/typeck/prim-with-args.fixed index 1c5fd7508676a..e3f99479a3809 100644 --- a/src/test/ui/typeck/prim-with-args.fixed +++ b/src/test/ui/typeck/prim-with-args.fixed @@ -1,28 +1,28 @@ // run-rustfix fn main() { -let _x: isize; //~ ERROR type arguments are not allowed on this type -let _x: i8; //~ ERROR type arguments are not allowed on this type -let _x: i16; //~ ERROR type arguments are not allowed on this type -let _x: i32; //~ ERROR type arguments are not allowed on this type -let _x: i64; //~ ERROR type arguments are not allowed on this type -let _x: usize; //~ ERROR type arguments are not allowed on this type -let _x: u8; //~ ERROR type arguments are not allowed on this type -let _x: u16; //~ ERROR type arguments are not allowed on this type -let _x: u32; //~ ERROR type arguments are not allowed on this type -let _x: u64; //~ ERROR type arguments are not allowed on this type -let _x: char; //~ ERROR type arguments are not allowed on this type +let _x: isize; //~ ERROR type arguments are not allowed on builtin type +let _x: i8; //~ ERROR type arguments are not allowed on builtin type +let _x: i16; //~ ERROR type arguments are not allowed on builtin type +let _x: i32; //~ ERROR type arguments are not allowed on builtin type +let _x: i64; //~ ERROR type arguments are not allowed on builtin type +let _x: usize; //~ ERROR type arguments are not allowed on builtin type +let _x: u8; //~ ERROR type arguments are not allowed on builtin type +let _x: u16; //~ ERROR type arguments are not allowed on builtin type +let _x: u32; //~ ERROR type arguments are not allowed on builtin type +let _x: u64; //~ ERROR type arguments are not allowed on builtin type +let _x: char; //~ ERROR type arguments are not allowed on builtin type -let _x: isize; //~ ERROR lifetime arguments are not allowed on this type -let _x: i8; //~ ERROR lifetime arguments are not allowed on this type -let _x: i16; //~ ERROR lifetime arguments are not allowed on this type -let _x: i32; //~ ERROR lifetime arguments are not allowed on this type -let _x: i64; //~ ERROR lifetime arguments are not allowed on this type -let _x: usize; //~ ERROR lifetime arguments are not allowed on this type -let _x: u8; //~ ERROR lifetime arguments are not allowed on this type -let _x: u16; //~ ERROR lifetime arguments are not allowed on this type -let _x: u32; //~ ERROR lifetime arguments are not allowed on this type -let _x: u64; //~ ERROR lifetime arguments are not allowed on this type -let _x: char; //~ ERROR lifetime arguments are not allowed on this type +let _x: isize; //~ ERROR lifetime arguments are not allowed on builtin type +let _x: i8; //~ ERROR lifetime arguments are not allowed on builtin type +let _x: i16; //~ ERROR lifetime arguments are not allowed on builtin type +let _x: i32; //~ ERROR lifetime arguments are not allowed on builtin type +let _x: i64; //~ ERROR lifetime arguments are not allowed on builtin type +let _x: usize; //~ ERROR lifetime arguments are not allowed on builtin type +let _x: u8; //~ ERROR lifetime arguments are not allowed on builtin type +let _x: u16; //~ ERROR lifetime arguments are not allowed on builtin type +let _x: u32; //~ ERROR lifetime arguments are not allowed on builtin type +let _x: u64; //~ ERROR lifetime arguments are not allowed on builtin type +let _x: char; //~ ERROR lifetime arguments are not allowed on builtin type } diff --git a/src/test/ui/typeck/prim-with-args.rs b/src/test/ui/typeck/prim-with-args.rs index b05d6c1cb4e4a..b10471eccee68 100644 --- a/src/test/ui/typeck/prim-with-args.rs +++ b/src/test/ui/typeck/prim-with-args.rs @@ -1,28 +1,28 @@ // run-rustfix fn main() { -let _x: isize; //~ ERROR type arguments are not allowed on this type -let _x: i8; //~ ERROR type arguments are not allowed on this type -let _x: i16; //~ ERROR type arguments are not allowed on this type -let _x: i32; //~ ERROR type arguments are not allowed on this type -let _x: i64; //~ ERROR type arguments are not allowed on this type -let _x: usize; //~ ERROR type arguments are not allowed on this type -let _x: u8; //~ ERROR type arguments are not allowed on this type -let _x: u16; //~ ERROR type arguments are not allowed on this type -let _x: u32; //~ ERROR type arguments are not allowed on this type -let _x: u64; //~ ERROR type arguments are not allowed on this type -let _x: char; //~ ERROR type arguments are not allowed on this type +let _x: isize; //~ ERROR type arguments are not allowed on builtin type +let _x: i8; //~ ERROR type arguments are not allowed on builtin type +let _x: i16; //~ ERROR type arguments are not allowed on builtin type +let _x: i32; //~ ERROR type arguments are not allowed on builtin type +let _x: i64; //~ ERROR type arguments are not allowed on builtin type +let _x: usize; //~ ERROR type arguments are not allowed on builtin type +let _x: u8; //~ ERROR type arguments are not allowed on builtin type +let _x: u16; //~ ERROR type arguments are not allowed on builtin type +let _x: u32; //~ ERROR type arguments are not allowed on builtin type +let _x: u64; //~ ERROR type arguments are not allowed on builtin type +let _x: char; //~ ERROR type arguments are not allowed on builtin type -let _x: isize<'static>; //~ ERROR lifetime arguments are not allowed on this type -let _x: i8<'static>; //~ ERROR lifetime arguments are not allowed on this type -let _x: i16<'static>; //~ ERROR lifetime arguments are not allowed on this type -let _x: i32<'static>; //~ ERROR lifetime arguments are not allowed on this type -let _x: i64<'static>; //~ ERROR lifetime arguments are not allowed on this type -let _x: usize<'static>; //~ ERROR lifetime arguments are not allowed on this type -let _x: u8<'static>; //~ ERROR lifetime arguments are not allowed on this type -let _x: u16<'static>; //~ ERROR lifetime arguments are not allowed on this type -let _x: u32<'static>; //~ ERROR lifetime arguments are not allowed on this type -let _x: u64<'static>; //~ ERROR lifetime arguments are not allowed on this type -let _x: char<'static>; //~ ERROR lifetime arguments are not allowed on this type +let _x: isize<'static>; //~ ERROR lifetime arguments are not allowed on builtin type +let _x: i8<'static>; //~ ERROR lifetime arguments are not allowed on builtin type +let _x: i16<'static>; //~ ERROR lifetime arguments are not allowed on builtin type +let _x: i32<'static>; //~ ERROR lifetime arguments are not allowed on builtin type +let _x: i64<'static>; //~ ERROR lifetime arguments are not allowed on builtin type +let _x: usize<'static>; //~ ERROR lifetime arguments are not allowed on builtin type +let _x: u8<'static>; //~ ERROR lifetime arguments are not allowed on builtin type +let _x: u16<'static>; //~ ERROR lifetime arguments are not allowed on builtin type +let _x: u32<'static>; //~ ERROR lifetime arguments are not allowed on builtin type +let _x: u64<'static>; //~ ERROR lifetime arguments are not allowed on builtin type +let _x: char<'static>; //~ ERROR lifetime arguments are not allowed on builtin type } diff --git a/src/test/ui/typeck/prim-with-args.stderr b/src/test/ui/typeck/prim-with-args.stderr index c45fd00bae9a5..2ddad5ad71e46 100644 --- a/src/test/ui/typeck/prim-with-args.stderr +++ b/src/test/ui/typeck/prim-with-args.stderr @@ -1,10 +1,10 @@ -error[E0109]: type arguments are not allowed on this type +error[E0109]: type arguments are not allowed on builtin type `isize` --> $DIR/prim-with-args.rs:4:15 | LL | let _x: isize; | ----- ^^^^^ type argument not allowed | | - | not allowed on this type + | not allowed on builtin type `isize` | help: primitive type `isize` doesn't have generic parameters | @@ -12,13 +12,13 @@ LL - let _x: isize; LL + let _x: isize; | -error[E0109]: type arguments are not allowed on this type +error[E0109]: type arguments are not allowed on builtin type `i8` --> $DIR/prim-with-args.rs:5:12 | LL | let _x: i8; | -- ^^^^^ type argument not allowed | | - | not allowed on this type + | not allowed on builtin type `i8` | help: primitive type `i8` doesn't have generic parameters | @@ -26,13 +26,13 @@ LL - let _x: i8; LL + let _x: i8; | -error[E0109]: type arguments are not allowed on this type +error[E0109]: type arguments are not allowed on builtin type `i16` --> $DIR/prim-with-args.rs:6:13 | LL | let _x: i16; | --- ^^^^^ type argument not allowed | | - | not allowed on this type + | not allowed on builtin type `i16` | help: primitive type `i16` doesn't have generic parameters | @@ -40,13 +40,13 @@ LL - let _x: i16; LL + let _x: i16; | -error[E0109]: type arguments are not allowed on this type +error[E0109]: type arguments are not allowed on builtin type `i32` --> $DIR/prim-with-args.rs:7:13 | LL | let _x: i32; | --- ^^^^^ type argument not allowed | | - | not allowed on this type + | not allowed on builtin type `i32` | help: primitive type `i32` doesn't have generic parameters | @@ -54,13 +54,13 @@ LL - let _x: i32; LL + let _x: i32; | -error[E0109]: type arguments are not allowed on this type +error[E0109]: type arguments are not allowed on builtin type `i64` --> $DIR/prim-with-args.rs:8:13 | LL | let _x: i64; | --- ^^^^^ type argument not allowed | | - | not allowed on this type + | not allowed on builtin type `i64` | help: primitive type `i64` doesn't have generic parameters | @@ -68,13 +68,13 @@ LL - let _x: i64; LL + let _x: i64; | -error[E0109]: type arguments are not allowed on this type +error[E0109]: type arguments are not allowed on builtin type `usize` --> $DIR/prim-with-args.rs:9:15 | LL | let _x: usize; | ----- ^^^^^ type argument not allowed | | - | not allowed on this type + | not allowed on builtin type `usize` | help: primitive type `usize` doesn't have generic parameters | @@ -82,13 +82,13 @@ LL - let _x: usize; LL + let _x: usize; | -error[E0109]: type arguments are not allowed on this type +error[E0109]: type arguments are not allowed on builtin type `u8` --> $DIR/prim-with-args.rs:10:12 | LL | let _x: u8; | -- ^^^^^ type argument not allowed | | - | not allowed on this type + | not allowed on builtin type `u8` | help: primitive type `u8` doesn't have generic parameters | @@ -96,13 +96,13 @@ LL - let _x: u8; LL + let _x: u8; | -error[E0109]: type arguments are not allowed on this type +error[E0109]: type arguments are not allowed on builtin type `u16` --> $DIR/prim-with-args.rs:11:13 | LL | let _x: u16; | --- ^^^^^ type argument not allowed | | - | not allowed on this type + | not allowed on builtin type `u16` | help: primitive type `u16` doesn't have generic parameters | @@ -110,13 +110,13 @@ LL - let _x: u16; LL + let _x: u16; | -error[E0109]: type arguments are not allowed on this type +error[E0109]: type arguments are not allowed on builtin type `u32` --> $DIR/prim-with-args.rs:12:13 | LL | let _x: u32; | --- ^^^^^ type argument not allowed | | - | not allowed on this type + | not allowed on builtin type `u32` | help: primitive type `u32` doesn't have generic parameters | @@ -124,13 +124,13 @@ LL - let _x: u32; LL + let _x: u32; | -error[E0109]: type arguments are not allowed on this type +error[E0109]: type arguments are not allowed on builtin type `u64` --> $DIR/prim-with-args.rs:13:13 | LL | let _x: u64; | --- ^^^^^ type argument not allowed | | - | not allowed on this type + | not allowed on builtin type `u64` | help: primitive type `u64` doesn't have generic parameters | @@ -138,13 +138,13 @@ LL - let _x: u64; LL + let _x: u64; | -error[E0109]: type arguments are not allowed on this type +error[E0109]: type arguments are not allowed on builtin type `char` --> $DIR/prim-with-args.rs:14:14 | LL | let _x: char; | ---- ^^^^^ type argument not allowed | | - | not allowed on this type + | not allowed on builtin type `char` | help: primitive type `char` doesn't have generic parameters | @@ -152,13 +152,13 @@ LL - let _x: char; LL + let _x: char; | -error[E0109]: lifetime arguments are not allowed on this type +error[E0109]: lifetime arguments are not allowed on builtin type `isize` --> $DIR/prim-with-args.rs:16:15 | LL | let _x: isize<'static>; | ----- ^^^^^^^ lifetime argument not allowed | | - | not allowed on this type + | not allowed on builtin type `isize` | help: primitive type `isize` doesn't have generic parameters | @@ -166,13 +166,13 @@ LL - let _x: isize<'static>; LL + let _x: isize; | -error[E0109]: lifetime arguments are not allowed on this type +error[E0109]: lifetime arguments are not allowed on builtin type `i8` --> $DIR/prim-with-args.rs:17:12 | LL | let _x: i8<'static>; | -- ^^^^^^^ lifetime argument not allowed | | - | not allowed on this type + | not allowed on builtin type `i8` | help: primitive type `i8` doesn't have generic parameters | @@ -180,13 +180,13 @@ LL - let _x: i8<'static>; LL + let _x: i8; | -error[E0109]: lifetime arguments are not allowed on this type +error[E0109]: lifetime arguments are not allowed on builtin type `i16` --> $DIR/prim-with-args.rs:18:13 | LL | let _x: i16<'static>; | --- ^^^^^^^ lifetime argument not allowed | | - | not allowed on this type + | not allowed on builtin type `i16` | help: primitive type `i16` doesn't have generic parameters | @@ -194,13 +194,13 @@ LL - let _x: i16<'static>; LL + let _x: i16; | -error[E0109]: lifetime arguments are not allowed on this type +error[E0109]: lifetime arguments are not allowed on builtin type `i32` --> $DIR/prim-with-args.rs:19:13 | LL | let _x: i32<'static>; | --- ^^^^^^^ lifetime argument not allowed | | - | not allowed on this type + | not allowed on builtin type `i32` | help: primitive type `i32` doesn't have generic parameters | @@ -208,13 +208,13 @@ LL - let _x: i32<'static>; LL + let _x: i32; | -error[E0109]: lifetime arguments are not allowed on this type +error[E0109]: lifetime arguments are not allowed on builtin type `i64` --> $DIR/prim-with-args.rs:20:13 | LL | let _x: i64<'static>; | --- ^^^^^^^ lifetime argument not allowed | | - | not allowed on this type + | not allowed on builtin type `i64` | help: primitive type `i64` doesn't have generic parameters | @@ -222,13 +222,13 @@ LL - let _x: i64<'static>; LL + let _x: i64; | -error[E0109]: lifetime arguments are not allowed on this type +error[E0109]: lifetime arguments are not allowed on builtin type `usize` --> $DIR/prim-with-args.rs:21:15 | LL | let _x: usize<'static>; | ----- ^^^^^^^ lifetime argument not allowed | | - | not allowed on this type + | not allowed on builtin type `usize` | help: primitive type `usize` doesn't have generic parameters | @@ -236,13 +236,13 @@ LL - let _x: usize<'static>; LL + let _x: usize; | -error[E0109]: lifetime arguments are not allowed on this type +error[E0109]: lifetime arguments are not allowed on builtin type `u8` --> $DIR/prim-with-args.rs:22:12 | LL | let _x: u8<'static>; | -- ^^^^^^^ lifetime argument not allowed | | - | not allowed on this type + | not allowed on builtin type `u8` | help: primitive type `u8` doesn't have generic parameters | @@ -250,13 +250,13 @@ LL - let _x: u8<'static>; LL + let _x: u8; | -error[E0109]: lifetime arguments are not allowed on this type +error[E0109]: lifetime arguments are not allowed on builtin type `u16` --> $DIR/prim-with-args.rs:23:13 | LL | let _x: u16<'static>; | --- ^^^^^^^ lifetime argument not allowed | | - | not allowed on this type + | not allowed on builtin type `u16` | help: primitive type `u16` doesn't have generic parameters | @@ -264,13 +264,13 @@ LL - let _x: u16<'static>; LL + let _x: u16; | -error[E0109]: lifetime arguments are not allowed on this type +error[E0109]: lifetime arguments are not allowed on builtin type `u32` --> $DIR/prim-with-args.rs:24:13 | LL | let _x: u32<'static>; | --- ^^^^^^^ lifetime argument not allowed | | - | not allowed on this type + | not allowed on builtin type `u32` | help: primitive type `u32` doesn't have generic parameters | @@ -278,13 +278,13 @@ LL - let _x: u32<'static>; LL + let _x: u32; | -error[E0109]: lifetime arguments are not allowed on this type +error[E0109]: lifetime arguments are not allowed on builtin type `u64` --> $DIR/prim-with-args.rs:25:13 | LL | let _x: u64<'static>; | --- ^^^^^^^ lifetime argument not allowed | | - | not allowed on this type + | not allowed on builtin type `u64` | help: primitive type `u64` doesn't have generic parameters | @@ -292,13 +292,13 @@ LL - let _x: u64<'static>; LL + let _x: u64; | -error[E0109]: lifetime arguments are not allowed on this type +error[E0109]: lifetime arguments are not allowed on builtin type `char` --> $DIR/prim-with-args.rs:26:14 | LL | let _x: char<'static>; | ---- ^^^^^^^ lifetime argument not allowed | | - | not allowed on this type + | not allowed on builtin type `char` | help: primitive type `char` doesn't have generic parameters | diff --git a/src/test/ui/usize-generic-argument-parent.rs b/src/test/ui/usize-generic-argument-parent.rs index 6d17ba9b5b261..4ab80d944a56f 100644 --- a/src/test/ui/usize-generic-argument-parent.rs +++ b/src/test/ui/usize-generic-argument-parent.rs @@ -1,5 +1,5 @@ fn foo() { - let x: usize; //~ ERROR const arguments are not allowed on this type + let x: usize; //~ ERROR const arguments are not allowed on builtin type `usize` } fn main() {} diff --git a/src/test/ui/usize-generic-argument-parent.stderr b/src/test/ui/usize-generic-argument-parent.stderr index abe8c09b739f7..131c476aa55c0 100644 --- a/src/test/ui/usize-generic-argument-parent.stderr +++ b/src/test/ui/usize-generic-argument-parent.stderr @@ -1,10 +1,10 @@ -error[E0109]: const arguments are not allowed on this type +error[E0109]: const arguments are not allowed on builtin type `usize` --> $DIR/usize-generic-argument-parent.rs:2:18 | LL | let x: usize; | ----- ^^^ const argument not allowed | | - | not allowed on this type + | not allowed on builtin type `usize` | help: primitive type `usize` doesn't have generic parameters | From a0eba6634f1430e29637257fd1416f896bcf9edc Mon Sep 17 00:00:00 2001 From: Caio Date: Tue, 21 Jun 2022 10:56:26 -0300 Subject: [PATCH 14/16] [RFC 2011] Optimize non-consuming operators --- .../src/assert/context.rs | 96 ++++++++++-- .../rfc-2011-nicer-assert-messages/codegen.rs | 9 -- .../codegen.stdout | 29 ---- ...onsuming-methods-have-optimized-codegen.rs | 32 ++++ ...ming-methods-have-optimized-codegen.stdout | 147 ++++++++++++++++++ 5 files changed, 260 insertions(+), 53 deletions(-) delete mode 100644 src/test/ui/macros/rfc-2011-nicer-assert-messages/codegen.rs delete mode 100644 src/test/ui/macros/rfc-2011-nicer-assert-messages/codegen.stdout create mode 100644 src/test/ui/macros/rfc-2011-nicer-assert-messages/non-consuming-methods-have-optimized-codegen.rs create mode 100644 src/test/ui/macros/rfc-2011-nicer-assert-messages/non-consuming-methods-have-optimized-codegen.stdout diff --git a/compiler/rustc_builtin_macros/src/assert/context.rs b/compiler/rustc_builtin_macros/src/assert/context.rs index cad301812123b..9e50d33486cf2 100644 --- a/compiler/rustc_builtin_macros/src/assert/context.rs +++ b/compiler/rustc_builtin_macros/src/assert/context.rs @@ -1,11 +1,10 @@ -use crate::assert::expr_if_not; use rustc_ast::{ attr, ptr::P, token, tokenstream::{DelimSpan, TokenStream, TokenTree}, - BorrowKind, Expr, ExprKind, ItemKind, MacArgs, MacCall, MacDelimiter, Mutability, Path, - PathSegment, Stmt, StructRest, UseTree, UseTreeKind, DUMMY_NODE_ID, + BinOpKind, BorrowKind, Expr, ExprKind, ItemKind, MacArgs, MacCall, MacDelimiter, Mutability, + Path, PathSegment, Stmt, StructRest, UnOp, UseTree, UseTreeKind, DUMMY_NODE_ID, }; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; @@ -16,11 +15,19 @@ use rustc_span::{ }; pub(super) struct Context<'cx, 'a> { + // An optimization. + // + // Elements that aren't consumed (PartialEq, PartialOrd, ...) can be copied **after** the + // `assert!` expression fails rather than copied on-the-fly. + best_case_captures: Vec, // Top-level `let captureN = Capture::new()` statements capture_decls: Vec, cx: &'cx ExtCtxt<'a>, // Formatting string used for debugging fmt_string: String, + // If the current expression being visited consumes itself. Used to construct + // `best_case_captures`. + is_consumed: bool, // Top-level `let __local_bindN = &expr` statements local_bind_decls: Vec, // Used to avoid capturing duplicated paths @@ -36,9 +43,11 @@ pub(super) struct Context<'cx, 'a> { impl<'cx, 'a> Context<'cx, 'a> { pub(super) fn new(cx: &'cx ExtCtxt<'a>, span: Span) -> Self { Self { + best_case_captures: <_>::default(), capture_decls: <_>::default(), cx, fmt_string: <_>::default(), + is_consumed: true, local_bind_decls: <_>::default(), paths: <_>::default(), span, @@ -69,14 +78,22 @@ impl<'cx, 'a> Context<'cx, 'a> { self.manage_cond_expr(&mut cond_expr); let initial_imports = self.build_initial_imports(); let panic = self.build_panic(&expr_str, panic_path); + let cond_expr_with_unlikely = self.build_unlikely(cond_expr); + + let Self { best_case_captures, capture_decls, cx, local_bind_decls, span, .. } = self; - let Self { capture_decls, cx, local_bind_decls, span, .. } = self; + let mut assert_then_stmts = Vec::with_capacity(2); + assert_then_stmts.extend(best_case_captures); + assert_then_stmts.push(self.cx.stmt_expr(panic)); + let assert_then = self.cx.block(span, assert_then_stmts); let mut stmts = Vec::with_capacity(4); stmts.push(initial_imports); stmts.extend(capture_decls.into_iter().map(|c| c.decl)); stmts.extend(local_bind_decls); - stmts.push(cx.stmt_expr(expr_if_not(cx, span, cond_expr, panic, None))); + stmts.push( + cx.stmt_expr(cx.expr(span, ExprKind::If(cond_expr_with_unlikely, assert_then, None))), + ); cx.expr_block(cx.block(span, stmts)) } @@ -115,6 +132,16 @@ impl<'cx, 'a> Context<'cx, 'a> { ) } + /// Takes the conditional expression of `assert!` and then wraps it inside `unlikely` + fn build_unlikely(&self, cond_expr: P) -> P { + let unlikely_path = self.cx.std_path(&[sym::intrinsics, sym::unlikely]); + self.cx.expr_call( + self.span, + self.cx.expr_path(self.cx.path(self.span, unlikely_path)), + vec![self.cx.expr(self.span, ExprKind::Unary(UnOp::Not, cond_expr))], + ) + } + /// The necessary custom `panic!(...)` expression. /// /// panic!( @@ -167,17 +194,39 @@ impl<'cx, 'a> Context<'cx, 'a> { /// See [Self::manage_initial_capture] and [Self::manage_try_capture] fn manage_cond_expr(&mut self, expr: &mut P) { match (*expr).kind { - ExprKind::AddrOf(_, _, ref mut local_expr) => { - self.manage_cond_expr(local_expr); + ExprKind::AddrOf(_, mutability, ref mut local_expr) => { + self.with_is_consumed_management( + matches!(mutability, Mutability::Mut), + |this| this.manage_cond_expr(local_expr) + ); } ExprKind::Array(ref mut local_exprs) => { for local_expr in local_exprs { self.manage_cond_expr(local_expr); } } - ExprKind::Binary(_, ref mut lhs, ref mut rhs) => { - self.manage_cond_expr(lhs); - self.manage_cond_expr(rhs); + ExprKind::Binary(ref op, ref mut lhs, ref mut rhs) => { + self.with_is_consumed_management( + matches!( + op.node, + BinOpKind::Add + | BinOpKind::And + | BinOpKind::BitAnd + | BinOpKind::BitOr + | BinOpKind::BitXor + | BinOpKind::Div + | BinOpKind::Mul + | BinOpKind::Or + | BinOpKind::Rem + | BinOpKind::Shl + | BinOpKind::Shr + | BinOpKind::Sub + ), + |this| { + this.manage_cond_expr(lhs); + this.manage_cond_expr(rhs); + } + ); } ExprKind::Call(_, ref mut local_exprs) => { for local_expr in local_exprs { @@ -228,8 +277,11 @@ impl<'cx, 'a> Context<'cx, 'a> { self.manage_cond_expr(local_expr); } } - ExprKind::Unary(_, ref mut local_expr) => { - self.manage_cond_expr(local_expr); + ExprKind::Unary(un_op, ref mut local_expr) => { + self.with_is_consumed_management( + matches!(un_op, UnOp::Neg | UnOp::Not), + |this| this.manage_cond_expr(local_expr) + ); } // Expressions that are not worth or can not be captured. // @@ -337,9 +389,23 @@ impl<'cx, 'a> Context<'cx, 'a> { )) .add_trailing_semicolon(); let local_bind_path = self.cx.expr_path(Path::from_ident(local_bind)); - let ret = self.cx.stmt_expr(local_bind_path); - let block = self.cx.expr_block(self.cx.block(self.span, vec![try_capture_call, ret])); - *expr = self.cx.expr_deref(self.span, block); + let rslt = if self.is_consumed { + let ret = self.cx.stmt_expr(local_bind_path); + self.cx.expr_block(self.cx.block(self.span, vec![try_capture_call, ret])) + } else { + self.best_case_captures.push(try_capture_call); + local_bind_path + }; + *expr = self.cx.expr_deref(self.span, rslt); + } + + // Calls `f` with the internal `is_consumed` set to `curr_is_consumed` and then + // sets the internal `is_consumed` back to its original value. + fn with_is_consumed_management(&mut self, curr_is_consumed: bool, f: impl FnOnce(&mut Self)) { + let prev_is_consumed = self.is_consumed; + self.is_consumed = curr_is_consumed; + f(self); + self.is_consumed = prev_is_consumed; } } diff --git a/src/test/ui/macros/rfc-2011-nicer-assert-messages/codegen.rs b/src/test/ui/macros/rfc-2011-nicer-assert-messages/codegen.rs deleted file mode 100644 index 1db9d33c72aee..0000000000000 --- a/src/test/ui/macros/rfc-2011-nicer-assert-messages/codegen.rs +++ /dev/null @@ -1,9 +0,0 @@ -// check-pass -// compile-flags: -Z unpretty=expanded - -#![feature(core_intrinsics, generic_assert, generic_assert_internals)] - -fn main() { - let elem = 1i32; - assert!(elem == 1); -} diff --git a/src/test/ui/macros/rfc-2011-nicer-assert-messages/codegen.stdout b/src/test/ui/macros/rfc-2011-nicer-assert-messages/codegen.stdout deleted file mode 100644 index a590eb3223254..0000000000000 --- a/src/test/ui/macros/rfc-2011-nicer-assert-messages/codegen.stdout +++ /dev/null @@ -1,29 +0,0 @@ -#![feature(prelude_import)] -#![no_std] -// check-pass -// compile-flags: -Z unpretty=expanded - -#![feature(core_intrinsics, generic_assert, generic_assert_internals)] -#[prelude_import] -use ::std::prelude::rust_2015::*; -#[macro_use] -extern crate std; - -fn main() { - let elem = 1i32; - { - #[allow(unused_imports)] - use ::core::asserting::{TryCaptureGeneric, TryCapturePrintable}; - let mut __capture0 = ::core::asserting::Capture::new(); - let __local_bind0 = &elem; - if !(*{ - (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); - __local_bind0 - } == 1) { - { - ::std::rt::panic_fmt(::core::fmt::Arguments::new_v1(&["Assertion failed: elem == 1\nWith captures:\n elem = ", - "\n"], &[::core::fmt::ArgumentV1::new_debug(&__capture0)])) - } - } - }; -} diff --git a/src/test/ui/macros/rfc-2011-nicer-assert-messages/non-consuming-methods-have-optimized-codegen.rs b/src/test/ui/macros/rfc-2011-nicer-assert-messages/non-consuming-methods-have-optimized-codegen.rs new file mode 100644 index 0000000000000..5ec84b08ff808 --- /dev/null +++ b/src/test/ui/macros/rfc-2011-nicer-assert-messages/non-consuming-methods-have-optimized-codegen.rs @@ -0,0 +1,32 @@ +// check-pass +// compile-flags: -Z unpretty=expanded + +#![feature(core_intrinsics, generic_assert, generic_assert_internals)] + +fn arbitrary_consuming_method_for_demonstration_purposes() { + let elem = 1i32; + assert!(elem as usize); +} + +fn addr_of() { + let elem = 1i32; + assert!(&elem); +} + +fn binary() { + let elem = 1i32; + assert!(elem == 1); + assert!(elem >= 1); + assert!(elem > 0); + assert!(elem < 3); + assert!(elem <= 3); + assert!(elem != 3); +} + +fn unary() { + let elem = &1i32; + assert!(*elem); +} + +fn main() { +} diff --git a/src/test/ui/macros/rfc-2011-nicer-assert-messages/non-consuming-methods-have-optimized-codegen.stdout b/src/test/ui/macros/rfc-2011-nicer-assert-messages/non-consuming-methods-have-optimized-codegen.stdout new file mode 100644 index 0000000000000..90f858f80e6b5 --- /dev/null +++ b/src/test/ui/macros/rfc-2011-nicer-assert-messages/non-consuming-methods-have-optimized-codegen.stdout @@ -0,0 +1,147 @@ +#![feature(prelude_import)] +#![no_std] +// check-pass +// compile-flags: -Z unpretty=expanded + +#![feature(core_intrinsics, generic_assert, generic_assert_internals)] +#[prelude_import] +use ::std::prelude::rust_2015::*; +#[macro_use] +extern crate std; + +fn arbitrary_consuming_method_for_demonstration_purposes() { + let elem = 1i32; + { + #[allow(unused_imports)] + use ::core::asserting::{TryCaptureGeneric, TryCapturePrintable}; + let mut __capture0 = ::core::asserting::Capture::new(); + let __local_bind0 = &elem; + if ::core::intrinsics::unlikely(!(*{ + (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); + __local_bind0 + } as usize)) { + + + + + { + ::std::rt::panic_fmt(::core::fmt::Arguments::new_v1(&["Assertion failed: elem as usize\nWith captures:\n elem = ", + "\n"], &[::core::fmt::ArgumentV1::new_debug(&__capture0)])) + } + } + }; +} +fn addr_of() { + let elem = 1i32; + { + #[allow(unused_imports)] + use ::core::asserting::{TryCaptureGeneric, TryCapturePrintable}; + let mut __capture0 = ::core::asserting::Capture::new(); + let __local_bind0 = &elem; + if ::core::intrinsics::unlikely(!&*__local_bind0) { + (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); + { + ::std::rt::panic_fmt(::core::fmt::Arguments::new_v1(&["Assertion failed: &elem\nWith captures:\n elem = ", + "\n"], &[::core::fmt::ArgumentV1::new_debug(&__capture0)])) + } + } + }; +} +fn binary() { + let elem = 1i32; + { + #[allow(unused_imports)] + use ::core::asserting::{TryCaptureGeneric, TryCapturePrintable}; + let mut __capture0 = ::core::asserting::Capture::new(); + let __local_bind0 = &elem; + if ::core::intrinsics::unlikely(!(*__local_bind0 == 1)) { + (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); + { + ::std::rt::panic_fmt(::core::fmt::Arguments::new_v1(&["Assertion failed: elem == 1\nWith captures:\n elem = ", + "\n"], &[::core::fmt::ArgumentV1::new_debug(&__capture0)])) + } + } + }; + { + #[allow(unused_imports)] + use ::core::asserting::{TryCaptureGeneric, TryCapturePrintable}; + let mut __capture0 = ::core::asserting::Capture::new(); + let __local_bind0 = &elem; + if ::core::intrinsics::unlikely(!(*__local_bind0 >= 1)) { + (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); + { + ::std::rt::panic_fmt(::core::fmt::Arguments::new_v1(&["Assertion failed: elem >= 1\nWith captures:\n elem = ", + "\n"], &[::core::fmt::ArgumentV1::new_debug(&__capture0)])) + } + } + }; + { + #[allow(unused_imports)] + use ::core::asserting::{TryCaptureGeneric, TryCapturePrintable}; + let mut __capture0 = ::core::asserting::Capture::new(); + let __local_bind0 = &elem; + if ::core::intrinsics::unlikely(!(*__local_bind0 > 0)) { + (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); + { + ::std::rt::panic_fmt(::core::fmt::Arguments::new_v1(&["Assertion failed: elem > 0\nWith captures:\n elem = ", + "\n"], &[::core::fmt::ArgumentV1::new_debug(&__capture0)])) + } + } + }; + { + #[allow(unused_imports)] + use ::core::asserting::{TryCaptureGeneric, TryCapturePrintable}; + let mut __capture0 = ::core::asserting::Capture::new(); + let __local_bind0 = &elem; + if ::core::intrinsics::unlikely(!(*__local_bind0 < 3)) { + (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); + { + ::std::rt::panic_fmt(::core::fmt::Arguments::new_v1(&["Assertion failed: elem < 3\nWith captures:\n elem = ", + "\n"], &[::core::fmt::ArgumentV1::new_debug(&__capture0)])) + } + } + }; + { + #[allow(unused_imports)] + use ::core::asserting::{TryCaptureGeneric, TryCapturePrintable}; + let mut __capture0 = ::core::asserting::Capture::new(); + let __local_bind0 = &elem; + if ::core::intrinsics::unlikely(!(*__local_bind0 <= 3)) { + (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); + { + ::std::rt::panic_fmt(::core::fmt::Arguments::new_v1(&["Assertion failed: elem <= 3\nWith captures:\n elem = ", + "\n"], &[::core::fmt::ArgumentV1::new_debug(&__capture0)])) + } + } + }; + { + #[allow(unused_imports)] + use ::core::asserting::{TryCaptureGeneric, TryCapturePrintable}; + let mut __capture0 = ::core::asserting::Capture::new(); + let __local_bind0 = &elem; + if ::core::intrinsics::unlikely(!(*__local_bind0 != 3)) { + (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); + { + ::std::rt::panic_fmt(::core::fmt::Arguments::new_v1(&["Assertion failed: elem != 3\nWith captures:\n elem = ", + "\n"], &[::core::fmt::ArgumentV1::new_debug(&__capture0)])) + } + } + }; +} +fn unary() { + let elem = &1i32; + { + #[allow(unused_imports)] + use ::core::asserting::{TryCaptureGeneric, TryCapturePrintable}; + let mut __capture0 = ::core::asserting::Capture::new(); + let __local_bind0 = &elem; + if ::core::intrinsics::unlikely(!**__local_bind0) { + (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); + { + ::std::rt::panic_fmt(::core::fmt::Arguments::new_v1(&["Assertion failed: *elem\nWith captures:\n elem = ", + "\n"], &[::core::fmt::ArgumentV1::new_debug(&__capture0)])) + } + } + }; +} +fn main() {} From f847261478de5fa72d7e1d2ec3341e31a794fcaf Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Wed, 22 Jun 2022 12:01:41 +0900 Subject: [PATCH 15/16] stop pointing at definitions of missing fields --- Cargo.lock | 1 - compiler/rustc_typeck/Cargo.toml | 1 - compiler/rustc_typeck/src/check/expr.rs | 69 ++++++++----------- src/test/ui/issues/issue-76077.stderr | 6 +- src/test/ui/privacy/issue-79593.stderr | 6 +- ...-missing-inaccessible-field-literal.stderr | 6 +- ...ng-private-fields-in-struct-literal.stderr | 10 +-- 7 files changed, 33 insertions(+), 66 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cc0f4cc53e656..df6f46f26cf0d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4590,7 +4590,6 @@ dependencies = [ name = "rustc_typeck" version = "0.0.0" dependencies = [ - "itertools", "rustc_arena", "rustc_ast", "rustc_attr", diff --git a/compiler/rustc_typeck/Cargo.toml b/compiler/rustc_typeck/Cargo.toml index b3dd695508094..c08023ee6a70a 100644 --- a/compiler/rustc_typeck/Cargo.toml +++ b/compiler/rustc_typeck/Cargo.toml @@ -10,7 +10,6 @@ doctest = false [dependencies] rustc_arena = { path = "../rustc_arena" } tracing = "0.1" -itertools = "0.10.1" rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } rustc_attr = { path = "../rustc_attr" } diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 0a017de80f248..e5048fc513217 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -23,13 +23,12 @@ use crate::type_error_struct; use super::suggest_call_constructor; use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive}; -use itertools::{Either, Itertools}; use rustc_ast as ast; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::{ pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, DiagnosticId, - EmissionGuarantee, ErrorGuaranteed, MultiSpan, + EmissionGuarantee, ErrorGuaranteed, }; use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; @@ -1682,11 +1681,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }) .collect(); - if !private_fields.is_empty() - && tcx - .visibility(variant.def_id) - .is_accessible_from(tcx.parent_module(expr_id).to_def_id(), tcx) - { + if !private_fields.is_empty() { self.report_private_fields(adt_ty, span, private_fields, ast_fields); } else { self.report_missing_fields( @@ -1826,12 +1821,31 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { private_fields: Vec<&ty::FieldDef>, used_fields: &'tcx [hir::ExprField<'tcx>], ) { - let field_names = |fields: Vec, len: usize| match &fields + let mut err = self.tcx.sess.struct_span_err( + span, + &format!( + "cannot construct `{adt_ty}` with struct literal syntax due to private fields", + ), + ); + let (used_private_fields, remaining_private_fields): ( + Vec<(Symbol, Span, bool)>, + Vec<(Symbol, Span, bool)>, + ) = private_fields + .iter() + .map(|field| { + match used_fields.iter().find(|used_field| field.name == used_field.ident.name) { + Some(used_field) => (field.name, used_field.span, true), + None => (field.name, self.tcx.def_span(field.did), false), + } + }) + .partition(|field| field.2); + let remaining_private_fields_len = remaining_private_fields.len(); + let names = match &remaining_private_fields .iter() - .map(|field| field.to_string()) + .map(|(name, _, _)| name.to_string()) .collect::>()[..] { - _ if len > 6 => String::new(), + _ if remaining_private_fields_len > 6 => String::new(), [name] => format!("`{name}` "), [names @ .., last] => { let names = names.iter().map(|name| format!("`{name}`")).collect::>(); @@ -1839,36 +1853,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } [] => unreachable!(), }; - - let mut err = self.tcx.sess.struct_span_err( - span, - &format!( - "cannot construct `{adt_ty}` with struct literal syntax due to private fields", - ), - ); - let (used_private_fields, remaining_private_fields): ( - Vec<(Symbol, Span)>, - Vec<(Symbol, Span)>, - ) = private_fields.iter().partition_map(|field| { - match used_fields.iter().find(|used_field| field.name == used_field.ident.name) { - Some(used_field) => Either::Left((field.name, used_field.span)), - None => Either::Right((field.name, self.tcx.def_span(field.did))), - } - }); - let remaining_private_fields_len = remaining_private_fields.len(); - err.span_labels(used_private_fields.iter().map(|(_, span)| *span), "private field"); - err.span_note( - MultiSpan::from_spans(remaining_private_fields.iter().map(|(_, span)| *span).collect()), - format!( - "missing field{s} {names}{are} private", - s = pluralize!(remaining_private_fields_len), - are = pluralize!("is", remaining_private_fields_len), - names = field_names( - remaining_private_fields.iter().map(|(name, _)| *name).collect(), - remaining_private_fields_len - ) - ), - ); + err.span_labels(used_private_fields.iter().map(|(_, span, _)| *span), "private field"); + err.note(format!( + "... and other private field{s} {names}that were not provided", + s = pluralize!(remaining_private_fields_len), + )); err.emit(); } diff --git a/src/test/ui/issues/issue-76077.stderr b/src/test/ui/issues/issue-76077.stderr index c70a928f6475a..57f7abe3931a4 100644 --- a/src/test/ui/issues/issue-76077.stderr +++ b/src/test/ui/issues/issue-76077.stderr @@ -4,11 +4,7 @@ error: cannot construct `Foo` with struct literal syntax due to private fields LL | foo::Foo {}; | ^^^^^^^^ | -note: missing field `you_cant_use_this_field` is private - --> $DIR/issue-76077.rs:3:9 - | -LL | you_cant_use_this_field: bool, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: ... and other private field `you_cant_use_this_field` that were not provided error: aborting due to previous error diff --git a/src/test/ui/privacy/issue-79593.stderr b/src/test/ui/privacy/issue-79593.stderr index 435d4cbf73595..d878e1c023ff1 100644 --- a/src/test/ui/privacy/issue-79593.stderr +++ b/src/test/ui/privacy/issue-79593.stderr @@ -16,11 +16,7 @@ error: cannot construct `Pub` with struct literal syntax due to private fields LL | foo::Pub {}; | ^^^^^^^^ | -note: missing field `private` is private - --> $DIR/issue-79593.rs:2:22 - | -LL | pub struct Pub { private: () } - | ^^^^^^^^^^^ + = note: ... and other private field `private` that were not provided error[E0063]: missing field `y` in initializer of `Enum` --> $DIR/issue-79593.rs:23:5 diff --git a/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.stderr b/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.stderr index 2ade7aea57b3d..fa1c661ef244e 100644 --- a/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.stderr +++ b/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.stderr @@ -4,11 +4,7 @@ error: cannot construct `Foo` with struct literal syntax due to private fields LL | foo::Foo {}; | ^^^^^^^^ | -note: missing field `you_cant_use_this_field` is private - --> $DIR/issue-87872-missing-inaccessible-field-literal.rs:4:9 - | -LL | you_cant_use_this_field: bool, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: ... and other private field `you_cant_use_this_field` that were not provided error: aborting due to previous error diff --git a/src/test/ui/typeck/missing-private-fields-in-struct-literal.stderr b/src/test/ui/typeck/missing-private-fields-in-struct-literal.stderr index eb5f460f868e1..234110f31f79c 100644 --- a/src/test/ui/typeck/missing-private-fields-in-struct-literal.stderr +++ b/src/test/ui/typeck/missing-private-fields-in-struct-literal.stderr @@ -9,15 +9,7 @@ LL | a: (), LL | b: (), | ----- private field | -note: missing fields `c`, `d` and `e` are private - --> $DIR/missing-private-fields-in-struct-literal.rs:6:9 - | -LL | c: (), - | ^^^^^ -LL | d: (), - | ^^^^^ -LL | e: (), - | ^^^^^ + = note: ... and other private fields `c`, `d` and `e` that were not provided error: aborting due to previous error From eb86daa1383d5330a18aa4e78270a6ca5b4ea469 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Wed, 22 Jun 2022 14:56:40 +0900 Subject: [PATCH 16/16] add "was" to pluralize macro and use it --- compiler/rustc_lint_defs/src/lib.rs | 3 ++ compiler/rustc_typeck/src/check/expr.rs | 39 ++++++++++--------- src/test/ui/issues/issue-76077.stderr | 2 +- src/test/ui/privacy/issue-79593.stderr | 2 +- ...-missing-inaccessible-field-literal.stderr | 2 +- 5 files changed, 27 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index cb1c6f4098767..1cd19c7eaab35 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -26,6 +26,9 @@ macro_rules! pluralize { ("is", $x:expr) => { if $x == 1 { "is" } else { "are" } }; + ("was", $x:expr) => { + if $x == 1 { "was" } else { "were" } + }; ("this", $x:expr) => { if $x == 1 { "this" } else { "these" } }; diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index e5048fc513217..c69de6434539a 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -1839,25 +1839,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }) .partition(|field| field.2); - let remaining_private_fields_len = remaining_private_fields.len(); - let names = match &remaining_private_fields - .iter() - .map(|(name, _, _)| name.to_string()) - .collect::>()[..] - { - _ if remaining_private_fields_len > 6 => String::new(), - [name] => format!("`{name}` "), - [names @ .., last] => { - let names = names.iter().map(|name| format!("`{name}`")).collect::>(); - format!("{} and `{last}` ", names.join(", ")) - } - [] => unreachable!(), - }; err.span_labels(used_private_fields.iter().map(|(_, span, _)| *span), "private field"); - err.note(format!( - "... and other private field{s} {names}that were not provided", - s = pluralize!(remaining_private_fields_len), - )); + if !remaining_private_fields.is_empty() { + let remaining_private_fields_len = remaining_private_fields.len(); + let names = match &remaining_private_fields + .iter() + .map(|(name, _, _)| name.to_string()) + .collect::>()[..] + { + _ if remaining_private_fields_len > 6 => String::new(), + [name] => format!("`{name}` "), + [names @ .., last] => { + let names = names.iter().map(|name| format!("`{name}`")).collect::>(); + format!("{} and `{last}` ", names.join(", ")) + } + [] => unreachable!(), + }; + err.note(format!( + "... and other private field{s} {names}that {were} not provided", + s = pluralize!(remaining_private_fields_len), + were = pluralize!("was", remaining_private_fields_len), + )); + } err.emit(); } diff --git a/src/test/ui/issues/issue-76077.stderr b/src/test/ui/issues/issue-76077.stderr index 57f7abe3931a4..197ca8d5a7b25 100644 --- a/src/test/ui/issues/issue-76077.stderr +++ b/src/test/ui/issues/issue-76077.stderr @@ -4,7 +4,7 @@ error: cannot construct `Foo` with struct literal syntax due to private fields LL | foo::Foo {}; | ^^^^^^^^ | - = note: ... and other private field `you_cant_use_this_field` that were not provided + = note: ... and other private field `you_cant_use_this_field` that was not provided error: aborting due to previous error diff --git a/src/test/ui/privacy/issue-79593.stderr b/src/test/ui/privacy/issue-79593.stderr index d878e1c023ff1..21ba760ad0bcc 100644 --- a/src/test/ui/privacy/issue-79593.stderr +++ b/src/test/ui/privacy/issue-79593.stderr @@ -16,7 +16,7 @@ error: cannot construct `Pub` with struct literal syntax due to private fields LL | foo::Pub {}; | ^^^^^^^^ | - = note: ... and other private field `private` that were not provided + = note: ... and other private field `private` that was not provided error[E0063]: missing field `y` in initializer of `Enum` --> $DIR/issue-79593.rs:23:5 diff --git a/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.stderr b/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.stderr index fa1c661ef244e..f0bd3e0ddf768 100644 --- a/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.stderr +++ b/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.stderr @@ -4,7 +4,7 @@ error: cannot construct `Foo` with struct literal syntax due to private fields LL | foo::Foo {}; | ^^^^^^^^ | - = note: ... and other private field `you_cant_use_this_field` that were not provided + = note: ... and other private field `you_cant_use_this_field` that was not provided error: aborting due to previous error