From 3fc35b5b935e390c61ea2bbf744838b2632b2df1 Mon Sep 17 00:00:00 2001 From: BlackHoleFox Date: Thu, 25 Aug 2022 15:43:59 -0500 Subject: [PATCH 01/24] Use getentropy when possible on all Apple platforms --- library/std/src/sys/unix/rand.rs | 94 +++++++++++++++++++------------- 1 file changed, 56 insertions(+), 38 deletions(-) diff --git a/library/std/src/sys/unix/rand.rs b/library/std/src/sys/unix/rand.rs index a6fe07873d7ee..40885417308b8 100644 --- a/library/std/src/sys/unix/rand.rs +++ b/library/std/src/sys/unix/rand.rs @@ -137,11 +137,9 @@ mod imp { } } -#[cfg(target_os = "macos")] +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))] mod imp { - use crate::fs::File; - use crate::io::Read; - use crate::sys::os::errno; + use crate::io; use crate::sys::weak::weak; use libc::{c_int, c_void, size_t}; @@ -155,7 +153,7 @@ mod imp { for s in v.chunks_mut(256) { let ret = unsafe { f(s.as_mut_ptr() as *mut c_void, s.len()) }; if ret == -1 { - panic!("unexpected getentropy error: {}", errno()); + panic!("unexpected getentropy error: {}", io::Error::last_os_error()); } } true @@ -163,14 +161,64 @@ mod imp { .unwrap_or(false) } + #[cfg(target_os = "macos")] + fn fallback_fill_bytes(v: &mut [u8]) { + use crate::fs::File; + use crate::io::Read; + + let mut file = File::open("/dev/urandom").expect("failed to open /dev/urandom"); + file.read_exact(v).expect("failed to read /dev/urandom") + } + + // On iOS and MacOS `SecRandomCopyBytes` calls `CCRandomCopyBytes` with + // `kCCRandomDefault`. `CCRandomCopyBytes` manages a CSPRNG which is seeded + // from `/dev/random` and which runs on its own thread accessed via GCD. + // + // This is very heavyweight compared to the alternatives, but they may not be usable: + // - `getentropy` was added in iOS 10, but we support a minimum of iOS 7 + // - `/dev/urandom` is not accessible inside the iOS app sandbox. + // + // Therefore `SecRandomCopyBytes` is only used on older iOS versions where no + // better options are present. + #[cfg(target_os = "ios")] + fn fallback_fill_bytes(v: &mut [u8]) { + use crate::ptr; + + enum SecRandom {} + + #[allow(non_upper_case_globals)] + const kSecRandomDefault: *const SecRandom = ptr::null(); + + extern "C" { + fn SecRandomCopyBytes(rnd: *const SecRandom, count: size_t, bytes: *mut u8) -> c_int; + } + + let ret = unsafe { SecRandomCopyBytes(kSecRandomDefault, v.len(), v.as_mut_ptr()) }; + if ret == -1 { + panic!("couldn't generate random bytes: {}", io::Error::last_os_error()); + } + } + + // All supported versions of watchOS (>= 5) have support for `getentropy`. + #[cfg(target_os = "watchos")] + #[cold] + fn fallback_fill_bytes(_: &mut [u8]) { + unreachable!() + } + pub fn fill_bytes(v: &mut [u8]) { if getentropy_fill_bytes(v) { return; } - // for older macos which doesn't support getentropy - let mut file = File::open("/dev/urandom").expect("failed to open /dev/urandom"); - file.read_exact(v).expect("failed to read /dev/urandom") + // Older macOS versions (< 10.12) don't support `getentropy`. Fallback to + // reading from `/dev/urandom` on these systems. + // + // Older iOS versions (< 10) don't support it either. Fallback to + // `SecRandomCopyBytes` on these systems. On watchOS, this is unreachable + // because the minimum supported version is 5 while `getentropy` became accessible + // in 3. + fallback_fill_bytes(v) } } @@ -189,36 +237,6 @@ mod imp { } } -// On iOS and MacOS `SecRandomCopyBytes` calls `CCRandomCopyBytes` with -// `kCCRandomDefault`. `CCRandomCopyBytes` manages a CSPRNG which is seeded -// from `/dev/random` and which runs on its own thread accessed via GCD. -// This seems needlessly heavyweight for the purposes of generating two u64s -// once per thread in `hashmap_random_keys`. Therefore `SecRandomCopyBytes` is -// only used on iOS where direct access to `/dev/urandom` is blocked by the -// sandbox. -#[cfg(any(target_os = "ios", target_os = "watchos"))] -mod imp { - use crate::io; - use crate::ptr; - use libc::{c_int, size_t}; - - enum SecRandom {} - - #[allow(non_upper_case_globals)] - const kSecRandomDefault: *const SecRandom = ptr::null(); - - extern "C" { - fn SecRandomCopyBytes(rnd: *const SecRandom, count: size_t, bytes: *mut u8) -> c_int; - } - - pub fn fill_bytes(v: &mut [u8]) { - let ret = unsafe { SecRandomCopyBytes(kSecRandomDefault, v.len(), v.as_mut_ptr()) }; - if ret == -1 { - panic!("couldn't generate random bytes: {}", io::Error::last_os_error()); - } - } -} - #[cfg(any(target_os = "freebsd", target_os = "netbsd"))] mod imp { use crate::ptr; From 908ac84662fa0e9478f3e44892146c8d1ffafa50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Marie?= Date: Fri, 26 Aug 2022 06:15:54 +0000 Subject: [PATCH 02/24] openbsd: rustc_target: reorder spec by name --- compiler/rustc_target/src/spec/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index cf6cb75d49a50..46fea26283636 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -893,9 +893,9 @@ supported_targets! { ("aarch64-unknown-openbsd", aarch64_unknown_openbsd), ("i686-unknown-openbsd", i686_unknown_openbsd), + ("powerpc-unknown-openbsd", powerpc_unknown_openbsd), ("sparc64-unknown-openbsd", sparc64_unknown_openbsd), ("x86_64-unknown-openbsd", x86_64_unknown_openbsd), - ("powerpc-unknown-openbsd", powerpc_unknown_openbsd), ("aarch64-unknown-netbsd", aarch64_unknown_netbsd), ("armv6-unknown-netbsd-eabihf", armv6_unknown_netbsd_eabihf), From e89d4fcc7d99bcf76464d9c0994d54d12e00dfb7 Mon Sep 17 00:00:00 2001 From: Jean CASPAR <55629512+JeanCASPAR@users.noreply.github.com> Date: Fri, 26 Aug 2022 18:03:41 +0200 Subject: [PATCH 03/24] remove span_fatal from ast_lowering --- compiler/rustc_ast_lowering/src/errors.rs | 7 +++++++ compiler/rustc_ast_lowering/src/expr.rs | 6 +++--- compiler/rustc_ast_lowering/src/lib.rs | 2 ++ .../rustc_error_messages/locales/en-US/ast_lowering.ftl | 2 ++ 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs index 59f1b7180e4f4..4adeaef9bbfa8 100644 --- a/compiler/rustc_ast_lowering/src/errors.rs +++ b/compiler/rustc_ast_lowering/src/errors.rs @@ -327,3 +327,10 @@ pub struct ArbitraryExpressionInPattern { #[primary_span] pub span: Span, } + +#[derive(SessionDiagnostic, Clone, Copy)] +#[diag(ast_lowering::inclusive_range_with_no_end)] +pub struct InclusiveRangeWithNoEnd { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 61f8c0216f1cf..6d8ce62d91327 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -1,8 +1,8 @@ use super::errors::{ AsyncGeneratorsNotSupported, AsyncNonMoveClosureNotSupported, AwaitOnlyInAsyncFnAndBlocks, BaseExpressionDoubleDot, ClosureCannotBeStatic, FunctionalRecordUpdateDestructuringAssignemnt, - GeneratorTooManyParameters, NotSupportedForLifetimeBinderAsyncClosure, RustcBoxAttributeError, - UnderscoreExprLhsAssign, + GeneratorTooManyParameters, InclusiveRangeWithNoEnd, NotSupportedForLifetimeBinderAsyncClosure, + RustcBoxAttributeError, UnderscoreExprLhsAssign, }; use super::ResolverAstLoweringExt; use super::{ImplTraitContext, LoweringContext, ParamMode, ParenthesizedGenericArgs}; @@ -1264,7 +1264,7 @@ impl<'hir> LoweringContext<'_, 'hir> { (Some(..), Some(..), HalfOpen) => hir::LangItem::Range, (None, Some(..), Closed) => hir::LangItem::RangeToInclusive, (Some(..), Some(..), Closed) => unreachable!(), - (_, None, Closed) => self.diagnostic().span_fatal(span, "inclusive range with no end"), + (_, None, Closed) => self.tcx.sess.emit_fatal(InclusiveRangeWithNoEnd { span }), }; let fields = self.arena.alloc_from_iter( diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index becb67d116546..f7a204ce663c1 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -35,6 +35,8 @@ #![feature(never_type)] #![recursion_limit = "256"] #![allow(rustc::potential_query_instability)] +#![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::diagnostic_outside_of_impl)] #[macro_use] extern crate tracing; diff --git a/compiler/rustc_error_messages/locales/en-US/ast_lowering.ftl b/compiler/rustc_error_messages/locales/en-US/ast_lowering.ftl index dcb1e2b08306f..f2790531aba44 100644 --- a/compiler/rustc_error_messages/locales/en-US/ast_lowering.ftl +++ b/compiler/rustc_error_messages/locales/en-US/ast_lowering.ftl @@ -129,3 +129,5 @@ ast_lowering_not_supported_for_lifetime_binder_async_closure = ast_lowering_arbitrary_expression_in_pattern = arbitrary expressions aren't allowed in patterns + +ast_lowering_inclusive_range_with_no_end = inclusive range with no end From dca5f5bf8f55faaab84fb23db1b4c5ebdc404c0c Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 27 Aug 2022 20:56:14 +0000 Subject: [PATCH 04/24] Drive-by: Rename expr_t to base_ty --- compiler/rustc_typeck/src/check/expr.rs | 52 ++++++++++++------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 71ae54bedce46..604ba22a1236f 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -2141,15 +2141,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { field: Ident, ) -> Ty<'tcx> { debug!("check_field(expr: {:?}, base: {:?}, field: {:?})", expr, base, field); - let expr_t = self.check_expr(base); - let expr_t = self.structurally_resolved_type(base.span, expr_t); + let base_ty = self.check_expr(base); + let base_ty = self.structurally_resolved_type(base.span, base_ty); let mut private_candidate = None; - let mut autoderef = self.autoderef(expr.span, expr_t); - while let Some((base_t, _)) = autoderef.next() { - debug!("base_t: {:?}", base_t); - match base_t.kind() { + let mut autoderef = self.autoderef(expr.span, base_ty); + while let Some((deref_base_ty, _)) = autoderef.next() { + debug!("deref_base_ty: {:?}", deref_base_ty); + match deref_base_ty.kind() { ty::Adt(base_def, substs) if !base_def.is_enum() => { - debug!("struct named {:?}", base_t); + debug!("struct named {:?}", deref_base_ty); let (ident, def_scope) = self.tcx.adjust_ident_and_get_scope(field, base_def.did(), self.body_id); let fields = &base_def.non_enum_variant().fields; @@ -2197,23 +2197,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // (#90483) apply adjustments to avoid ExprUseVisitor from // creating erroneous projection. self.apply_adjustments(base, adjustments); - self.ban_private_field_access(expr, expr_t, field, did); + self.ban_private_field_access(expr, base_ty, field, did); return field_ty; } if field.name == kw::Empty { - } else if self.method_exists(field, expr_t, expr.hir_id, true) { - self.ban_take_value_of_method(expr, expr_t, field); - } else if !expr_t.is_primitive_ty() { - self.ban_nonexisting_field(field, base, expr, expr_t); + } else if self.method_exists(field, base_ty, expr.hir_id, true) { + self.ban_take_value_of_method(expr, base_ty, field); + } else if !base_ty.is_primitive_ty() { + self.ban_nonexisting_field(field, base, expr, base_ty); } else { let field_name = field.to_string(); let mut err = type_error_struct!( self.tcx().sess, field.span, - expr_t, + base_ty, E0610, - "`{expr_t}` is a primitive type and therefore doesn't have fields", + "`{base_ty}` is a primitive type and therefore doesn't have fields", ); let is_valid_suffix = |field: &str| { if field == "f32" || field == "f64" { @@ -2251,7 +2251,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None } }; - if let ty::Infer(ty::IntVar(_)) = expr_t.kind() + if let ty::Infer(ty::IntVar(_)) = base_ty.kind() && let ExprKind::Lit(Spanned { node: ast::LitKind::Int(_, ast::LitIntType::Unsuffixed), .. @@ -2351,32 +2351,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn ban_nonexisting_field( &self, - field: Ident, + ident: Ident, base: &'tcx hir::Expr<'tcx>, expr: &'tcx hir::Expr<'tcx>, - expr_t: Ty<'tcx>, + base_ty: Ty<'tcx>, ) { debug!( - "ban_nonexisting_field: field={:?}, base={:?}, expr={:?}, expr_ty={:?}", - field, base, expr, expr_t + "ban_nonexisting_field: field={:?}, base={:?}, expr={:?}, base_ty={:?}", + ident, base, expr, base_ty ); - let mut err = self.no_such_field_err(field, expr_t, base.hir_id); + let mut err = self.no_such_field_err(ident, base_ty, base.hir_id); - match *expr_t.peel_refs().kind() { + match *base_ty.peel_refs().kind() { ty::Array(_, len) => { - self.maybe_suggest_array_indexing(&mut err, expr, base, field, len); + self.maybe_suggest_array_indexing(&mut err, expr, base, ident, len); } ty::RawPtr(..) => { - self.suggest_first_deref_field(&mut err, expr, base, field); + self.suggest_first_deref_field(&mut err, expr, base, ident); } ty::Adt(def, _) if !def.is_enum() => { - self.suggest_fields_on_recordish(&mut err, def, field, expr.span); + self.suggest_fields_on_recordish(&mut err, def, ident, expr.span); } ty::Param(param_ty) => { self.point_at_param_definition(&mut err, param_ty); } ty::Opaque(_, _) => { - self.suggest_await_on_field_access(&mut err, field, base, expr_t.peel_refs()); + self.suggest_await_on_field_access(&mut err, ident, base, base_ty.peel_refs()); } ty::FnDef(def_id, _) => { self.check_call_constructor(&mut err, base, def_id); @@ -2384,7 +2384,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => {} } - if field.name == kw::Await { + if ident.name == kw::Await { // We know by construction that `.await` is either on Rust 2015 // or results in `ExprKind::Await`. Suggest switching the edition to 2018. err.note("to `.await` a `Future`, switch to Rust 2018 or later"); From 0734200e801d52662f02aff7fdd5026337798620 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 27 Aug 2022 20:53:04 +0000 Subject: [PATCH 05/24] Use autoderef --- .../src/check/fn_ctxt/suggestions.rs | 94 ++++++++++--------- src/test/ui/suggestions/call-boxed.rs | 7 ++ src/test/ui/suggestions/call-boxed.stderr | 20 ++++ 3 files changed, 78 insertions(+), 43 deletions(-) create mode 100644 src/test/ui/suggestions/call-boxed.rs create mode 100644 src/test/ui/suggestions/call-boxed.stderr diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs index 57771e0969bac..45872a0b07aeb 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs @@ -61,55 +61,64 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pointing_at_return_type } - /// When encountering an fn-like ctor that needs to unify with a value, check whether calling - /// the ctor would successfully solve the type mismatch and if so, suggest it: + /// When encountering an fn-like type, try accessing the output of the type + /// // and suggesting calling it if it satisfies a predicate (i.e. if the + /// output has a method or a field): /// ```compile_fail,E0308 /// fn foo(x: usize) -> usize { x } /// let x: usize = foo; // suggest calling the `foo` function: `foo(42)` /// ``` - fn suggest_fn_call( + pub(crate) fn suggest_fn_call( &self, err: &mut Diagnostic, expr: &hir::Expr<'_>, - expected: Ty<'tcx>, found: Ty<'tcx>, + can_satisfy: impl FnOnce(Ty<'tcx>) -> bool, ) -> bool { - let (def_id, output, inputs) = match *found.kind() { - ty::FnDef(def_id, _) => { - let fn_sig = found.fn_sig(self.tcx); - (def_id, fn_sig.output(), fn_sig.inputs().skip_binder().len()) - } - ty::Closure(def_id, substs) => { - let fn_sig = substs.as_closure().sig(); - (def_id, fn_sig.output(), fn_sig.inputs().skip_binder().len() - 1) - } - ty::Opaque(def_id, substs) => { - let sig = self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| { - if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder() - && Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output() - // args tuple will always be substs[1] - && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind() - { - Some(( - pred.kind().rebind(proj.term.ty().unwrap()), - args.len(), - )) + // Autoderef is useful here because sometimes we box callables, etc. + let Some((def_id, output, inputs)) = self.autoderef(expr.span, found).silence_errors().find_map(|(found, _)| { + match *found.kind() { + ty::FnPtr(fn_sig) => Some((None, fn_sig.output(), fn_sig.inputs().skip_binder().len())), + ty::FnDef(def_id, _) => { + let fn_sig = found.fn_sig(self.tcx); + Some((Some(def_id), fn_sig.output(), fn_sig.inputs().skip_binder().len())) + } + ty::Closure(def_id, substs) => { + let fn_sig = substs.as_closure().sig(); + Some((Some(def_id), fn_sig.output(), fn_sig.inputs().skip_binder().len() - 1)) + } + ty::Opaque(def_id, substs) => { + let sig = self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| { + if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder() + && Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output() + // args tuple will always be substs[1] + && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind() + { + Some(( + pred.kind().rebind(proj.term.ty().unwrap()), + args.len(), + )) + } else { + None + } + }); + if let Some((output, inputs)) = sig { + Some((Some(def_id), output, inputs)) } else { None } - }); - if let Some((output, inputs)) = sig { - (def_id, output, inputs) - } else { - return false; } + _ => None, } - _ => return false, - }; + }) else { return false; }; let output = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, output); - let output = self.normalize_associated_types_in(expr.span, output); - if !output.is_ty_var() && self.can_coerce(output, expected) { + // We don't want to register any extra obligations, which should be + // implied by wf, but also because that would possibly result in + // erroneous errors later on. + let infer::InferOk { value: output, obligations: _ } = + self.normalize_associated_types_in_as_infer_ok(expr.span, output); + if !output.is_ty_var() && can_satisfy(output) { let (sugg_call, mut applicability) = match inputs { 0 => ("".to_string(), Applicability::MachineApplicable), 1..=4 => ( @@ -119,11 +128,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => ("...".to_string(), Applicability::HasPlaceholders), }; - let msg = match self.tcx.def_kind(def_id) { - DefKind::Fn => "call this function", - DefKind::Closure | DefKind::OpaqueTy => "call this closure", - DefKind::Ctor(CtorOf::Struct, _) => "instantiate this tuple struct", - DefKind::Ctor(CtorOf::Variant, _) => "instantiate this tuple variant", + let msg = match def_id.map(|def_id| self.tcx.def_kind(def_id)) { + Some(DefKind::Fn) => "call this function", + Some(DefKind::Closure | DefKind::OpaqueTy) => "call this closure", + Some(DefKind::Ctor(CtorOf::Struct, _)) => "instantiate this tuple struct", + Some(DefKind::Ctor(CtorOf::Variant, _)) => "instantiate this tuple variant", _ => "call this function", }; @@ -178,12 +187,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { err.span_suggestion(sp, &msg, suggestion, applicability); } - } else if let (ty::FnDef(def_id, ..), true) = - (&found.kind(), self.suggest_fn_call(err, expr, expected, found)) + } else if self.suggest_fn_call(err, expr, found, |output| self.can_coerce(output, expected)) + && let ty::FnDef(def_id, ..) = &found.kind() + && let Some(sp) = self.tcx.hir().span_if_local(*def_id) { - if let Some(sp) = self.tcx.hir().span_if_local(*def_id) { - err.span_label(sp, format!("{found} defined here")); - } + err.span_label(sp, format!("{found} defined here")); } else if !self.check_for_cast(err, expr, found, expected, expected_ty_expr) { let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id); if !methods.is_empty() { diff --git a/src/test/ui/suggestions/call-boxed.rs b/src/test/ui/suggestions/call-boxed.rs new file mode 100644 index 0000000000000..d19e4596a0cc1 --- /dev/null +++ b/src/test/ui/suggestions/call-boxed.rs @@ -0,0 +1,7 @@ +fn main() { + let mut x = 1i32; + let y = Box::new(|| 1); + x = y; + //~^ ERROR mismatched types + //~| HELP use parentheses to call this closure +} diff --git a/src/test/ui/suggestions/call-boxed.stderr b/src/test/ui/suggestions/call-boxed.stderr new file mode 100644 index 0000000000000..9b619ac9a3f50 --- /dev/null +++ b/src/test/ui/suggestions/call-boxed.stderr @@ -0,0 +1,20 @@ +error[E0308]: mismatched types + --> $DIR/call-boxed.rs:4:9 + | +LL | let mut x = 1i32; + | ---- expected due to this value +LL | let y = Box::new(|| 1); + | -- the found closure +LL | x = y; + | ^ expected `i32`, found struct `Box` + | + = note: expected type `i32` + found struct `Box<[closure@$DIR/call-boxed.rs:3:22: 3:24]>` +help: use parentheses to call this closure + | +LL | x = y(); + | ++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. From 703603a3623051b9f1a882b389f40f6a6dc5cfb0 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 27 Aug 2022 20:59:07 +0000 Subject: [PATCH 06/24] Only suggest call on nonexistent fields and methods if they make sense --- compiler/rustc_typeck/src/check/expr.rs | 50 ++++++----------- .../rustc_typeck/src/check/method/suggest.rs | 55 ++++++------------- compiler/rustc_typeck/src/check/mod.rs | 29 ---------- .../ui/functions-closures/fn-help-with-err.rs | 28 +++++++--- .../fn-help-with-err.stderr | 38 +++++++++---- src/test/ui/issues/issue-57362-1.stderr | 4 +- src/test/ui/typeck/issue-29124.stderr | 8 +-- .../typeck/issue-87181/empty-tuple-method.rs | 2 +- .../issue-87181/empty-tuple-method.stderr | 6 +- .../ui/typeck/issue-87181/enum-variant.rs | 2 +- .../ui/typeck/issue-87181/enum-variant.stderr | 6 +- .../ui/typeck/issue-87181/tuple-field.stderr | 6 +- .../ui/typeck/issue-87181/tuple-method.stderr | 9 +-- src/test/ui/typeck/issue-96738.stderr | 18 +----- ...ed-closures-static-call-wrong-trait.stderr | 4 +- 15 files changed, 94 insertions(+), 171 deletions(-) diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 604ba22a1236f..1ae4075aab631 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -21,7 +21,6 @@ use crate::errors::{ }; use crate::type_error_struct; -use super::suggest_call_constructor; use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive}; use rustc_ast as ast; use rustc_data_structures::fx::FxHashMap; @@ -44,7 +43,7 @@ use rustc_middle::middle::stability; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase}; use rustc_middle::ty::error::TypeError::FieldMisMatch; use rustc_middle::ty::subst::SubstsRef; -use rustc_middle::ty::{self, AdtKind, DefIdTree, Ty, TypeVisitable}; +use rustc_middle::ty::{self, AdtKind, Ty, TypeVisitable}; use rustc_session::parse::feature_err; use rustc_span::hygiene::DesugaringKind; use rustc_span::lev_distance::find_best_match_for_name; @@ -2280,35 +2279,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx().ty_error() } - fn check_call_constructor( - &self, - err: &mut Diagnostic, - base: &'tcx hir::Expr<'tcx>, - def_id: DefId, - ) { - if let Some(local_id) = def_id.as_local() { - let hir_id = self.tcx.hir().local_def_id_to_hir_id(local_id); - let node = self.tcx.hir().get(hir_id); - - if let Some(fields) = node.tuple_fields() { - let kind = match self.tcx.opt_def_kind(local_id) { - Some(DefKind::Ctor(of, _)) => of, - _ => return, - }; - - suggest_call_constructor(base.span, kind, fields.len(), err); - } - } else { - // The logic here isn't smart but `associated_item_def_ids` - // doesn't work nicely on local. - if let DefKind::Ctor(of, _) = self.tcx.def_kind(def_id) { - let parent_def_id = self.tcx.parent(def_id); - let fields = self.tcx.associated_item_def_ids(parent_def_id); - suggest_call_constructor(base.span, of, fields.len(), err); - } - } - } - fn suggest_await_on_field_access( &self, err: &mut Diagnostic, @@ -2378,12 +2348,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::Opaque(_, _) => { self.suggest_await_on_field_access(&mut err, ident, base, base_ty.peel_refs()); } - ty::FnDef(def_id, _) => { - self.check_call_constructor(&mut err, base, def_id); - } _ => {} } + self.suggest_fn_call(&mut err, base, base_ty, |output_ty| { + if let ty::Adt(def, _) = output_ty.kind() && !def.is_enum() { + def.non_enum_variant().fields.iter().any(|field| { + field.ident(self.tcx) == ident + && field.vis.is_accessible_from(expr.hir_id.owner.to_def_id(), self.tcx) + }) + } else if let ty::Tuple(tys) = output_ty.kind() + && let Ok(idx) = ident.as_str().parse::() + { + idx < tys.len() + } else { + false + } + }); + if ident.name == kw::Await { // We know by construction that `.await` is either on Rust 2015 // or results in `ExprKind::Await`. Suggest switching the edition to 2018. diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs index 441a62256de88..cfedd145fed30 100644 --- a/compiler/rustc_typeck/src/check/method/suggest.rs +++ b/compiler/rustc_typeck/src/check/method/suggest.rs @@ -31,7 +31,7 @@ use std::cmp::Ordering; use std::iter; use super::probe::{Mode, ProbeScope}; -use super::{super::suggest_call_constructor, CandidateSource, MethodError, NoMatchData}; +use super::{CandidateSource, MethodError, NoMatchData}; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool { @@ -363,44 +363,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } - if self.is_fn_ty(rcvr_ty, span) { - if let SelfSource::MethodCall(expr) = source { - let suggest = if let ty::FnDef(def_id, _) = rcvr_ty.kind() { - if let Some(local_id) = def_id.as_local() { - let hir_id = tcx.hir().local_def_id_to_hir_id(local_id); - let node = tcx.hir().get(hir_id); - let fields = node.tuple_fields(); - if let Some(fields) = fields - && let Some(DefKind::Ctor(of, _)) = self.tcx.opt_def_kind(local_id) { - Some((fields.len(), of)) - } else { - None - } - } else { - // The logic here isn't smart but `associated_item_def_ids` - // doesn't work nicely on local. - if let DefKind::Ctor(of, _) = tcx.def_kind(def_id) { - let parent_def_id = tcx.parent(*def_id); - Some((tcx.associated_item_def_ids(parent_def_id).len(), of)) - } else { - None - } - } - } else { - None - }; - - // If the function is a tuple constructor, we recommend that they call it - if let Some((fields, kind)) = suggest { - suggest_call_constructor(expr.span, kind, fields, &mut err); - } else { - // General case - err.span_label( - expr.span, - "this is a function, perhaps you wish to call it", - ); - } - } + if let SelfSource::MethodCall(rcvr_expr) = source { + self.suggest_fn_call(&mut err, rcvr_expr, rcvr_ty, |output_ty| { + let call_expr = self + .tcx + .hir() + .expect_expr(self.tcx.hir().get_parent_node(rcvr_expr.hir_id)); + let probe = self.lookup_probe( + span, + item_name, + output_ty, + call_expr, + ProbeScope::AllTraits, + ); + probe.is_ok() + }); } let mut custom_span_label = false; diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_typeck/src/check/mod.rs index fb675212e3ffb..343ea9e0fa5a1 100644 --- a/compiler/rustc_typeck/src/check/mod.rs +++ b/compiler/rustc_typeck/src/check/mod.rs @@ -96,7 +96,6 @@ use check::{check_abi, check_fn, check_mod_item_types}; pub use diverges::Diverges; pub use expectation::Expectation; pub use fn_ctxt::*; -use hir::def::CtorOf; pub use inherited::{Inherited, InheritedBuilder}; use crate::astconv::AstConv; @@ -960,31 +959,3 @@ fn has_expected_num_generic_args<'tcx>( generics.count() == expected + if generics.has_self { 1 } else { 0 } }) } - -/// Suggests calling the constructor of a tuple struct or enum variant -/// -/// * `snippet` - The snippet of code that references the constructor -/// * `span` - The span of the snippet -/// * `params` - The number of parameters the constructor accepts -/// * `err` - A mutable diagnostic builder to add the suggestion to -fn suggest_call_constructor(span: Span, kind: CtorOf, params: usize, err: &mut Diagnostic) { - // Note: tuple-structs don't have named fields, so just use placeholders - let args = vec!["_"; params].join(", "); - let applicable = if params > 0 { - Applicability::HasPlaceholders - } else { - // When n = 0, it's an empty-tuple struct/enum variant - // so we trivially know how to construct it - Applicability::MachineApplicable - }; - let kind = match kind { - CtorOf::Struct => "a struct", - CtorOf::Variant => "an enum variant", - }; - err.span_label(span, &format!("this is the constructor of {kind}")); - err.multipart_suggestion( - "call the constructor", - vec![(span.shrink_to_lo(), "(".to_string()), (span.shrink_to_hi(), format!(")({args})"))], - applicable, - ); -} diff --git a/src/test/ui/functions-closures/fn-help-with-err.rs b/src/test/ui/functions-closures/fn-help-with-err.rs index 3d2bcb8ad3550..49a514a8b4e34 100644 --- a/src/test/ui/functions-closures/fn-help-with-err.rs +++ b/src/test/ui/functions-closures/fn-help-with-err.rs @@ -1,16 +1,28 @@ // This test case checks the behavior of typeck::check::method::suggest::is_fn on Ty::Error. + +struct Foo; + +trait Bar { + //~^ NOTE `Bar` defines an item `bar`, perhaps you need to implement it + //~| NOTE `Bar` defines an item `bar`, perhaps you need to implement it + fn bar(&self) {} +} + +impl Bar for Foo {} + fn main() { let arc = std::sync::Arc::new(oops); //~^ ERROR cannot find value `oops` in this scope //~| NOTE not found - // The error "note: this is a function, perhaps you wish to call it" MUST NOT appear. - arc.blablabla(); - //~^ ERROR no method named `blablabla` + arc.bar(); + //~^ ERROR no method named `bar` //~| NOTE method not found - let arc2 = std::sync::Arc::new(|| 1); - // The error "note: this is a function, perhaps you wish to call it" SHOULD appear - arc2.blablabla(); - //~^ ERROR no method named `blablabla` + //~| HELP items from traits can only be used if the trait is implemented and in scope + + let arc2 = std::sync::Arc::new(|| Foo); + arc2.bar(); + //~^ ERROR no method named `bar` //~| NOTE method not found - //~| NOTE this is a function, perhaps you wish to call it + //~| HELP items from traits can only be used if the trait is implemented and in scope + //~| HELP use parentheses to call this closure } diff --git a/src/test/ui/functions-closures/fn-help-with-err.stderr b/src/test/ui/functions-closures/fn-help-with-err.stderr index 06e29daef456c..2296666219eef 100644 --- a/src/test/ui/functions-closures/fn-help-with-err.stderr +++ b/src/test/ui/functions-closures/fn-help-with-err.stderr @@ -1,22 +1,38 @@ error[E0425]: cannot find value `oops` in this scope - --> $DIR/fn-help-with-err.rs:3:35 + --> $DIR/fn-help-with-err.rs:14:35 | LL | let arc = std::sync::Arc::new(oops); | ^^^^ not found in this scope -error[E0599]: no method named `blablabla` found for struct `Arc<_>` in the current scope - --> $DIR/fn-help-with-err.rs:7:9 +error[E0599]: no method named `bar` found for struct `Arc<_>` in the current scope + --> $DIR/fn-help-with-err.rs:17:9 | -LL | arc.blablabla(); - | ^^^^^^^^^ method not found in `Arc<_>` +LL | arc.bar(); + | ^^^ method not found in `Arc<_>` + | + = help: items from traits can only be used if the trait is implemented and in scope +note: `Bar` defines an item `bar`, perhaps you need to implement it + --> $DIR/fn-help-with-err.rs:5:1 + | +LL | trait Bar { + | ^^^^^^^^^ -error[E0599]: no method named `blablabla` found for struct `Arc<[closure@$DIR/fn-help-with-err.rs:10:36: 10:38]>` in the current scope - --> $DIR/fn-help-with-err.rs:12:10 +error[E0599]: no method named `bar` found for struct `Arc<[closure@$DIR/fn-help-with-err.rs:22:36: 22:38]>` in the current scope + --> $DIR/fn-help-with-err.rs:23:10 + | +LL | arc2.bar(); + | ^^^ method not found in `Arc<[closure@$DIR/fn-help-with-err.rs:22:36: 22:38]>` + | + = help: items from traits can only be used if the trait is implemented and in scope +note: `Bar` defines an item `bar`, perhaps you need to implement it + --> $DIR/fn-help-with-err.rs:5:1 + | +LL | trait Bar { + | ^^^^^^^^^ +help: use parentheses to call this closure | -LL | arc2.blablabla(); - | ---- ^^^^^^^^^ method not found in `Arc<[closure@$DIR/fn-help-with-err.rs:10:36: 10:38]>` - | | - | this is a function, perhaps you wish to call it +LL | arc2().bar(); + | ++ error: aborting due to 3 previous errors diff --git a/src/test/ui/issues/issue-57362-1.stderr b/src/test/ui/issues/issue-57362-1.stderr index 8e19f14009a0e..b10273f14bd03 100644 --- a/src/test/ui/issues/issue-57362-1.stderr +++ b/src/test/ui/issues/issue-57362-1.stderr @@ -2,9 +2,7 @@ error[E0599]: no method named `f` found for fn pointer `fn(&u8)` in the current --> $DIR/issue-57362-1.rs:20:7 | LL | a.f(); - | - ^ method not found in `fn(&u8)` - | | - | this is a function, perhaps you wish to call it + | ^ method not found in `fn(&u8)` | = help: items from traits can only be used if the trait is implemented and in scope note: `Trait` defines an item `f`, perhaps you need to implement it diff --git a/src/test/ui/typeck/issue-29124.stderr b/src/test/ui/typeck/issue-29124.stderr index c5d2ec0840996..a837a7d2d62d1 100644 --- a/src/test/ui/typeck/issue-29124.stderr +++ b/src/test/ui/typeck/issue-29124.stderr @@ -2,17 +2,13 @@ error[E0599]: no method named `x` found for fn item `fn() -> Ret {Obj::func}` in --> $DIR/issue-29124.rs:15:15 | LL | Obj::func.x(); - | --------- ^ method not found in `fn() -> Ret {Obj::func}` - | | - | this is a function, perhaps you wish to call it + | ^ method not found in `fn() -> Ret {Obj::func}` error[E0599]: no method named `x` found for fn item `fn() -> Ret {func}` in the current scope --> $DIR/issue-29124.rs:17:10 | LL | func.x(); - | ---- ^ method not found in `fn() -> Ret {func}` - | | - | this is a function, perhaps you wish to call it + | ^ method not found in `fn() -> Ret {func}` error: aborting due to 2 previous errors diff --git a/src/test/ui/typeck/issue-87181/empty-tuple-method.rs b/src/test/ui/typeck/issue-87181/empty-tuple-method.rs index 1875d8280cb62..be68ad32ae55b 100644 --- a/src/test/ui/typeck/issue-87181/empty-tuple-method.rs +++ b/src/test/ui/typeck/issue-87181/empty-tuple-method.rs @@ -4,7 +4,7 @@ struct Bar { struct Foo(); impl Foo { - fn foo() { } + fn foo(&self) { } } fn main() { diff --git a/src/test/ui/typeck/issue-87181/empty-tuple-method.stderr b/src/test/ui/typeck/issue-87181/empty-tuple-method.stderr index 6ed70b301e4a4..a18c54a29b52c 100644 --- a/src/test/ui/typeck/issue-87181/empty-tuple-method.stderr +++ b/src/test/ui/typeck/issue-87181/empty-tuple-method.stderr @@ -2,11 +2,9 @@ error[E0599]: no method named `foo` found for fn item `fn() -> Foo {Foo}` in the --> $DIR/empty-tuple-method.rs:12:15 | LL | thing.bar.foo(); - | --------- ^^^ method not found in `fn() -> Foo {Foo}` - | | - | this is the constructor of a struct + | ^^^ method not found in `fn() -> Foo {Foo}` | -help: call the constructor +help: use parentheses to instantiate this tuple struct | LL | (thing.bar)().foo(); | + +++ diff --git a/src/test/ui/typeck/issue-87181/enum-variant.rs b/src/test/ui/typeck/issue-87181/enum-variant.rs index 3b926b90f10bb..d87f99c3c5a19 100644 --- a/src/test/ui/typeck/issue-87181/enum-variant.rs +++ b/src/test/ui/typeck/issue-87181/enum-variant.rs @@ -6,7 +6,7 @@ enum Foo{ Tup() } impl Foo { - fn foo() { } + fn foo(&self) { } } fn main() { diff --git a/src/test/ui/typeck/issue-87181/enum-variant.stderr b/src/test/ui/typeck/issue-87181/enum-variant.stderr index a3a818696ab5b..90641410d8e96 100644 --- a/src/test/ui/typeck/issue-87181/enum-variant.stderr +++ b/src/test/ui/typeck/issue-87181/enum-variant.stderr @@ -2,11 +2,9 @@ error[E0599]: no method named `foo` found for fn item `fn() -> Foo {Foo::Tup}` i --> $DIR/enum-variant.rs:14:15 | LL | thing.bar.foo(); - | --------- ^^^ method not found in `fn() -> Foo {Foo::Tup}` - | | - | this is the constructor of an enum variant + | ^^^ method not found in `fn() -> Foo {Foo::Tup}` | -help: call the constructor +help: use parentheses to instantiate this tuple variant | LL | (thing.bar)().foo(); | + +++ diff --git a/src/test/ui/typeck/issue-87181/tuple-field.stderr b/src/test/ui/typeck/issue-87181/tuple-field.stderr index 4d22ada0247e9..0e43ace893396 100644 --- a/src/test/ui/typeck/issue-87181/tuple-field.stderr +++ b/src/test/ui/typeck/issue-87181/tuple-field.stderr @@ -2,11 +2,9 @@ error[E0609]: no field `0` on type `fn(char, u16) -> Foo {Foo}` --> $DIR/tuple-field.rs:12:15 | LL | thing.bar.0; - | --------- ^ - | | - | this is the constructor of a struct + | ^ | -help: call the constructor +help: use parentheses to instantiate this tuple struct | LL | (thing.bar)(_, _).0; | + +++++++ diff --git a/src/test/ui/typeck/issue-87181/tuple-method.stderr b/src/test/ui/typeck/issue-87181/tuple-method.stderr index 1e392e17984b0..e27c41858d322 100644 --- a/src/test/ui/typeck/issue-87181/tuple-method.stderr +++ b/src/test/ui/typeck/issue-87181/tuple-method.stderr @@ -2,14 +2,7 @@ error[E0599]: no method named `foo` found for fn item `fn(u8, i32) -> Foo {Foo}` --> $DIR/tuple-method.rs:12:15 | LL | thing.bar.foo(); - | --------- ^^^ method not found in `fn(u8, i32) -> Foo {Foo}` - | | - | this is the constructor of a struct - | -help: call the constructor - | -LL | (thing.bar)(_, _).foo(); - | + +++++++ + | ^^^ method not found in `fn(u8, i32) -> Foo {Foo}` error: aborting due to previous error diff --git a/src/test/ui/typeck/issue-96738.stderr b/src/test/ui/typeck/issue-96738.stderr index 32f53849848c7..0d4d87ef47e2b 100644 --- a/src/test/ui/typeck/issue-96738.stderr +++ b/src/test/ui/typeck/issue-96738.stderr @@ -2,27 +2,13 @@ error[E0599]: no method named `nonexistent_method` found for fn item `fn(_) -> O --> $DIR/issue-96738.rs:2:10 | LL | Some.nonexistent_method(); - | ---- ^^^^^^^^^^^^^^^^^^ method not found in `fn(_) -> Option<_> {Option::<_>::Some}` - | | - | this is the constructor of an enum variant - | -help: call the constructor - | -LL | (Some)(_).nonexistent_method(); - | + ++++ + | ^^^^^^^^^^^^^^^^^^ method not found in `fn(_) -> Option<_> {Option::<_>::Some}` error[E0609]: no field `nonexistent_field` on type `fn(_) -> Option<_> {Option::<_>::Some}` --> $DIR/issue-96738.rs:3:10 | LL | Some.nonexistent_field; - | ---- ^^^^^^^^^^^^^^^^^ - | | - | this is the constructor of an enum variant - | -help: call the constructor - | -LL | (Some)(_).nonexistent_field; - | + ++++ + | ^^^^^^^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/src/test/ui/unboxed-closures/unboxed-closures-static-call-wrong-trait.stderr b/src/test/ui/unboxed-closures/unboxed-closures-static-call-wrong-trait.stderr index 4f89afa320d70..e5ca0edd7a91c 100644 --- a/src/test/ui/unboxed-closures/unboxed-closures-static-call-wrong-trait.stderr +++ b/src/test/ui/unboxed-closures/unboxed-closures-static-call-wrong-trait.stderr @@ -2,9 +2,7 @@ error[E0599]: no method named `call` found for closure `[closure@$DIR/unboxed-cl --> $DIR/unboxed-closures-static-call-wrong-trait.rs:7:10 | LL | mut_.call((0, )); - | ---- ^^^^ method not found in `[closure@$DIR/unboxed-closures-static-call-wrong-trait.rs:6:26: 6:29]` - | | - | this is a function, perhaps you wish to call it + | ^^^^ method not found in `[closure@$DIR/unboxed-closures-static-call-wrong-trait.rs:6:26: 6:29]` error: aborting due to previous error From cef0482d11ec42d50611beca2dbf551094d5025c Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 27 Aug 2022 21:17:17 +0000 Subject: [PATCH 07/24] Add test --- src/test/ui/suggestions/call-on-missing.rs | 19 ++++++++++++++ .../ui/suggestions/call-on-missing.stderr | 26 +++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 src/test/ui/suggestions/call-on-missing.rs create mode 100644 src/test/ui/suggestions/call-on-missing.stderr diff --git a/src/test/ui/suggestions/call-on-missing.rs b/src/test/ui/suggestions/call-on-missing.rs new file mode 100644 index 0000000000000..611b9d40f0f06 --- /dev/null +++ b/src/test/ui/suggestions/call-on-missing.rs @@ -0,0 +1,19 @@ +struct Foo { i: i32 } + +impl Foo { + fn bar(&self) {} +} + +fn foo() -> Foo { + Foo { i: 1 } +} + +fn main() { + foo.bar(); + //~^ ERROR no method named `bar` + //~| HELP use parentheses to call this function + + foo.i; + //~^ ERROR no field `i` + //~| HELP use parentheses to call this function +} diff --git a/src/test/ui/suggestions/call-on-missing.stderr b/src/test/ui/suggestions/call-on-missing.stderr new file mode 100644 index 0000000000000..d8070321e1462 --- /dev/null +++ b/src/test/ui/suggestions/call-on-missing.stderr @@ -0,0 +1,26 @@ +error[E0599]: no method named `bar` found for fn item `fn() -> Foo {foo}` in the current scope + --> $DIR/call-on-missing.rs:12:9 + | +LL | foo.bar(); + | ^^^ method not found in `fn() -> Foo {foo}` + | +help: use parentheses to call this function + | +LL | foo().bar(); + | ++ + +error[E0609]: no field `i` on type `fn() -> Foo {foo}` + --> $DIR/call-on-missing.rs:16:9 + | +LL | foo.i; + | ^ + | +help: use parentheses to call this function + | +LL | foo().i; + | ++ + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0599, E0609. +For more information about an error, try `rustc --explain E0599`. From 2f78dd15a6f29894e5876582cc84e8a5faa539c1 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 27 Aug 2022 23:08:21 +0000 Subject: [PATCH 08/24] Suggest calling trait objects and parameters too, when possible --- .../src/check/fn_ctxt/suggestions.rs | 75 ++++++++++++++----- .../substs-ppaux.normal.stderr | 6 +- .../substs-ppaux.verbose.stderr | 6 +- src/test/ui/fn/fn-trait-formatting.stderr | 8 ++ .../suggest-calling-rpit-closure.stderr | 2 +- src/test/ui/suggestions/call-on-missing.rs | 20 +++++ .../ui/suggestions/call-on-missing.stderr | 51 ++++++++++++- .../fn-or-tuple-struct-without-args.stderr | 16 ++-- 8 files changed, 151 insertions(+), 33 deletions(-) diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs index 45872a0b07aeb..4eb0f045d7791 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs @@ -2,6 +2,7 @@ use super::FnCtxt; use crate::astconv::AstConv; use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel}; +use hir::def_id::DefId; use rustc_ast::util::parser::ExprPrecedence; use rustc_errors::{Applicability, Diagnostic, MultiSpan}; use rustc_hir as hir; @@ -75,38 +76,75 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { found: Ty<'tcx>, can_satisfy: impl FnOnce(Ty<'tcx>) -> bool, ) -> bool { + enum DefIdOrName { + DefId(DefId), + Name(&'static str), + } // Autoderef is useful here because sometimes we box callables, etc. - let Some((def_id, output, inputs)) = self.autoderef(expr.span, found).silence_errors().find_map(|(found, _)| { + let Some((def_id_or_name, output, inputs)) = self.autoderef(expr.span, found).silence_errors().find_map(|(found, _)| { match *found.kind() { - ty::FnPtr(fn_sig) => Some((None, fn_sig.output(), fn_sig.inputs().skip_binder().len())), + ty::FnPtr(fn_sig) => + Some((DefIdOrName::Name("function pointer"), fn_sig.output(), fn_sig.inputs().skip_binder().len())), ty::FnDef(def_id, _) => { let fn_sig = found.fn_sig(self.tcx); - Some((Some(def_id), fn_sig.output(), fn_sig.inputs().skip_binder().len())) + Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs().skip_binder().len())) } ty::Closure(def_id, substs) => { let fn_sig = substs.as_closure().sig(); - Some((Some(def_id), fn_sig.output(), fn_sig.inputs().skip_binder().len() - 1)) + Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs().skip_binder().len() - 1)) } ty::Opaque(def_id, substs) => { - let sig = self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| { + self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| { if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder() && Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output() // args tuple will always be substs[1] && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind() { Some(( + DefIdOrName::DefId(def_id), pred.kind().rebind(proj.term.ty().unwrap()), args.len(), )) } else { None } - }); - if let Some((output, inputs)) = sig { - Some((Some(def_id), output, inputs)) - } else { - None - } + }) + } + ty::Dynamic(data, _) => { + data.iter().find_map(|pred| { + if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder() + && Some(proj.item_def_id) == self.tcx.lang_items().fn_once_output() + // for existential projection, substs are shifted over by 1 + && let ty::Tuple(args) = proj.substs.type_at(0).kind() + { + Some(( + DefIdOrName::Name("trait object"), + pred.rebind(proj.term.ty().unwrap()), + args.len(), + )) + } else { + None + } + }) + } + ty::Param(param) => { + let def_id = self.tcx.generics_of(self.body_id.owner).type_param(¶m, self.tcx).def_id; + self.tcx.predicates_of(self.body_id.owner).predicates.iter().find_map(|(pred, _)| { + if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder() + && Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output() + && proj.projection_ty.self_ty() == found + // args tuple will always be substs[1] + && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind() + { + Some(( + DefIdOrName::DefId(def_id), + pred.kind().rebind(proj.term.ty().unwrap()), + args.len(), + )) + } else { + None + } + }) } _ => None, } @@ -128,12 +166,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => ("...".to_string(), Applicability::HasPlaceholders), }; - let msg = match def_id.map(|def_id| self.tcx.def_kind(def_id)) { - Some(DefKind::Fn) => "call this function", - Some(DefKind::Closure | DefKind::OpaqueTy) => "call this closure", - Some(DefKind::Ctor(CtorOf::Struct, _)) => "instantiate this tuple struct", - Some(DefKind::Ctor(CtorOf::Variant, _)) => "instantiate this tuple variant", - _ => "call this function", + let msg = match def_id_or_name { + DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) { + DefKind::Ctor(CtorOf::Struct, _) => "instantiate this tuple struct".to_string(), + DefKind::Ctor(CtorOf::Variant, _) => { + "instantiate this tuple variant".to_string() + } + kind => format!("call this {}", kind.descr(def_id)), + }, + DefIdOrName::Name(name) => format!("call this {name}"), }; let sugg = match expr.kind { diff --git a/src/test/ui/associated-types/substs-ppaux.normal.stderr b/src/test/ui/associated-types/substs-ppaux.normal.stderr index 501d2cfaa26b7..3f180cf4f1f89 100644 --- a/src/test/ui/associated-types/substs-ppaux.normal.stderr +++ b/src/test/ui/associated-types/substs-ppaux.normal.stderr @@ -11,7 +11,7 @@ LL | let x: () = >::bar::<'static, char>; | = note: expected unit type `()` found fn item `fn() {>::bar::<'static, char>}` -help: use parentheses to call this function +help: use parentheses to call this associated function | LL | let x: () = >::bar::<'static, char>(); | ++ @@ -29,7 +29,7 @@ LL | let x: () = >::bar::<'static, char>; | = note: expected unit type `()` found fn item `fn() {>::bar::<'static, char>}` -help: use parentheses to call this function +help: use parentheses to call this associated function | LL | let x: () = >::bar::<'static, char>(); | ++ @@ -47,7 +47,7 @@ LL | let x: () = >::baz; | = note: expected unit type `()` found fn item `fn() {>::baz}` -help: use parentheses to call this function +help: use parentheses to call this associated function | LL | let x: () = >::baz(); | ++ diff --git a/src/test/ui/associated-types/substs-ppaux.verbose.stderr b/src/test/ui/associated-types/substs-ppaux.verbose.stderr index ae3e862dddd20..16dd29de2c543 100644 --- a/src/test/ui/associated-types/substs-ppaux.verbose.stderr +++ b/src/test/ui/associated-types/substs-ppaux.verbose.stderr @@ -11,7 +11,7 @@ LL | let x: () = >::bar::<'static, char>; | = note: expected unit type `()` found fn item `fn() {>::bar::}` -help: use parentheses to call this function +help: use parentheses to call this associated function | LL | let x: () = >::bar::<'static, char>(); | ++ @@ -29,7 +29,7 @@ LL | let x: () = >::bar::<'static, char>; | = note: expected unit type `()` found fn item `fn() {>::bar::}` -help: use parentheses to call this function +help: use parentheses to call this associated function | LL | let x: () = >::bar::<'static, char>(); | ++ @@ -47,7 +47,7 @@ LL | let x: () = >::baz; | = note: expected unit type `()` found fn item `fn() {>::baz}` -help: use parentheses to call this function +help: use parentheses to call this associated function | LL | let x: () = >::baz(); | ++ diff --git a/src/test/ui/fn/fn-trait-formatting.stderr b/src/test/ui/fn/fn-trait-formatting.stderr index ea88e401bed87..1d5e0a859a6da 100644 --- a/src/test/ui/fn/fn-trait-formatting.stderr +++ b/src/test/ui/fn/fn-trait-formatting.stderr @@ -8,6 +8,10 @@ LL | let _: () = Box::new(|_: isize| {}) as Box; | = note: expected unit type `()` found struct `Box` +help: use parentheses to call this trait object + | +LL | let _: () = (Box::new(|_: isize| {}) as Box)(_); + | + ++++ error[E0308]: mismatched types --> $DIR/fn-trait-formatting.rs:10:17 @@ -19,6 +23,10 @@ LL | let _: () = Box::new(|_: isize, isize| {}) as Box | = note: expected unit type `()` found struct `Box` +help: use parentheses to call this trait object + | +LL | let _: () = (Box::new(|_: isize, isize| {}) as Box)(_, _); + | + +++++++ error[E0308]: mismatched types --> $DIR/fn-trait-formatting.rs:14:17 diff --git a/src/test/ui/impl-trait/suggest-calling-rpit-closure.stderr b/src/test/ui/impl-trait/suggest-calling-rpit-closure.stderr index 2a328a0e6f54d..c10a856d83ba8 100644 --- a/src/test/ui/impl-trait/suggest-calling-rpit-closure.stderr +++ b/src/test/ui/impl-trait/suggest-calling-rpit-closure.stderr @@ -11,7 +11,7 @@ LL | fn opaque() -> impl Fn() -> i32 { | = note: expected type `i32` found opaque type `impl Fn() -> i32` -help: use parentheses to call this closure +help: use parentheses to call this opaque type | LL | opaque()() | ++ diff --git a/src/test/ui/suggestions/call-on-missing.rs b/src/test/ui/suggestions/call-on-missing.rs index 611b9d40f0f06..25ced84dd3783 100644 --- a/src/test/ui/suggestions/call-on-missing.rs +++ b/src/test/ui/suggestions/call-on-missing.rs @@ -16,4 +16,24 @@ fn main() { foo.i; //~^ ERROR no field `i` //~| HELP use parentheses to call this function + + let callable = Box::new(|| Foo { i: 1 }) as Box Foo>; + + callable.bar(); + //~^ ERROR no method named `bar` + //~| HELP use parentheses to call this trait object + + callable.i; + //~^ ERROR no field `i` + //~| HELP use parentheses to call this trait object +} + +fn type_param Foo>(t: T) { + t.bar(); + //~^ ERROR no method named `bar` + //~| HELP use parentheses to call this type parameter + + t.i; + //~^ ERROR no field `i` + //~| HELP use parentheses to call this type parameter } diff --git a/src/test/ui/suggestions/call-on-missing.stderr b/src/test/ui/suggestions/call-on-missing.stderr index d8070321e1462..ca9abc7e90689 100644 --- a/src/test/ui/suggestions/call-on-missing.stderr +++ b/src/test/ui/suggestions/call-on-missing.stderr @@ -20,7 +20,56 @@ help: use parentheses to call this function LL | foo().i; | ++ -error: aborting due to 2 previous errors +error[E0599]: no method named `bar` found for struct `Box Foo>` in the current scope + --> $DIR/call-on-missing.rs:22:14 + | +LL | callable.bar(); + | ^^^ method not found in `Box Foo>` + | +help: use parentheses to call this trait object + | +LL | callable().bar(); + | ++ + +error[E0609]: no field `i` on type `Box Foo>` + --> $DIR/call-on-missing.rs:26:14 + | +LL | callable.i; + | ^ unknown field + | +help: use parentheses to call this trait object + | +LL | callable().i; + | ++ + +error[E0599]: no method named `bar` found for type parameter `T` in the current scope + --> $DIR/call-on-missing.rs:32:7 + | +LL | fn type_param Foo>(t: T) { + | - method `bar` not found for this type parameter +LL | t.bar(); + | ^^^ method not found in `T` + | +help: use parentheses to call this type parameter + | +LL | t().bar(); + | ++ + +error[E0609]: no field `i` on type `T` + --> $DIR/call-on-missing.rs:36:7 + | +LL | fn type_param Foo>(t: T) { + | - type parameter 'T' declared here +... +LL | t.i; + | ^ + | +help: use parentheses to call this type parameter + | +LL | t().i; + | ++ + +error: aborting due to 6 previous errors Some errors have detailed explanations: E0599, E0609. For more information about an error, try `rustc --explain E0599`. diff --git a/src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr b/src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr index e75ce0da82e51..ba710bfa746ae 100644 --- a/src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr +++ b/src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr @@ -103,7 +103,7 @@ LL | let _: usize = T::baz; | = note: expected type `usize` found fn item `fn(usize, usize) -> usize {<_ as T>::baz}` -help: use parentheses to call this function +help: use parentheses to call this associated function | LL | let _: usize = T::baz(_, _); | ++++++ @@ -121,7 +121,7 @@ LL | let _: usize = T::bat; | = note: expected type `usize` found fn item `fn(usize) -> usize {<_ as T>::bat}` -help: use parentheses to call this function +help: use parentheses to call this associated function | LL | let _: usize = T::bat(_); | +++ @@ -157,7 +157,7 @@ LL | let _: usize = X::baz; | = note: expected type `usize` found fn item `fn(usize, usize) -> usize {::baz}` -help: use parentheses to call this function +help: use parentheses to call this associated function | LL | let _: usize = X::baz(_, _); | ++++++ @@ -175,7 +175,7 @@ LL | let _: usize = X::bat; | = note: expected type `usize` found fn item `fn(usize) -> usize {::bat}` -help: use parentheses to call this function +help: use parentheses to call this associated function | LL | let _: usize = X::bat(_); | +++ @@ -193,7 +193,7 @@ LL | let _: usize = X::bax; | = note: expected type `usize` found fn item `fn(usize) -> usize {::bax}` -help: use parentheses to call this function +help: use parentheses to call this associated function | LL | let _: usize = X::bax(_); | +++ @@ -211,7 +211,7 @@ LL | let _: usize = X::bach; | = note: expected type `usize` found fn item `fn(usize) -> usize {::bach}` -help: use parentheses to call this function +help: use parentheses to call this associated function | LL | let _: usize = X::bach(_); | +++ @@ -229,7 +229,7 @@ LL | let _: usize = X::ban; | = note: expected type `usize` found fn item `for<'r> fn(&'r X) -> usize {::ban}` -help: use parentheses to call this function +help: use parentheses to call this associated function | LL | let _: usize = X::ban(_); | +++ @@ -247,7 +247,7 @@ LL | let _: usize = X::bal; | = note: expected type `usize` found fn item `for<'r> fn(&'r X) -> usize {::bal}` -help: use parentheses to call this function +help: use parentheses to call this associated function | LL | let _: usize = X::bal(_); | +++ From 18b640aee5b5435491a9db82c08c8fde8b7b62c9 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 28 Aug 2022 01:08:24 +0000 Subject: [PATCH 09/24] Suggest calling when operator types mismatch --- compiler/rustc_errors/src/lib.rs | 19 +- compiler/rustc_lint_defs/src/lib.rs | 3 +- .../src/check/fn_ctxt/suggestions.rs | 173 +++++++++++++----- compiler/rustc_typeck/src/check/op.rs | 128 ++++--------- src/test/ui/binop/issue-77910-2.stderr | 5 + src/test/ui/fn/fn-compare-mismatch.stderr | 10 +- src/test/ui/issues/issue-59488.stderr | 18 +- ...70724-add_type_neq_err_label-unwrap.stderr | 7 +- 8 files changed, 199 insertions(+), 164 deletions(-) diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 9b9c334d4dfcc..b21e9c2c96cbc 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -1248,9 +1248,13 @@ impl HandlerInner { } fn treat_err_as_bug(&self) -> bool { - self.flags - .treat_err_as_bug - .map_or(false, |c| self.err_count() + self.lint_err_count >= c.get()) + self.flags.treat_err_as_bug.map_or(false, |c| { + self.err_count() + + self.lint_err_count + + self.delayed_span_bugs.len() + + self.delayed_good_path_bugs.len() + >= c.get() + }) } fn print_error_count(&mut self, registry: &Registry) { @@ -1406,7 +1410,14 @@ impl HandlerInner { // This is technically `self.treat_err_as_bug()` but `delay_span_bug` is called before // incrementing `err_count` by one, so we need to +1 the comparing. // FIXME: Would be nice to increment err_count in a more coherent way. - if self.flags.treat_err_as_bug.map_or(false, |c| self.err_count() + 1 >= c.get()) { + if self.flags.treat_err_as_bug.map_or(false, |c| { + self.err_count() + + self.lint_err_count + + self.delayed_span_bugs.len() + + self.delayed_good_path_bugs.len() + + 1 + >= c.get() + }) { // FIXME: don't abort here if report_delayed_bugs is off self.span_bug(sp, msg); } diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 9e7cbba9511b2..9c6530c8a0843 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -41,7 +41,8 @@ macro_rules! pluralize { /// All suggestions are marked with an `Applicability`. Tools use the applicability of a suggestion /// to determine whether it should be automatically applied or if the user should be consulted /// before applying the suggestion. -#[derive(Copy, Clone, Debug, PartialEq, Hash, Encodable, Decodable, Serialize, Deserialize)] +#[derive(Copy, Clone, Debug, Hash, Encodable, Decodable, Serialize, Deserialize)] +#[derive(PartialEq, Eq, PartialOrd, Ord)] pub enum Applicability { /// The suggestion is definitely what the user intended, or maintains the exact meaning of the code. /// This suggestion should be automatically applied. diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs index 4eb0f045d7791..96901b5330312 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs @@ -76,10 +76,68 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { found: Ty<'tcx>, can_satisfy: impl FnOnce(Ty<'tcx>) -> bool, ) -> bool { - enum DefIdOrName { - DefId(DefId), - Name(&'static str), + let Some((def_id_or_name, output, num_inputs)) = self.extract_callable_info(expr, found) + else { return false; }; + if can_satisfy(output) { + let (sugg_call, mut applicability) = match num_inputs { + 0 => ("".to_string(), Applicability::MachineApplicable), + 1..=4 => ( + (0..num_inputs).map(|_| "_").collect::>().join(", "), + Applicability::MachineApplicable, + ), + _ => ("...".to_string(), Applicability::HasPlaceholders), + }; + + let msg = match def_id_or_name { + DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) { + DefKind::Ctor(CtorOf::Struct, _) => "instantiate this tuple struct".to_string(), + DefKind::Ctor(CtorOf::Variant, _) => { + "instantiate this tuple variant".to_string() + } + kind => format!("call this {}", kind.descr(def_id)), + }, + DefIdOrName::Name(name) => format!("call this {name}"), + }; + + let sugg = match expr.kind { + hir::ExprKind::Call(..) + | hir::ExprKind::Path(..) + | hir::ExprKind::Index(..) + | hir::ExprKind::Lit(..) => { + vec![(expr.span.shrink_to_hi(), format!("({sugg_call})"))] + } + hir::ExprKind::Closure { .. } => { + // Might be `{ expr } || { bool }` + applicability = Applicability::MaybeIncorrect; + vec![ + (expr.span.shrink_to_lo(), "(".to_string()), + (expr.span.shrink_to_hi(), format!(")({sugg_call})")), + ] + } + _ => { + vec![ + (expr.span.shrink_to_lo(), "(".to_string()), + (expr.span.shrink_to_hi(), format!(")({sugg_call})")), + ] + } + }; + + err.multipart_suggestion_verbose( + format!("use parentheses to {msg}"), + sugg, + applicability, + ); + + return true; } + false + } + + fn extract_callable_info( + &self, + expr: &Expr<'_>, + found: Ty<'tcx>, + ) -> Option<(DefIdOrName, Ty<'tcx>, usize)> { // Autoderef is useful here because sometimes we box callables, etc. let Some((def_id_or_name, output, inputs)) = self.autoderef(expr.span, found).silence_errors().find_map(|(found, _)| { match *found.kind() { @@ -148,67 +206,83 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } _ => None, } - }) else { return false; }; + }) else { return None; }; let output = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, output); + // We don't want to register any extra obligations, which should be // implied by wf, but also because that would possibly result in // erroneous errors later on. let infer::InferOk { value: output, obligations: _ } = self.normalize_associated_types_in_as_infer_ok(expr.span, output); - if !output.is_ty_var() && can_satisfy(output) { - let (sugg_call, mut applicability) = match inputs { - 0 => ("".to_string(), Applicability::MachineApplicable), - 1..=4 => ( - (0..inputs).map(|_| "_").collect::>().join(", "), - Applicability::MachineApplicable, - ), - _ => ("...".to_string(), Applicability::HasPlaceholders), - }; - let msg = match def_id_or_name { - DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) { - DefKind::Ctor(CtorOf::Struct, _) => "instantiate this tuple struct".to_string(), - DefKind::Ctor(CtorOf::Variant, _) => { - "instantiate this tuple variant".to_string() - } - kind => format!("call this {}", kind.descr(def_id)), - }, - DefIdOrName::Name(name) => format!("call this {name}"), - }; + if output.is_ty_var() { None } else { Some((def_id_or_name, output, inputs)) } + } - let sugg = match expr.kind { - hir::ExprKind::Call(..) - | hir::ExprKind::Path(..) - | hir::ExprKind::Index(..) - | hir::ExprKind::Lit(..) => { - vec![(expr.span.shrink_to_hi(), format!("({sugg_call})"))] - } - hir::ExprKind::Closure { .. } => { - // Might be `{ expr } || { bool }` - applicability = Applicability::MaybeIncorrect; - vec![ - (expr.span.shrink_to_lo(), "(".to_string()), - (expr.span.shrink_to_hi(), format!(")({sugg_call})")), - ] - } - _ => { - vec![ - (expr.span.shrink_to_lo(), "(".to_string()), - (expr.span.shrink_to_hi(), format!(")({sugg_call})")), - ] + pub fn suggest_two_fn_call( + &self, + err: &mut Diagnostic, + lhs_expr: &'tcx hir::Expr<'tcx>, + lhs_ty: Ty<'tcx>, + rhs_expr: &'tcx hir::Expr<'tcx>, + rhs_ty: Ty<'tcx>, + can_satisfy: impl FnOnce(Ty<'tcx>, Ty<'tcx>) -> bool, + ) -> bool { + let Some((_, lhs_output_ty, num_lhs_inputs)) = self.extract_callable_info(lhs_expr, lhs_ty) + else { return false; }; + let Some((_, rhs_output_ty, num_rhs_inputs)) = self.extract_callable_info(rhs_expr, rhs_ty) + else { return false; }; + + if can_satisfy(lhs_output_ty, rhs_output_ty) { + let mut sugg = vec![]; + let mut applicability = Applicability::MachineApplicable; + + for (expr, num_inputs) in [(lhs_expr, num_lhs_inputs), (rhs_expr, num_rhs_inputs)] { + let (sugg_call, this_applicability) = match num_inputs { + 0 => ("".to_string(), Applicability::MachineApplicable), + 1..=4 => ( + (0..num_inputs).map(|_| "_").collect::>().join(", "), + Applicability::MachineApplicable, + ), + _ => ("...".to_string(), Applicability::HasPlaceholders), + }; + + applicability = applicability.max(this_applicability); + + match expr.kind { + hir::ExprKind::Call(..) + | hir::ExprKind::Path(..) + | hir::ExprKind::Index(..) + | hir::ExprKind::Lit(..) => { + sugg.extend([(expr.span.shrink_to_hi(), format!("({sugg_call})"))]); + } + hir::ExprKind::Closure { .. } => { + // Might be `{ expr } || { bool }` + applicability = Applicability::MaybeIncorrect; + sugg.extend([ + (expr.span.shrink_to_lo(), "(".to_string()), + (expr.span.shrink_to_hi(), format!(")({sugg_call})")), + ]); + } + _ => { + sugg.extend([ + (expr.span.shrink_to_lo(), "(".to_string()), + (expr.span.shrink_to_hi(), format!(")({sugg_call})")), + ]); + } } - }; + } err.multipart_suggestion_verbose( - format!("use parentheses to {msg}"), + format!("use parentheses to call these"), sugg, applicability, ); - return true; + true + } else { + false } - false } pub fn suggest_deref_ref_or_into( @@ -959,3 +1033,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } + +enum DefIdOrName { + DefId(DefId), + Name(&'static str), +} diff --git a/compiler/rustc_typeck/src/check/op.rs b/compiler/rustc_typeck/src/check/op.rs index eb0c51bb2f979..952086e898fc7 100644 --- a/compiler/rustc_typeck/src/check/op.rs +++ b/compiler/rustc_typeck/src/check/op.rs @@ -410,26 +410,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let mut err = struct_span_err!(self.tcx.sess, op.span, E0369, "{message}"); if !lhs_expr.span.eq(&rhs_expr.span) { - self.add_type_neq_err_label( - &mut err, - lhs_expr.span, - lhs_ty, - rhs_ty, - rhs_expr, - op, - is_assign, - expected, - ); - self.add_type_neq_err_label( - &mut err, - rhs_expr.span, - rhs_ty, - lhs_ty, - lhs_expr, - op, - is_assign, - expected, - ); + err.span_label(lhs_expr.span, lhs_ty.to_string()); + err.span_label(rhs_expr.span, rhs_ty.to_string()); } self.note_unmet_impls_on_type(&mut err, errors); (err, missing_trait, use_output) @@ -468,17 +450,50 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; + let is_compatible = |lhs_ty, rhs_ty| { + self.lookup_op_method( + lhs_ty, + Some(rhs_ty), + Some(rhs_expr), + Op::Binary(op, is_assign), + expected, + ) + .is_ok() + }; + // We should suggest `a + b` => `*a + b` if `a` is copy, and suggest // `a += b` => `*a += b` if a is a mut ref. - if is_assign == IsAssign::Yes - && let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) { - suggest_deref_binop(lhs_deref_ty); + if !op.span.can_be_used_for_suggestions() { + // Suppress suggestions when lhs and rhs are not in the same span as the error + } else if is_assign == IsAssign::Yes + && let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) + { + suggest_deref_binop(lhs_deref_ty); } else if is_assign == IsAssign::No - && let Ref(_, lhs_deref_ty, _) = lhs_ty.kind() { - if self.type_is_copy_modulo_regions(self.param_env, *lhs_deref_ty, lhs_expr.span) { + && let Ref(_, lhs_deref_ty, _) = lhs_ty.kind() + { + if self.type_is_copy_modulo_regions( + self.param_env, + *lhs_deref_ty, + lhs_expr.span, + ) { suggest_deref_binop(*lhs_deref_ty); } + } else if self.suggest_fn_call(&mut err, lhs_expr, lhs_ty, |lhs_ty| { + is_compatible(lhs_ty, rhs_ty) + }) || self.suggest_fn_call(&mut err, rhs_expr, rhs_ty, |rhs_ty| { + is_compatible(lhs_ty, rhs_ty) + }) || self.suggest_two_fn_call( + &mut err, + rhs_expr, + rhs_ty, + lhs_expr, + lhs_ty, + |lhs_ty, rhs_ty| is_compatible(lhs_ty, rhs_ty), + ) { + // Cool } + if let Some(missing_trait) = missing_trait { let mut visitor = TypeParamVisitor(vec![]); visitor.visit_ty(lhs_ty); @@ -548,69 +563,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (lhs_ty, rhs_ty, return_ty) } - /// If one of the types is an uncalled function and calling it would yield the other type, - /// suggest calling the function. Returns `true` if suggestion would apply (even if not given). - fn add_type_neq_err_label( - &self, - err: &mut Diagnostic, - span: Span, - ty: Ty<'tcx>, - other_ty: Ty<'tcx>, - other_expr: &'tcx hir::Expr<'tcx>, - op: hir::BinOp, - is_assign: IsAssign, - expected: Expectation<'tcx>, - ) -> bool /* did we suggest to call a function because of missing parentheses? */ { - err.span_label(span, ty.to_string()); - if let FnDef(def_id, _) = *ty.kind() { - if !self.tcx.has_typeck_results(def_id) { - return false; - } - // FIXME: Instead of exiting early when encountering bound vars in - // the function signature, consider keeping the binder here and - // propagating it downwards. - let Some(fn_sig) = self.tcx.fn_sig(def_id).no_bound_vars() else { - return false; - }; - - let other_ty = if let FnDef(def_id, _) = *other_ty.kind() { - if !self.tcx.has_typeck_results(def_id) { - return false; - } - // We're emitting a suggestion, so we can just ignore regions - self.tcx.fn_sig(def_id).skip_binder().output() - } else { - other_ty - }; - - if self - .lookup_op_method( - fn_sig.output(), - Some(other_ty), - Some(other_expr), - Op::Binary(op, is_assign), - expected, - ) - .is_ok() - { - let (variable_snippet, applicability) = if !fn_sig.inputs().is_empty() { - ("( /* arguments */ )", Applicability::HasPlaceholders) - } else { - ("()", Applicability::MaybeIncorrect) - }; - - err.span_suggestion_verbose( - span.shrink_to_hi(), - "you might have forgotten to call this function", - variable_snippet, - applicability, - ); - return true; - } - } - false - } - /// Provide actionable suggestions when trying to add two strings with incorrect types, /// like `&str + &str`, `String + String` and `&str + &String`. /// diff --git a/src/test/ui/binop/issue-77910-2.stderr b/src/test/ui/binop/issue-77910-2.stderr index 5477a5762a8fd..74860a93f3799 100644 --- a/src/test/ui/binop/issue-77910-2.stderr +++ b/src/test/ui/binop/issue-77910-2.stderr @@ -5,6 +5,11 @@ LL | if foo == y {} | --- ^^ - _ | | | for<'r> fn(&'r i32) -> &'r i32 {foo} + | +help: use parentheses to call this function + | +LL | if foo(_) == y {} + | +++ error: aborting due to previous error diff --git a/src/test/ui/fn/fn-compare-mismatch.stderr b/src/test/ui/fn/fn-compare-mismatch.stderr index 096440225b999..df838cb118105 100644 --- a/src/test/ui/fn/fn-compare-mismatch.stderr +++ b/src/test/ui/fn/fn-compare-mismatch.stderr @@ -6,14 +6,10 @@ LL | let x = f == g; | | | fn() {f} | -help: you might have forgotten to call this function +help: use parentheses to call these | -LL | let x = f() == g; - | ++ -help: you might have forgotten to call this function - | -LL | let x = f == g(); - | ++ +LL | let x = f() == g(); + | ++ ++ error[E0308]: mismatched types --> $DIR/fn-compare-mismatch.rs:4:18 diff --git a/src/test/ui/issues/issue-59488.stderr b/src/test/ui/issues/issue-59488.stderr index bb6843a19586e..df681eb2489c0 100644 --- a/src/test/ui/issues/issue-59488.stderr +++ b/src/test/ui/issues/issue-59488.stderr @@ -6,7 +6,7 @@ LL | foo > 12; | | | fn() -> i32 {foo} | -help: you might have forgotten to call this function +help: use parentheses to call this function | LL | foo() > 12; | ++ @@ -28,10 +28,10 @@ LL | bar > 13; | | | fn(i64) -> i64 {bar} | -help: you might have forgotten to call this function +help: use parentheses to call this function | -LL | bar( /* arguments */ ) > 13; - | +++++++++++++++++++ +LL | bar(_) > 13; + | +++ error[E0308]: mismatched types --> $DIR/issue-59488.rs:18:11 @@ -50,14 +50,10 @@ LL | foo > foo; | | | fn() -> i32 {foo} | -help: you might have forgotten to call this function +help: use parentheses to call these | -LL | foo() > foo; - | ++ -help: you might have forgotten to call this function - | -LL | foo > foo(); - | ++ +LL | foo() > foo(); + | ++ ++ error[E0369]: binary operation `>` cannot be applied to type `fn() -> i32 {foo}` --> $DIR/issue-59488.rs:25:9 diff --git a/src/test/ui/issues/issue-70724-add_type_neq_err_label-unwrap.stderr b/src/test/ui/issues/issue-70724-add_type_neq_err_label-unwrap.stderr index c6e6ea1e096af..9239385e64369 100644 --- a/src/test/ui/issues/issue-70724-add_type_neq_err_label-unwrap.stderr +++ b/src/test/ui/issues/issue-70724-add_type_neq_err_label-unwrap.stderr @@ -8,11 +8,6 @@ LL | assert_eq!(a, 0); | {integer} | = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) -help: you might have forgotten to call this function - --> $SRC_DIR/core/src/macros/mod.rs:LL:COL - | -LL | if !(*left_val() == *right_val) { - | ++ error[E0308]: mismatched types --> $DIR/issue-70724-add_type_neq_err_label-unwrap.rs:6:5 @@ -21,7 +16,7 @@ LL | assert_eq!(a, 0); | ^^^^^^^^^^^^^^^^ expected fn item, found integer | = note: expected fn item `fn() -> i32 {a}` - found type `i32` + found type `{integer}` = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: `fn() -> i32 {a}` doesn't implement `Debug` From 1256530643fdd4762de8de5a47041fc2cf700828 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 28 Aug 2022 01:22:51 +0000 Subject: [PATCH 10/24] More descriptive argument placeholders --- .../rustc_middle/src/ty/structural_impls.rs | 6 ++ .../src/check/fn_ctxt/suggestions.rs | 69 ++++++++++++++----- src/test/ui/binop/issue-77910-2.stderr | 4 +- src/test/ui/fn/fn-trait-formatting.stderr | 8 +-- src/test/ui/issues/issue-35241.stderr | 4 +- src/test/ui/issues/issue-59488.stderr | 4 +- src/test/ui/resolve/privacy-enum-ctor.stderr | 12 ++-- .../fn-or-tuple-struct-without-args.stderr | 44 ++++++------ .../ui/typeck/issue-87181/tuple-field.stderr | 4 +- 9 files changed, 96 insertions(+), 59 deletions(-) diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 7660a2f3af60a..57555433f55b7 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -844,6 +844,12 @@ impl<'tcx, T: TypeVisitable<'tcx>> TypeVisitable<'tcx> for Vec { } } +impl<'tcx, T: TypeVisitable<'tcx>> TypeVisitable<'tcx> for &[T] { + fn visit_with>(&self, visitor: &mut V) -> ControlFlow { + self.iter().try_for_each(|t| t.visit_with(visitor)) + } +} + impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Box<[T]> { fn try_fold_with>(self, folder: &mut F) -> Result { self.try_map_id(|t| t.try_fold_with(folder)) diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs index 96901b5330312..d48bdbd7b6d64 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs @@ -76,16 +76,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { found: Ty<'tcx>, can_satisfy: impl FnOnce(Ty<'tcx>) -> bool, ) -> bool { - let Some((def_id_or_name, output, num_inputs)) = self.extract_callable_info(expr, found) + let Some((def_id_or_name, output, inputs)) = self.extract_callable_info(expr, found) else { return false; }; if can_satisfy(output) { - let (sugg_call, mut applicability) = match num_inputs { + let (sugg_call, mut applicability) = match inputs.len() { 0 => ("".to_string(), Applicability::MachineApplicable), 1..=4 => ( - (0..num_inputs).map(|_| "_").collect::>().join(", "), - Applicability::MachineApplicable, + inputs + .iter() + .map(|ty| { + if ty.is_suggestable(self.tcx, false) { + format!("/* {ty} */") + } else { + "".to_string() + } + }) + .collect::>() + .join(", "), + Applicability::HasPlaceholders, ), - _ => ("...".to_string(), Applicability::HasPlaceholders), + _ => ("/* ... */".to_string(), Applicability::HasPlaceholders), }; let msg = match def_id_or_name { @@ -137,19 +147,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, expr: &Expr<'_>, found: Ty<'tcx>, - ) -> Option<(DefIdOrName, Ty<'tcx>, usize)> { + ) -> Option<(DefIdOrName, Ty<'tcx>, Vec>)> { // Autoderef is useful here because sometimes we box callables, etc. let Some((def_id_or_name, output, inputs)) = self.autoderef(expr.span, found).silence_errors().find_map(|(found, _)| { match *found.kind() { ty::FnPtr(fn_sig) => - Some((DefIdOrName::Name("function pointer"), fn_sig.output(), fn_sig.inputs().skip_binder().len())), + Some((DefIdOrName::Name("function pointer"), fn_sig.output(), fn_sig.inputs())), ty::FnDef(def_id, _) => { let fn_sig = found.fn_sig(self.tcx); - Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs().skip_binder().len())) + Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs())) } ty::Closure(def_id, substs) => { let fn_sig = substs.as_closure().sig(); - Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs().skip_binder().len() - 1)) + Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs().map_bound(|inputs| &inputs[1..]))) } ty::Opaque(def_id, substs) => { self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| { @@ -161,7 +171,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(( DefIdOrName::DefId(def_id), pred.kind().rebind(proj.term.ty().unwrap()), - args.len(), + pred.kind().rebind(args.as_slice()), )) } else { None @@ -178,7 +188,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(( DefIdOrName::Name("trait object"), pred.rebind(proj.term.ty().unwrap()), - args.len(), + pred.rebind(args.as_slice()), )) } else { None @@ -197,7 +207,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(( DefIdOrName::DefId(def_id), pred.kind().rebind(proj.term.ty().unwrap()), - args.len(), + pred.kind().rebind(args.as_slice()), )) } else { None @@ -209,6 +219,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }) else { return None; }; let output = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, output); + let inputs = inputs + .skip_binder() + .iter() + .map(|ty| { + self.replace_bound_vars_with_fresh_vars( + expr.span, + infer::FnCall, + inputs.rebind(*ty), + ) + }) + .collect(); // We don't want to register any extra obligations, which should be // implied by wf, but also because that would possibly result in @@ -228,23 +249,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rhs_ty: Ty<'tcx>, can_satisfy: impl FnOnce(Ty<'tcx>, Ty<'tcx>) -> bool, ) -> bool { - let Some((_, lhs_output_ty, num_lhs_inputs)) = self.extract_callable_info(lhs_expr, lhs_ty) + let Some((_, lhs_output_ty, lhs_inputs)) = self.extract_callable_info(lhs_expr, lhs_ty) else { return false; }; - let Some((_, rhs_output_ty, num_rhs_inputs)) = self.extract_callable_info(rhs_expr, rhs_ty) + let Some((_, rhs_output_ty, rhs_inputs)) = self.extract_callable_info(rhs_expr, rhs_ty) else { return false; }; if can_satisfy(lhs_output_ty, rhs_output_ty) { let mut sugg = vec![]; let mut applicability = Applicability::MachineApplicable; - for (expr, num_inputs) in [(lhs_expr, num_lhs_inputs), (rhs_expr, num_rhs_inputs)] { - let (sugg_call, this_applicability) = match num_inputs { + for (expr, inputs) in [(lhs_expr, lhs_inputs), (rhs_expr, rhs_inputs)] { + let (sugg_call, this_applicability) = match inputs.len() { 0 => ("".to_string(), Applicability::MachineApplicable), 1..=4 => ( - (0..num_inputs).map(|_| "_").collect::>().join(", "), - Applicability::MachineApplicable, + inputs + .iter() + .map(|ty| { + if ty.is_suggestable(self.tcx, false) { + format!("/* {ty} */") + } else { + "/* value */".to_string() + } + }) + .collect::>() + .join(", "), + Applicability::HasPlaceholders, ), - _ => ("...".to_string(), Applicability::HasPlaceholders), + _ => ("/* ... */".to_string(), Applicability::HasPlaceholders), }; applicability = applicability.max(this_applicability); diff --git a/src/test/ui/binop/issue-77910-2.stderr b/src/test/ui/binop/issue-77910-2.stderr index 74860a93f3799..a334bd8562593 100644 --- a/src/test/ui/binop/issue-77910-2.stderr +++ b/src/test/ui/binop/issue-77910-2.stderr @@ -8,8 +8,8 @@ LL | if foo == y {} | help: use parentheses to call this function | -LL | if foo(_) == y {} - | +++ +LL | if foo(/* &i32 */) == y {} + | ++++++++++++ error: aborting due to previous error diff --git a/src/test/ui/fn/fn-trait-formatting.stderr b/src/test/ui/fn/fn-trait-formatting.stderr index 1d5e0a859a6da..2a674d3c1d23d 100644 --- a/src/test/ui/fn/fn-trait-formatting.stderr +++ b/src/test/ui/fn/fn-trait-formatting.stderr @@ -10,8 +10,8 @@ LL | let _: () = Box::new(|_: isize| {}) as Box; found struct `Box` help: use parentheses to call this trait object | -LL | let _: () = (Box::new(|_: isize| {}) as Box)(_); - | + ++++ +LL | let _: () = (Box::new(|_: isize| {}) as Box)(/* isize */); + | + ++++++++++++++ error[E0308]: mismatched types --> $DIR/fn-trait-formatting.rs:10:17 @@ -25,8 +25,8 @@ LL | let _: () = Box::new(|_: isize, isize| {}) as Box found struct `Box` help: use parentheses to call this trait object | -LL | let _: () = (Box::new(|_: isize, isize| {}) as Box)(_, _); - | + +++++++ +LL | let _: () = (Box::new(|_: isize, isize| {}) as Box)(/* isize */, /* isize */); + | + +++++++++++++++++++++++++++ error[E0308]: mismatched types --> $DIR/fn-trait-formatting.rs:14:17 diff --git a/src/test/ui/issues/issue-35241.stderr b/src/test/ui/issues/issue-35241.stderr index a66289a1cf8eb..9ee7654a0885d 100644 --- a/src/test/ui/issues/issue-35241.stderr +++ b/src/test/ui/issues/issue-35241.stderr @@ -13,8 +13,8 @@ LL | fn test() -> Foo { Foo } found fn item `fn(u32) -> Foo {Foo}` help: use parentheses to instantiate this tuple struct | -LL | fn test() -> Foo { Foo(_) } - | +++ +LL | fn test() -> Foo { Foo(/* u32 */) } + | +++++++++++ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-59488.stderr b/src/test/ui/issues/issue-59488.stderr index df681eb2489c0..e5368ddf1e576 100644 --- a/src/test/ui/issues/issue-59488.stderr +++ b/src/test/ui/issues/issue-59488.stderr @@ -30,8 +30,8 @@ LL | bar > 13; | help: use parentheses to call this function | -LL | bar(_) > 13; - | +++ +LL | bar(/* i64 */) > 13; + | +++++++++++ error[E0308]: mismatched types --> $DIR/issue-59488.rs:18:11 diff --git a/src/test/ui/resolve/privacy-enum-ctor.stderr b/src/test/ui/resolve/privacy-enum-ctor.stderr index f885ac2151d61..7cf32775a33ef 100644 --- a/src/test/ui/resolve/privacy-enum-ctor.stderr +++ b/src/test/ui/resolve/privacy-enum-ctor.stderr @@ -329,8 +329,8 @@ LL | let _: Z = Z::Fn; found fn item `fn(u8) -> Z {Z::Fn}` help: use parentheses to instantiate this tuple variant | -LL | let _: Z = Z::Fn(_); - | +++ +LL | let _: Z = Z::Fn(/* u8 */); + | ++++++++++ error[E0618]: expected function, found enum variant `Z::Unit` --> $DIR/privacy-enum-ctor.rs:31:17 @@ -364,8 +364,8 @@ LL | let _: E = m::E::Fn; found fn item `fn(u8) -> E {E::Fn}` help: use parentheses to instantiate this tuple variant | -LL | let _: E = m::E::Fn(_); - | +++ +LL | let _: E = m::E::Fn(/* u8 */); + | ++++++++++ error[E0618]: expected function, found enum variant `m::E::Unit` --> $DIR/privacy-enum-ctor.rs:47:16 @@ -399,8 +399,8 @@ LL | let _: E = E::Fn; found fn item `fn(u8) -> E {E::Fn}` help: use parentheses to instantiate this tuple variant | -LL | let _: E = E::Fn(_); - | +++ +LL | let _: E = E::Fn(/* u8 */); + | ++++++++++ error[E0618]: expected function, found enum variant `E::Unit` --> $DIR/privacy-enum-ctor.rs:55:16 diff --git a/src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr b/src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr index ba710bfa746ae..3c7b895e337e7 100644 --- a/src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr +++ b/src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr @@ -33,8 +33,8 @@ LL | let _: usize = foo; found fn item `fn(usize, usize) -> usize {foo}` help: use parentheses to call this function | -LL | let _: usize = foo(_, _); - | ++++++ +LL | let _: usize = foo(/* usize */, /* usize */); + | ++++++++++++++++++++++++++ error[E0308]: mismatched types --> $DIR/fn-or-tuple-struct-without-args.rs:30:16 @@ -51,8 +51,8 @@ LL | let _: S = S; found fn item `fn(usize, usize) -> S {S}` help: use parentheses to instantiate this tuple struct | -LL | let _: S = S(_, _); - | ++++++ +LL | let _: S = S(/* usize */, /* usize */); + | ++++++++++++++++++++++++++ error[E0308]: mismatched types --> $DIR/fn-or-tuple-struct-without-args.rs:31:20 @@ -105,8 +105,8 @@ LL | let _: usize = T::baz; found fn item `fn(usize, usize) -> usize {<_ as T>::baz}` help: use parentheses to call this associated function | -LL | let _: usize = T::baz(_, _); - | ++++++ +LL | let _: usize = T::baz(/* usize */, /* usize */); + | ++++++++++++++++++++++++++ error[E0308]: mismatched types --> $DIR/fn-or-tuple-struct-without-args.rs:34:20 @@ -123,8 +123,8 @@ LL | let _: usize = T::bat; found fn item `fn(usize) -> usize {<_ as T>::bat}` help: use parentheses to call this associated function | -LL | let _: usize = T::bat(_); - | +++ +LL | let _: usize = T::bat(/* usize */); + | +++++++++++++ error[E0308]: mismatched types --> $DIR/fn-or-tuple-struct-without-args.rs:35:16 @@ -141,8 +141,8 @@ LL | let _: E = E::A; found fn item `fn(usize) -> E {E::A}` help: use parentheses to instantiate this tuple variant | -LL | let _: E = E::A(_); - | +++ +LL | let _: E = E::A(/* usize */); + | +++++++++++++ error[E0308]: mismatched types --> $DIR/fn-or-tuple-struct-without-args.rs:37:20 @@ -159,8 +159,8 @@ LL | let _: usize = X::baz; found fn item `fn(usize, usize) -> usize {::baz}` help: use parentheses to call this associated function | -LL | let _: usize = X::baz(_, _); - | ++++++ +LL | let _: usize = X::baz(/* usize */, /* usize */); + | ++++++++++++++++++++++++++ error[E0308]: mismatched types --> $DIR/fn-or-tuple-struct-without-args.rs:38:20 @@ -177,8 +177,8 @@ LL | let _: usize = X::bat; found fn item `fn(usize) -> usize {::bat}` help: use parentheses to call this associated function | -LL | let _: usize = X::bat(_); - | +++ +LL | let _: usize = X::bat(/* usize */); + | +++++++++++++ error[E0308]: mismatched types --> $DIR/fn-or-tuple-struct-without-args.rs:39:20 @@ -195,8 +195,8 @@ LL | let _: usize = X::bax; found fn item `fn(usize) -> usize {::bax}` help: use parentheses to call this associated function | -LL | let _: usize = X::bax(_); - | +++ +LL | let _: usize = X::bax(/* usize */); + | +++++++++++++ error[E0308]: mismatched types --> $DIR/fn-or-tuple-struct-without-args.rs:40:20 @@ -213,8 +213,8 @@ LL | let _: usize = X::bach; found fn item `fn(usize) -> usize {::bach}` help: use parentheses to call this associated function | -LL | let _: usize = X::bach(_); - | +++ +LL | let _: usize = X::bach(/* usize */); + | +++++++++++++ error[E0308]: mismatched types --> $DIR/fn-or-tuple-struct-without-args.rs:41:20 @@ -231,8 +231,8 @@ LL | let _: usize = X::ban; found fn item `for<'r> fn(&'r X) -> usize {::ban}` help: use parentheses to call this associated function | -LL | let _: usize = X::ban(_); - | +++ +LL | let _: usize = X::ban(/* &X */); + | ++++++++++ error[E0308]: mismatched types --> $DIR/fn-or-tuple-struct-without-args.rs:42:20 @@ -249,8 +249,8 @@ LL | let _: usize = X::bal; found fn item `for<'r> fn(&'r X) -> usize {::bal}` help: use parentheses to call this associated function | -LL | let _: usize = X::bal(_); - | +++ +LL | let _: usize = X::bal(/* &X */); + | ++++++++++ error[E0615]: attempted to take value of method `ban` on type `X` --> $DIR/fn-or-tuple-struct-without-args.rs:43:22 diff --git a/src/test/ui/typeck/issue-87181/tuple-field.stderr b/src/test/ui/typeck/issue-87181/tuple-field.stderr index 0e43ace893396..c1ca26ee9af1d 100644 --- a/src/test/ui/typeck/issue-87181/tuple-field.stderr +++ b/src/test/ui/typeck/issue-87181/tuple-field.stderr @@ -6,8 +6,8 @@ LL | thing.bar.0; | help: use parentheses to instantiate this tuple struct | -LL | (thing.bar)(_, _).0; - | + +++++++ +LL | (thing.bar)(/* char */, /* u16 */).0; + | + ++++++++++++++++++++++++ error: aborting due to previous error From 7bb47a6f38d2c4673663fa1370817c7fb5e682c1 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Sat, 20 Aug 2022 08:03:33 +0100 Subject: [PATCH 11/24] Reinstate preloading of some dll imports --- library/std/src/sys/windows/c.rs | 3 - library/std/src/sys/windows/compat.rs | 82 +++++++++++++++++---------- 2 files changed, 52 insertions(+), 33 deletions(-) diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs index ef3f6a9ba1755..891d7e855f0b9 100644 --- a/library/std/src/sys/windows/c.rs +++ b/library/std/src/sys/windows/c.rs @@ -228,8 +228,6 @@ pub const IPV6_ADD_MEMBERSHIP: c_int = 12; pub const IPV6_DROP_MEMBERSHIP: c_int = 13; pub const MSG_PEEK: c_int = 0x2; -pub const LOAD_LIBRARY_SEARCH_SYSTEM32: u32 = 0x800; - #[repr(C)] #[derive(Copy, Clone)] pub struct linger { @@ -1032,7 +1030,6 @@ extern "system" { pub fn GetProcAddress(handle: HMODULE, name: LPCSTR) -> *mut c_void; pub fn GetModuleHandleA(lpModuleName: LPCSTR) -> HMODULE; pub fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HMODULE; - pub fn LoadLibraryExA(lplibfilename: *const i8, hfile: HANDLE, dwflags: u32) -> HINSTANCE; pub fn GetSystemTimeAsFileTime(lpSystemTimeAsFileTime: LPFILETIME); pub fn GetSystemInfo(lpSystemInfo: LPSYSTEM_INFO); diff --git a/library/std/src/sys/windows/compat.rs b/library/std/src/sys/windows/compat.rs index 9c8ddc3aa1d25..7dff81ecb8dde 100644 --- a/library/std/src/sys/windows/compat.rs +++ b/library/std/src/sys/windows/compat.rs @@ -21,9 +21,52 @@ use crate::ffi::{c_void, CStr}; use crate::ptr::NonNull; -use crate::sync::atomic::{AtomicBool, Ordering}; +use crate::sync::atomic::Ordering; use crate::sys::c; +// This uses a static initializer to preload some imported functions. +// The CRT (C runtime) executes static initializers before `main` +// is called (for binaries) and before `DllMain` is called (for DLLs). +// +// It works by contributing a global symbol to the `.CRT$XCT` section. +// The linker builds a table of all static initializer functions. +// The CRT startup code then iterates that table, calling each +// initializer function. +// +// NOTE: User code should instead use .CRT$XCU to reliably run after std's initializer. +// If you're reading this and would like a guarantee here, please +// file an issue for discussion; currently we don't guarantee any functionality +// before main. +// See https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-initialization?view=msvc-170 +#[used] +#[link_section = ".CRT$XCT"] +static INIT_TABLE_ENTRY: unsafe extern "C" fn() = init; + +/// Preload some imported functions. +/// +/// Note that any functions included here will be unconditionally loaded in +/// the final binary, regardless of whether or not they're actually used. +/// +/// Therefore, this should be limited to `compat_fn_optional` functions which +/// must be preloaded or any functions where lazier loading demonstrates a +/// negative performance impact in practical situations. +/// +/// Currently we only preload `WaitOnAddress` and `WakeByAddressSingle`. +unsafe extern "C" fn init() { + // In an exe this code is executed before main() so is single threaded. + // In a DLL the system's loader lock will be held thereby synchronizing + // access. So the same best practices apply here as they do to running in DllMain: + // https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-best-practices + // + // DO NOT do anything interesting or complicated in this function! DO NOT call + // any Rust functions or CRT functions if those functions touch any global state, + // because this function runs during global initialization. For example, DO NOT + // do any dynamic allocation, don't call LoadLibrary, etc. + + // Attempt to preload the synch functions. + load_synch_functions(); +} + /// Helper macro for creating CStrs from literals and symbol names. macro_rules! ansi_str { (sym $ident:ident) => {{ @@ -75,20 +118,6 @@ impl Module { NonNull::new(module).map(Self) } - /// Load the library (if not already loaded) - /// - /// # Safety - /// - /// The module must not be unloaded. - pub unsafe fn load_system_library(name: &CStr) -> Option { - let module = c::LoadLibraryExA( - name.as_ptr(), - crate::ptr::null_mut(), - c::LOAD_LIBRARY_SEARCH_SYSTEM32, - ); - NonNull::new(module).map(Self) - } - // Try to get the address of a function. pub fn proc_address(self, name: &CStr) -> Option> { // SAFETY: @@ -182,14 +211,10 @@ macro_rules! compat_fn_optional { #[inline(always)] pub fn option() -> Option { - let f = PTR.load(Ordering::Acquire); - if !f.is_null() { Some(unsafe { mem::transmute(f) }) } else { try_load() } - } - - #[cold] - fn try_load() -> Option { - $load_functions; - NonNull::new(PTR.load(Ordering::Acquire)).map(|f| unsafe { mem::transmute(f) }) + // Miri does not understand the way we do preloading + // therefore load the function here instead. + #[cfg(miri)] $load_functions; + NonNull::new(PTR.load(Ordering::Relaxed)).map(|f| unsafe { mem::transmute(f) }) } } )+ @@ -205,17 +230,14 @@ pub(super) fn load_synch_functions() { // Try loading the library and all the required functions. // If any step fails, then they all fail. - let library = unsafe { Module::load_system_library(MODULE_NAME) }?; + let library = unsafe { Module::new(MODULE_NAME) }?; let wait_on_address = library.proc_address(WAIT_ON_ADDRESS)?; let wake_by_address_single = library.proc_address(WAKE_BY_ADDRESS_SINGLE)?; - c::WaitOnAddress::PTR.store(wait_on_address.as_ptr(), Ordering::Release); - c::WakeByAddressSingle::PTR.store(wake_by_address_single.as_ptr(), Ordering::Release); + c::WaitOnAddress::PTR.store(wait_on_address.as_ptr(), Ordering::Relaxed); + c::WakeByAddressSingle::PTR.store(wake_by_address_single.as_ptr(), Ordering::Relaxed); Some(()) } - // Try to load the module but skip loading if a previous attempt failed. - static LOAD_MODULE: AtomicBool = AtomicBool::new(true); - let module_loaded = LOAD_MODULE.load(Ordering::Acquire) && try_load().is_some(); - LOAD_MODULE.store(module_loaded, Ordering::Release) + try_load(); } From dacb6ee7b00fcd46f1a0e17dae2c81c7580843e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Marie?= Date: Sun, 28 Aug 2022 05:16:02 +0000 Subject: [PATCH 12/24] add powerpc64-unknown-openbsd support --- compiler/rustc_target/src/spec/mod.rs | 1 + .../src/spec/powerpc64_unknown_openbsd.rs | 17 +++++++++++++++++ src/doc/rustc/src/platform-support.md | 1 + src/doc/rustc/src/platform-support/openbsd.md | 1 + 4 files changed, 20 insertions(+) create mode 100644 compiler/rustc_target/src/spec/powerpc64_unknown_openbsd.rs diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 46fea26283636..d579508768313 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -894,6 +894,7 @@ supported_targets! { ("aarch64-unknown-openbsd", aarch64_unknown_openbsd), ("i686-unknown-openbsd", i686_unknown_openbsd), ("powerpc-unknown-openbsd", powerpc_unknown_openbsd), + ("powerpc64-unknown-openbsd", powerpc64_unknown_openbsd), ("sparc64-unknown-openbsd", sparc64_unknown_openbsd), ("x86_64-unknown-openbsd", x86_64_unknown_openbsd), diff --git a/compiler/rustc_target/src/spec/powerpc64_unknown_openbsd.rs b/compiler/rustc_target/src/spec/powerpc64_unknown_openbsd.rs new file mode 100644 index 0000000000000..9cb3a67dc58b3 --- /dev/null +++ b/compiler/rustc_target/src/spec/powerpc64_unknown_openbsd.rs @@ -0,0 +1,17 @@ +use crate::abi::Endian; +use crate::spec::{LinkerFlavor, Target, TargetOptions}; + +pub fn target() -> Target { + let mut base = super::openbsd_base::opts(); + base.cpu = "ppc64".into(); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]); + base.max_atomic_width = Some(64); + + Target { + llvm_target: "powerpc64-unknown-openbsd".into(), + pointer_width: 64, + data_layout: "E-m:e-i64:64-n32:64".into(), + arch: "powerpc64".into(), + options: TargetOptions { endian: Endian::Big, mcount: "_mcount".into(), ..base }, + } +} diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index f0f57f9338672..34bd201d1cd07 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -277,6 +277,7 @@ target | std | host | notes `powerpc64-unknown-linux-musl` | ? | | `powerpc64-wrs-vxworks` | ? | | `powerpc64le-unknown-linux-musl` | ? | | +[`powerpc64-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | OpenBSD/powerpc64 `riscv32gc-unknown-linux-gnu` | | | RISC-V Linux (kernel 5.4, glibc 2.33) `riscv32gc-unknown-linux-musl` | | | RISC-V Linux (kernel 5.4, musl + RISCV32 support patches) `riscv32im-unknown-none-elf` | * | | Bare RISC-V (RV32IM ISA) diff --git a/src/doc/rustc/src/platform-support/openbsd.md b/src/doc/rustc/src/platform-support/openbsd.md index b2ac776eada48..432d8a9999419 100644 --- a/src/doc/rustc/src/platform-support/openbsd.md +++ b/src/doc/rustc/src/platform-support/openbsd.md @@ -12,6 +12,7 @@ The target names follow this format: `$ARCH-unknown-openbsd`, where `$ARCH` spec |--------------------------------|-------------|------------------| | `aarch64-unknown-openbsd` | libc++ | [64-bit ARM systems](https://www.openbsd.org/arm64.html) | | `i686-unknown-openbsd` | libc++ | [Standard PC and clones based on the Intel i386 architecture and compatible processors](https://www.openbsd.org/i386.html) | +| `powerpc64-unknown-openbsd` | libc++ | [IBM POWER-based PowerNV systems](https://www.openbsd.org/powerpc64.html) | | `sparc64-unknown-openbsd` | estdc++ | [Sun UltraSPARC and Fujitsu SPARC64 systems](https://www.openbsd.org/sparc64.html) | | `x86_64-unknown-openbsd` | libc++ | [AMD64-based systems](https://www.openbsd.org/amd64.html) | From 1de5b226782361d9ea1929fd619f4fc2aaab1094 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Marie?= Date: Sun, 28 Aug 2022 05:22:21 +0000 Subject: [PATCH 13/24] add riscv64gc-unknown-openbsd support (target riscv64-unknown-openbsd on OpenBSD) - add platform-support documentation - add riscv64gc-unknown-openbsd spec - do not try to link with -latomic on openbsd --- compiler/rustc_llvm/build.rs | 4 ++-- compiler/rustc_target/src/spec/mod.rs | 1 + .../src/spec/riscv64gc_unknown_openbsd.rs | 18 ++++++++++++++++++ src/bootstrap/native.rs | 5 +++-- src/doc/rustc/src/platform-support.md | 1 + src/doc/rustc/src/platform-support/openbsd.md | 1 + 6 files changed, 26 insertions(+), 4 deletions(-) create mode 100644 compiler/rustc_target/src/spec/riscv64gc_unknown_openbsd.rs diff --git a/compiler/rustc_llvm/build.rs b/compiler/rustc_llvm/build.rs index abb68f3afe527..28e092c1eb72c 100644 --- a/compiler/rustc_llvm/build.rs +++ b/compiler/rustc_llvm/build.rs @@ -342,10 +342,10 @@ fn main() { }; // RISC-V GCC erroneously requires libatomic for sub-word - // atomic operations. FreeBSD uses Clang as its system + // atomic operations. Some BSD uses Clang as its system // compiler and provides no libatomic in its base system so // does not want this. - if !target.contains("freebsd") && target.starts_with("riscv") { + if target.starts_with("riscv") && !target.contains("freebsd") && !target.contains("openbsd") { println!("cargo:rustc-link-lib=atomic"); } diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index d579508768313..d1bffa2e6ee30 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -895,6 +895,7 @@ supported_targets! { ("i686-unknown-openbsd", i686_unknown_openbsd), ("powerpc-unknown-openbsd", powerpc_unknown_openbsd), ("powerpc64-unknown-openbsd", powerpc64_unknown_openbsd), + ("riscv64gc-unknown-openbsd", riscv64gc_unknown_openbsd), ("sparc64-unknown-openbsd", sparc64_unknown_openbsd), ("x86_64-unknown-openbsd", x86_64_unknown_openbsd), diff --git a/compiler/rustc_target/src/spec/riscv64gc_unknown_openbsd.rs b/compiler/rustc_target/src/spec/riscv64gc_unknown_openbsd.rs new file mode 100644 index 0000000000000..cd10f3afaac06 --- /dev/null +++ b/compiler/rustc_target/src/spec/riscv64gc_unknown_openbsd.rs @@ -0,0 +1,18 @@ +use crate::spec::{CodeModel, Target, TargetOptions}; + +pub fn target() -> Target { + Target { + llvm_target: "riscv64-unknown-openbsd".into(), + pointer_width: 64, + data_layout: "e-m:e-p:64:64-i64:64-i128:128-n64-S128".into(), + arch: "riscv64".into(), + options: TargetOptions { + code_model: Some(CodeModel::Medium), + cpu: "generic-rv64".into(), + features: "+m,+a,+f,+d,+c".into(), + llvm_abiname: "lp64d".into(), + max_atomic_width: Some(64), + ..super::openbsd_base::opts() + }, + } +} diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index 7ecf74d3068d0..e1cc8d671d7fe 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -432,12 +432,13 @@ impl Step for Llvm { cfg.define("LLVM_LINK_LLVM_DYLIB", "ON"); } - if target.starts_with("riscv") && !target.contains("freebsd") { + if target.starts_with("riscv") && !target.contains("freebsd") && !target.contains("openbsd") + { // RISC-V GCC erroneously requires linking against // `libatomic` when using 1-byte and 2-byte C++ // atomics but the LLVM build system check cannot // detect this. Therefore it is set manually here. - // FreeBSD uses Clang as its system compiler and + // Some BSD uses Clang as its system compiler and // provides no libatomic in its base system so does // not want this. ldflags.exe.push(" -latomic"); diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 34bd201d1cd07..742fbe11d9c6f 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -285,6 +285,7 @@ target | std | host | notes `riscv32imc-esp-espidf` | ✓ | | RISC-V ESP-IDF `riscv64gc-unknown-freebsd` | | | RISC-V FreeBSD `riscv64gc-unknown-linux-musl` | | | RISC-V Linux (kernel 4.20, musl 1.2.0) +[`riscv64gc-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | OpenBSD/riscv64 `s390x-unknown-linux-musl` | | | S390x Linux (kernel 3.2, MUSL) `sparc-unknown-linux-gnu` | ✓ | | 32-bit SPARC Linux `sparc64-unknown-netbsd` | ✓ | ✓ | NetBSD/sparc64 diff --git a/src/doc/rustc/src/platform-support/openbsd.md b/src/doc/rustc/src/platform-support/openbsd.md index 432d8a9999419..4ce80157dbf9c 100644 --- a/src/doc/rustc/src/platform-support/openbsd.md +++ b/src/doc/rustc/src/platform-support/openbsd.md @@ -13,6 +13,7 @@ The target names follow this format: `$ARCH-unknown-openbsd`, where `$ARCH` spec | `aarch64-unknown-openbsd` | libc++ | [64-bit ARM systems](https://www.openbsd.org/arm64.html) | | `i686-unknown-openbsd` | libc++ | [Standard PC and clones based on the Intel i386 architecture and compatible processors](https://www.openbsd.org/i386.html) | | `powerpc64-unknown-openbsd` | libc++ | [IBM POWER-based PowerNV systems](https://www.openbsd.org/powerpc64.html) | +| `riscv64gc-unknown-openbsd` | libc++ | [64-bit RISC-V systems](https://www.openbsd.org/riscv64.html) | | `sparc64-unknown-openbsd` | estdc++ | [Sun UltraSPARC and Fujitsu SPARC64 systems](https://www.openbsd.org/sparc64.html) | | `x86_64-unknown-openbsd` | libc++ | [AMD64-based systems](https://www.openbsd.org/amd64.html) | From 1b8025a24c4b063d2566671f0664e5dfc263c2a4 Mon Sep 17 00:00:00 2001 From: Thom Chiovoloni Date: Mon, 29 Aug 2022 15:36:01 -0700 Subject: [PATCH 14/24] Fix some possible UB in std::sys::windows --- library/std/src/sys/windows/c.rs | 6 ++++ library/std/src/sys/windows/fs.rs | 40 +++++++++++++++---------- library/std/src/sys/windows/mod.rs | 23 ++++++++++++++ library/std/src/sys/windows/os/tests.rs | 18 +++++++++++ 4 files changed, 71 insertions(+), 16 deletions(-) diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs index ef3f6a9ba1755..865e2ad4b4347 100644 --- a/library/std/src/sys/windows/c.rs +++ b/library/std/src/sys/windows/c.rs @@ -503,6 +503,8 @@ pub struct FILE_END_OF_FILE_INFO { pub EndOfFile: LARGE_INTEGER, } +/// NB: Use carefully! In general using this as a reference is likely to get the +/// provenance wrong for the `rest` field! #[repr(C)] pub struct REPARSE_DATA_BUFFER { pub ReparseTag: c_uint, @@ -511,6 +513,8 @@ pub struct REPARSE_DATA_BUFFER { pub rest: (), } +/// NB: Use carefully! In general using this as a reference is likely to get the +/// provenance wrong for the `PathBuffer` field! #[repr(C)] pub struct SYMBOLIC_LINK_REPARSE_BUFFER { pub SubstituteNameOffset: c_ushort, @@ -521,6 +525,8 @@ pub struct SYMBOLIC_LINK_REPARSE_BUFFER { pub PathBuffer: WCHAR, } +/// NB: Use carefully! In general using this as a reference is likely to get the +/// provenance wrong for the `PathBuffer` field! #[repr(C)] pub struct MOUNT_POINT_REPARSE_BUFFER { pub SubstituteNameOffset: c_ushort, diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs index 9653b1abfc3d8..01f914617a073 100644 --- a/library/std/src/sys/windows/fs.rs +++ b/library/std/src/sys/windows/fs.rs @@ -11,7 +11,7 @@ use crate::slice; use crate::sync::Arc; use crate::sys::handle::Handle; use crate::sys::time::SystemTime; -use crate::sys::{c, cvt}; +use crate::sys::{c, cvt, AlignedAs}; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::thread; @@ -47,6 +47,9 @@ pub struct ReadDir { first: Option, } +type AlignedReparseBuf = + AlignedAs; + struct FindNextFileHandle(c::HANDLE); unsafe impl Send for FindNextFileHandle {} @@ -326,9 +329,9 @@ impl File { cvt(c::GetFileInformationByHandle(self.handle.as_raw_handle(), &mut info))?; let mut reparse_tag = 0; if info.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 { - let mut b = [0; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; + let mut b = AlignedReparseBuf::new([0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]); if let Ok((_, buf)) = self.reparse_point(&mut b) { - reparse_tag = buf.ReparseTag; + reparse_tag = (*buf).ReparseTag; } } Ok(FileAttr { @@ -389,7 +392,7 @@ impl File { attr.file_size = info.AllocationSize as u64; attr.number_of_links = Some(info.NumberOfLinks); if attr.file_type().is_reparse_point() { - let mut b = [0; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; + let mut b = AlignedReparseBuf::new([0; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]); if let Ok((_, buf)) = self.reparse_point(&mut b) { attr.reparse_tag = buf.ReparseTag; } @@ -458,10 +461,13 @@ impl File { Ok(Self { handle: self.handle.try_clone()? }) } - fn reparse_point<'a>( + // NB: returned pointer is derived from `space`, and has provenance to + // match. A raw pointer is returned rather than a reference in order to + // avoid narrowing provenance to the actual `REPARSE_DATA_BUFFER`. + fn reparse_point( &self, - space: &'a mut [u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE], - ) -> io::Result<(c::DWORD, &'a c::REPARSE_DATA_BUFFER)> { + space: &mut AlignedReparseBuf, + ) -> io::Result<(c::DWORD, *const c::REPARSE_DATA_BUFFER)> { unsafe { let mut bytes = 0; cvt({ @@ -470,26 +476,27 @@ impl File { c::FSCTL_GET_REPARSE_POINT, ptr::null_mut(), 0, - space.as_mut_ptr() as *mut _, - space.len() as c::DWORD, + space.value.as_mut_ptr() as *mut _, + space.value.len() as c::DWORD, &mut bytes, ptr::null_mut(), ) })?; - Ok((bytes, &*(space.as_ptr() as *const c::REPARSE_DATA_BUFFER))) + Ok((bytes, space.value.as_ptr().cast::())) } } fn readlink(&self) -> io::Result { - let mut space = [0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; + let mut space = AlignedReparseBuf::new([0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]); let (_bytes, buf) = self.reparse_point(&mut space)?; unsafe { - let (path_buffer, subst_off, subst_len, relative) = match buf.ReparseTag { + let (path_buffer, subst_off, subst_len, relative) = match (*buf).ReparseTag { c::IO_REPARSE_TAG_SYMLINK => { let info: *const c::SYMBOLIC_LINK_REPARSE_BUFFER = - &buf.rest as *const _ as *const _; + ptr::addr_of!((*buf).rest).cast(); + assert!(info.is_aligned()); ( - &(*info).PathBuffer as *const _ as *const u16, + ptr::addr_of!((*info).PathBuffer).cast::(), (*info).SubstituteNameOffset / 2, (*info).SubstituteNameLength / 2, (*info).Flags & c::SYMLINK_FLAG_RELATIVE != 0, @@ -497,9 +504,10 @@ impl File { } c::IO_REPARSE_TAG_MOUNT_POINT => { let info: *const c::MOUNT_POINT_REPARSE_BUFFER = - &buf.rest as *const _ as *const _; + ptr::addr_of!((*buf).rest).cast(); + assert!(info.is_aligned()); ( - &(*info).PathBuffer as *const _ as *const u16, + ptr::addr_of!((*info).PathBuffer).cast::(), (*info).SubstituteNameOffset / 2, (*info).SubstituteNameLength / 2, false, diff --git a/library/std/src/sys/windows/mod.rs b/library/std/src/sys/windows/mod.rs index a9846a484880b..fbbc5cb79fb8b 100644 --- a/library/std/src/sys/windows/mod.rs +++ b/library/std/src/sys/windows/mod.rs @@ -329,3 +329,26 @@ pub fn abort_internal() -> ! { } crate::intrinsics::abort(); } + +/// Used for some win32 buffers which are stack allocated, for example: +/// `AlignedAs` +#[repr(C)] +#[derive(Copy, Clone)] +pub struct AlignedAs { + /// Use `[Aligner; 0]` as a sort of `PhantomAlignNextField`. This + /// is a bit of a hack, and could break (in a way that's caught by tests) if + /// #81996 is fixed. + aligner: [Aligner; 0], + /// The aligned value. Public rather than exposed via accessors so that if + /// needed it can be used with `addr_of` and such (also, this is less code). + pub value: Alignee, +} + +impl AlignedAs { + // This is frequently used with large stack buffers, so force-inline to + // try and avoid using 2x as much stack space in debug builds. + #[inline(always)] + pub const fn new(value: Alignee) -> Self { + Self { aligner: [], value } + } +} diff --git a/library/std/src/sys/windows/os/tests.rs b/library/std/src/sys/windows/os/tests.rs index 458d6e11c2098..532be0cf083ba 100644 --- a/library/std/src/sys/windows/os/tests.rs +++ b/library/std/src/sys/windows/os/tests.rs @@ -11,3 +11,21 @@ fn ntstatus_error() { .contains("FormatMessageW() returned error") ); } + +#[test] +fn smoketest_aligned_as() { + use crate::{ + mem::{align_of, size_of}, + ptr::addr_of, + sys::{c, AlignedAs}, + }; + type AlignedReparseBuf = + AlignedAs; + assert!(size_of::() >= c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE); + assert_eq!(align_of::(), align_of::()); + let a = AlignedReparseBuf::new([0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]); + // Quick and dirty offsetof check. + assert_eq!(addr_of!(a).cast::(), addr_of!(a.value).cast::()); + // Smoke check that it's actually aligned. + assert!(addr_of!(a.value).is_aligned_to(align_of::())); +} From 5a4f7d4ad3e1124291b177af369023c3f181422e Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 30 Aug 2022 06:19:48 +0000 Subject: [PATCH 15/24] Tweak WellFormedLocs a bit --- compiler/rustc_typeck/src/check/wfcheck.rs | 21 ++++++++++++++++--- compiler/rustc_typeck/src/hir_wf_check.rs | 4 ++++ ...ature-gate-object_safe_for_dispatch.stderr | 8 +++---- ...-trait-in-return-position-dyn-trait.stderr | 4 ++-- ...bject-safety-associated-consts.curr.stderr | 4 ++-- .../object-safety/object-safety-bounds.stderr | 4 ++-- .../object-safety-generics.curr.stderr | 8 +++---- .../object-safety-mentions-Self.curr.stderr | 8 +++---- .../object-safety-no-static.curr.stderr | 4 ++-- .../object-safety-sized-2.curr.stderr | 4 ++-- .../object-safety-sized.curr.stderr | 4 ++-- src/test/ui/type/type-check-defaults.stderr | 8 +++---- src/test/ui/wf/wf-trait-fn-ret.stderr | 4 ++-- 13 files changed, 52 insertions(+), 33 deletions(-) diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index ce42647c837a5..ba42453bd60e9 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -1262,7 +1262,11 @@ fn check_impl<'tcx>( } None => { let self_ty = tcx.type_of(item.def_id); - let self_ty = wfcx.normalize(item.span, None, self_ty); + let self_ty = wfcx.normalize( + item.span, + Some(WellFormedLoc::Ty(item.hir_id().expect_owner())), + self_ty, + ); wfcx.register_wf_obligation( ast_self_ty.span, Some(WellFormedLoc::Ty(item.hir_id().expect_owner())), @@ -1307,7 +1311,11 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id // parameter includes another (e.g., ``). In those cases, we can't // be sure if it will error or not as user might always specify the other. if !ty.needs_subst() { - wfcx.register_wf_obligation(tcx.def_span(param.def_id), None, ty.into()); + wfcx.register_wf_obligation( + tcx.def_span(param.def_id), + Some(WellFormedLoc::Ty(param.def_id.expect_local())), + ty.into(), + ); } } } @@ -1512,7 +1520,14 @@ fn check_fn_or_method<'tcx>( ); } - wfcx.register_wf_obligation(hir_decl.output.span(), None, sig.output().into()); + wfcx.register_wf_obligation( + hir_decl.output.span(), + Some(WellFormedLoc::Param { + function: def_id, + param_idx: sig.inputs().len().try_into().unwrap(), + }), + sig.output().into(), + ); check_where_clauses(wfcx, span, def_id); } diff --git a/compiler/rustc_typeck/src/hir_wf_check.rs b/compiler/rustc_typeck/src/hir_wf_check.rs index fd9715e6ca374..7b080dc2942e0 100644 --- a/compiler/rustc_typeck/src/hir_wf_check.rs +++ b/compiler/rustc_typeck/src/hir_wf_check.rs @@ -140,6 +140,10 @@ fn diagnostic_hir_wf_check<'tcx>( hir::Node::ForeignItem(ForeignItem { kind: ForeignItemKind::Static(ty, _), .. }) => Some(*ty), + hir::Node::GenericParam(hir::GenericParam { + kind: hir::GenericParamKind::Type { default: Some(ty), .. }, + .. + }) => Some(*ty), ref node => bug!("Unexpected node {:?}", node), }, WellFormedLoc::Param { function: _, param_idx } => { diff --git a/src/test/ui/feature-gates/feature-gate-object_safe_for_dispatch.stderr b/src/test/ui/feature-gates/feature-gate-object_safe_for_dispatch.stderr index 72cb4cc843cc4..d76c697fe737d 100644 --- a/src/test/ui/feature-gates/feature-gate-object_safe_for_dispatch.stderr +++ b/src/test/ui/feature-gates/feature-gate-object_safe_for_dispatch.stderr @@ -13,10 +13,10 @@ LL | trait NonObjectSafe1: Sized {} | this trait cannot be made into an object... error[E0038]: the trait `NonObjectSafe2` cannot be made into an object - --> $DIR/feature-gate-object_safe_for_dispatch.rs:22:36 + --> $DIR/feature-gate-object_safe_for_dispatch.rs:22:45 | LL | fn return_non_object_safe_ref() -> &'static dyn NonObjectSafe2 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `NonObjectSafe2` cannot be made into an object + | ^^^^^^^^^^^^^^^^^^ `NonObjectSafe2` cannot be made into an object | note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit --> $DIR/feature-gate-object_safe_for_dispatch.rs:7:8 @@ -50,10 +50,10 @@ LL | fn foo(&self); = help: consider moving `foo` to another trait error[E0038]: the trait `NonObjectSafe4` cannot be made into an object - --> $DIR/feature-gate-object_safe_for_dispatch.rs:31:35 + --> $DIR/feature-gate-object_safe_for_dispatch.rs:31:47 | LL | fn return_non_object_safe_rc() -> std::rc::Rc { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `NonObjectSafe4` cannot be made into an object + | ^^^^^^^^^^^^^^^^^^ `NonObjectSafe4` cannot be made into an object | note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit --> $DIR/feature-gate-object_safe_for_dispatch.rs:15:22 diff --git a/src/test/ui/impl-trait/object-unsafe-trait-in-return-position-dyn-trait.stderr b/src/test/ui/impl-trait/object-unsafe-trait-in-return-position-dyn-trait.stderr index 365ecd9fcfa1d..687dbe65e6c38 100644 --- a/src/test/ui/impl-trait/object-unsafe-trait-in-return-position-dyn-trait.stderr +++ b/src/test/ui/impl-trait/object-unsafe-trait-in-return-position-dyn-trait.stderr @@ -21,10 +21,10 @@ LL | fn foo() -> Self where Self: Sized; | +++++++++++++++++ error[E0038]: the trait `NotObjectSafe` cannot be made into an object - --> $DIR/object-unsafe-trait-in-return-position-dyn-trait.rs:28:13 + --> $DIR/object-unsafe-trait-in-return-position-dyn-trait.rs:28:17 | LL | fn cat() -> Box { - | ^^^^^^^^^^^^^^^^^^^^^^ `NotObjectSafe` cannot be made into an object + | ^^^^^^^^^^^^^^^^^ `NotObjectSafe` cannot be made into an object | note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit --> $DIR/object-unsafe-trait-in-return-position-dyn-trait.rs:3:8 diff --git a/src/test/ui/object-safety/object-safety-associated-consts.curr.stderr b/src/test/ui/object-safety/object-safety-associated-consts.curr.stderr index 9dd144fee24a6..5f94c9284ea66 100644 --- a/src/test/ui/object-safety/object-safety-associated-consts.curr.stderr +++ b/src/test/ui/object-safety/object-safety-associated-consts.curr.stderr @@ -1,8 +1,8 @@ error[E0038]: the trait `Bar` cannot be made into an object - --> $DIR/object-safety-associated-consts.rs:12:30 + --> $DIR/object-safety-associated-consts.rs:12:31 | LL | fn make_bar(t: &T) -> &dyn Bar { - | ^^^^^^^^ `Bar` cannot be made into an object + | ^^^^^^^ `Bar` cannot be made into an object | note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit --> $DIR/object-safety-associated-consts.rs:9:11 diff --git a/src/test/ui/object-safety/object-safety-bounds.stderr b/src/test/ui/object-safety/object-safety-bounds.stderr index 89c4f8ced79f6..29ffb5448427d 100644 --- a/src/test/ui/object-safety/object-safety-bounds.stderr +++ b/src/test/ui/object-safety/object-safety-bounds.stderr @@ -1,8 +1,8 @@ error[E0038]: the trait `X` cannot be made into an object - --> $DIR/object-safety-bounds.rs:7:11 + --> $DIR/object-safety-bounds.rs:7:15 | LL | fn f() -> Box> { - | ^^^^^^^^^^^^^^^^^^^ `X` cannot be made into an object + | ^^^^^^^^^^^^^^ `X` cannot be made into an object | note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit --> $DIR/object-safety-bounds.rs:4:13 diff --git a/src/test/ui/object-safety/object-safety-generics.curr.stderr b/src/test/ui/object-safety/object-safety-generics.curr.stderr index 345950f1ae670..4581037526397 100644 --- a/src/test/ui/object-safety/object-safety-generics.curr.stderr +++ b/src/test/ui/object-safety/object-safety-generics.curr.stderr @@ -1,8 +1,8 @@ error[E0038]: the trait `Bar` cannot be made into an object - --> $DIR/object-safety-generics.rs:18:30 + --> $DIR/object-safety-generics.rs:18:31 | LL | fn make_bar(t: &T) -> &dyn Bar { - | ^^^^^^^^ `Bar` cannot be made into an object + | ^^^^^^^ `Bar` cannot be made into an object | note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit --> $DIR/object-safety-generics.rs:10:8 @@ -14,10 +14,10 @@ LL | fn bar(&self, t: T); = help: consider moving `bar` to another trait error[E0038]: the trait `Bar` cannot be made into an object - --> $DIR/object-safety-generics.rs:24:39 + --> $DIR/object-safety-generics.rs:24:40 | LL | fn make_bar_explicit(t: &T) -> &dyn Bar { - | ^^^^^^^^ `Bar` cannot be made into an object + | ^^^^^^^ `Bar` cannot be made into an object | note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit --> $DIR/object-safety-generics.rs:10:8 diff --git a/src/test/ui/object-safety/object-safety-mentions-Self.curr.stderr b/src/test/ui/object-safety/object-safety-mentions-Self.curr.stderr index f91c9b9856055..de430a89bf82b 100644 --- a/src/test/ui/object-safety/object-safety-mentions-Self.curr.stderr +++ b/src/test/ui/object-safety/object-safety-mentions-Self.curr.stderr @@ -1,8 +1,8 @@ error[E0038]: the trait `Bar` cannot be made into an object - --> $DIR/object-safety-mentions-Self.rs:22:30 + --> $DIR/object-safety-mentions-Self.rs:22:31 | LL | fn make_bar(t: &T) -> &dyn Bar { - | ^^^^^^^^ `Bar` cannot be made into an object + | ^^^^^^^ `Bar` cannot be made into an object | note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit --> $DIR/object-safety-mentions-Self.rs:11:22 @@ -14,10 +14,10 @@ LL | fn bar(&self, x: &Self); = help: consider moving `bar` to another trait error[E0038]: the trait `Baz` cannot be made into an object - --> $DIR/object-safety-mentions-Self.rs:28:30 + --> $DIR/object-safety-mentions-Self.rs:28:31 | LL | fn make_baz(t: &T) -> &dyn Baz { - | ^^^^^^^^ `Baz` cannot be made into an object + | ^^^^^^^ `Baz` cannot be made into an object | note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit --> $DIR/object-safety-mentions-Self.rs:15:22 diff --git a/src/test/ui/object-safety/object-safety-no-static.curr.stderr b/src/test/ui/object-safety/object-safety-no-static.curr.stderr index bd8cf4e30f7fa..1b025229e543e 100644 --- a/src/test/ui/object-safety/object-safety-no-static.curr.stderr +++ b/src/test/ui/object-safety/object-safety-no-static.curr.stderr @@ -1,8 +1,8 @@ error[E0038]: the trait `Foo` cannot be made into an object - --> $DIR/object-safety-no-static.rs:12:18 + --> $DIR/object-safety-no-static.rs:12:22 | LL | fn diverges() -> Box { - | ^^^^^^^^^^^^ `Foo` cannot be made into an object + | ^^^^^^^ `Foo` cannot be made into an object | note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit --> $DIR/object-safety-no-static.rs:9:8 diff --git a/src/test/ui/object-safety/object-safety-sized-2.curr.stderr b/src/test/ui/object-safety/object-safety-sized-2.curr.stderr index 71236c8e38438..b019264128e76 100644 --- a/src/test/ui/object-safety/object-safety-sized-2.curr.stderr +++ b/src/test/ui/object-safety/object-safety-sized-2.curr.stderr @@ -1,8 +1,8 @@ error[E0038]: the trait `Bar` cannot be made into an object - --> $DIR/object-safety-sized-2.rs:14:30 + --> $DIR/object-safety-sized-2.rs:14:31 | LL | fn make_bar(t: &T) -> &dyn Bar { - | ^^^^^^^^ `Bar` cannot be made into an object + | ^^^^^^^ `Bar` cannot be made into an object | note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit --> $DIR/object-safety-sized-2.rs:9:18 diff --git a/src/test/ui/object-safety/object-safety-sized.curr.stderr b/src/test/ui/object-safety/object-safety-sized.curr.stderr index 94b06ee934d44..97481312142fb 100644 --- a/src/test/ui/object-safety/object-safety-sized.curr.stderr +++ b/src/test/ui/object-safety/object-safety-sized.curr.stderr @@ -1,8 +1,8 @@ error[E0038]: the trait `Bar` cannot be made into an object - --> $DIR/object-safety-sized.rs:12:30 + --> $DIR/object-safety-sized.rs:12:31 | LL | fn make_bar(t: &T) -> &dyn Bar { - | ^^^^^^^^ `Bar` cannot be made into an object + | ^^^^^^^ `Bar` cannot be made into an object | note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit --> $DIR/object-safety-sized.rs:8:13 diff --git a/src/test/ui/type/type-check-defaults.stderr b/src/test/ui/type/type-check-defaults.stderr index 56a9b5317f76e..cf77c057d46d4 100644 --- a/src/test/ui/type/type-check-defaults.stderr +++ b/src/test/ui/type/type-check-defaults.stderr @@ -1,8 +1,8 @@ error[E0277]: a value of type `i32` cannot be built from an iterator over elements of type `i32` - --> $DIR/type-check-defaults.rs:6:19 + --> $DIR/type-check-defaults.rs:6:23 | LL | struct WellFormed>(Z); - | ^^^^^^^^^^^^^^^^^ value of type `i32` cannot be built from `std::iter::Iterator` + | ^^^^^^^^^^^^^ value of type `i32` cannot be built from `std::iter::Iterator` | = help: the trait `FromIterator` is not implemented for `i32` note: required by a bound in `Foo` @@ -12,10 +12,10 @@ LL | struct Foo>(T, U); | ^^^^^^^^^^^^^^^ required by this bound in `Foo` error[E0277]: a value of type `i32` cannot be built from an iterator over elements of type `i32` - --> $DIR/type-check-defaults.rs:8:27 + --> $DIR/type-check-defaults.rs:8:38 | LL | struct WellFormedNoBounds>(Z); - | ^^^^^^^^^^^^^^^^^^^^^^^^ value of type `i32` cannot be built from `std::iter::Iterator` + | ^^^^^^^^^^^^^ value of type `i32` cannot be built from `std::iter::Iterator` | = help: the trait `FromIterator` is not implemented for `i32` note: required by a bound in `Foo` diff --git a/src/test/ui/wf/wf-trait-fn-ret.stderr b/src/test/ui/wf/wf-trait-fn-ret.stderr index a59ba3400a4d1..9bd3cc7711b84 100644 --- a/src/test/ui/wf/wf-trait-fn-ret.stderr +++ b/src/test/ui/wf/wf-trait-fn-ret.stderr @@ -1,8 +1,8 @@ error[E0277]: the trait bound `Self: Eq` is not satisfied - --> $DIR/wf-trait-fn-ret.rs:10:22 + --> $DIR/wf-trait-fn-ret.rs:10:23 | LL | fn bar(&self) -> &Bar; - | ^^^^^^^^^^ the trait `Eq` is not implemented for `Self` + | ^^^^^^^^^ the trait `Eq` is not implemented for `Self` | note: required by a bound in `Bar` --> $DIR/wf-trait-fn-ret.rs:7:14 From d9c760db43d8ab701a71f633d820efc72d8cedea Mon Sep 17 00:00:00 2001 From: Thom Chiovoloni Date: Tue, 30 Aug 2022 00:16:53 -0700 Subject: [PATCH 16/24] Fix UWP and use `AlignedReparseBuf` in `symlink_junction_inner` --- library/std/src/sys/windows/fs.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs index 01f914617a073..1545ba0d023fa 100644 --- a/library/std/src/sys/windows/fs.rs +++ b/library/std/src/sys/windows/fs.rs @@ -394,7 +394,7 @@ impl File { if attr.file_type().is_reparse_point() { let mut b = AlignedReparseBuf::new([0; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]); if let Ok((_, buf)) = self.reparse_point(&mut b) { - attr.reparse_tag = buf.ReparseTag; + attr.reparse_tag = (*buf).ReparseTag; } } Ok(attr) @@ -1345,9 +1345,10 @@ fn symlink_junction_inner(original: &Path, junction: &Path) -> io::Result<()> { let h = f.as_inner().as_raw_handle(); unsafe { - let mut data = [0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; - let db = data.as_mut_ptr() as *mut c::REPARSE_MOUNTPOINT_DATA_BUFFER; - let buf = &mut (*db).ReparseTarget as *mut c::WCHAR; + let mut data = AlignedReparseBuf::new([0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]); + let data_ptr = data.value.as_mut_ptr(); + let db = data_ptr.cast::(); + let buf = ptr::addr_of_mut!((*db).ReparseTarget).cast::(); let mut i = 0; // FIXME: this conversion is very hacky let v = br"\??\"; @@ -1367,7 +1368,7 @@ fn symlink_junction_inner(original: &Path, junction: &Path) -> io::Result<()> { cvt(c::DeviceIoControl( h as *mut _, c::FSCTL_SET_REPARSE_POINT, - data.as_ptr() as *mut _, + data_ptr.cast(), (*db).ReparseDataLength + 8, ptr::null_mut(), 0, From 8af7f4208a789ba27a341c99df2de9c018b79828 Mon Sep 17 00:00:00 2001 From: Xiretza Date: Mon, 22 Aug 2022 13:38:08 +0200 Subject: [PATCH 17/24] Code deduplication in tool_only_multipart_suggestion --- compiler/rustc_errors/src/diagnostic.rs | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 506198df4d8ed..f75e2596f361b 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -686,19 +686,12 @@ impl Diagnostic { suggestion: Vec<(Span, String)>, applicability: Applicability, ) -> &mut Self { - assert!(!suggestion.is_empty()); - self.push_suggestion(CodeSuggestion { - substitutions: vec![Substitution { - parts: suggestion - .into_iter() - .map(|(span, snippet)| SubstitutionPart { snippet, span }) - .collect(), - }], - msg: self.subdiagnostic_message_to_diagnostic_message(msg), - style: SuggestionStyle::CompletelyHidden, + self.multipart_suggestion_with_style( + msg, + suggestion, applicability, - }); - self + SuggestionStyle::CompletelyHidden, + ) } /// Prints out a message with a suggested edit of the code. From 6e8dad5c073555dcbea9a276f76db47e3d7cc693 Mon Sep 17 00:00:00 2001 From: Xiretza Date: Mon, 22 Aug 2022 18:18:54 +0200 Subject: [PATCH 18/24] Use span_suggestion_with_style in SessionSubdiagnostic derive --- .../src/diagnostics/subdiagnostic.rs | 104 +++++++++++------- 1 file changed, 65 insertions(+), 39 deletions(-) diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index 666dbc23c287c..0db15f0feea72 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -28,6 +28,39 @@ enum SubdiagnosticSuggestionKind { Verbose, } +impl FromStr for SubdiagnosticSuggestionKind { + type Err = (); + + fn from_str(s: &str) -> Result { + match s { + "" => Ok(SubdiagnosticSuggestionKind::Normal), + "_short" => Ok(SubdiagnosticSuggestionKind::Short), + "_hidden" => Ok(SubdiagnosticSuggestionKind::Hidden), + "_verbose" => Ok(SubdiagnosticSuggestionKind::Verbose), + _ => Err(()), + } + } +} + +impl SubdiagnosticSuggestionKind { + pub fn to_suggestion_style(&self) -> TokenStream { + match self { + SubdiagnosticSuggestionKind::Normal => { + quote! { rustc_errors::SuggestionStyle::ShowCode } + } + SubdiagnosticSuggestionKind::Short => { + quote! { rustc_errors::SuggestionStyle::HideCodeInline } + } + SubdiagnosticSuggestionKind::Hidden => { + quote! { rustc_errors::SuggestionStyle::HideCodeAlways } + } + SubdiagnosticSuggestionKind::Verbose => { + quote! { rustc_errors::SuggestionStyle::ShowAlways } + } + } + } +} + /// Which kind of subdiagnostic is being created from a variant? #[derive(Clone, Copy)] enum SubdiagnosticKind { @@ -52,17 +85,15 @@ impl FromStr for SubdiagnosticKind { "note" => Ok(SubdiagnosticKind::Note), "help" => Ok(SubdiagnosticKind::Help), "warning" => Ok(SubdiagnosticKind::Warn), - "suggestion" => Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal)), - "suggestion_short" => { - Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Short)) - } - "suggestion_hidden" => { - Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Hidden)) - } - "suggestion_verbose" => { - Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Verbose)) + _ => { + if let Some(suggestion_kind) = + s.strip_prefix("suggestion").and_then(|s| s.parse().ok()) + { + return Ok(SubdiagnosticKind::Suggestion(suggestion_kind)); + }; + + Err(()) } - _ => Err(()), } } } @@ -74,18 +105,7 @@ impl quote::IdentFragment for SubdiagnosticKind { SubdiagnosticKind::Note => write!(f, "note"), SubdiagnosticKind::Help => write!(f, "help"), SubdiagnosticKind::Warn => write!(f, "warn"), - SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal) => { - write!(f, "suggestion") - } - SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Short) => { - write!(f, "suggestion_short") - } - SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Hidden) => { - write!(f, "suggestion_hidden") - } - SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Verbose) => { - write!(f, "suggestion_verbose") - } + SubdiagnosticKind::Suggestion(..) => write!(f, "suggestion_with_style"), } } @@ -461,25 +481,31 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> { let diag = &self.diag; let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind); let message = quote! { rustc_errors::fluent::#slug }; - let call = if matches!(kind, SubdiagnosticKind::Suggestion(..)) { - if let Some(span) = span_field { - quote! { #diag.#name(#span, #message, #code, #applicability); } - } else { - span_err(self.span, "suggestion without `#[primary_span]` field").emit(); - quote! { unreachable!(); } + let call = match kind { + SubdiagnosticKind::Suggestion(style) => { + if let Some(span) = span_field { + let style = style.to_suggestion_style(); + + quote! { #diag.#name(#span, #message, #code, #applicability, #style); } + } else { + span_err(self.span, "suggestion without `#[primary_span]` field").emit(); + quote! { unreachable!(); } + } } - } else if matches!(kind, SubdiagnosticKind::Label) { - if let Some(span) = span_field { - quote! { #diag.#name(#span, #message); } - } else { - span_err(self.span, "label without `#[primary_span]` field").emit(); - quote! { unreachable!(); } + SubdiagnosticKind::Label => { + if let Some(span) = span_field { + quote! { #diag.#name(#span, #message); } + } else { + span_err(self.span, "label without `#[primary_span]` field").emit(); + quote! { unreachable!(); } + } } - } else { - if let Some(span) = span_field { - quote! { #diag.#name(#span, #message); } - } else { - quote! { #diag.#name(#message); } + _ => { + if let Some(span) = span_field { + quote! { #diag.#name(#span, #message); } + } else { + quote! { #diag.#name(#message); } + } } }; From 9dc0643744c87e5470e29718b86f54430cc7ba04 Mon Sep 17 00:00:00 2001 From: Xiretza Date: Fri, 26 Aug 2022 11:09:06 +0200 Subject: [PATCH 19/24] SessionSubdiagnostic: make `#[applicability]` optional --- .../src/diagnostics/subdiagnostic.rs | 12 ++--- .../subdiagnostic-derive.rs | 5 +- .../subdiagnostic-derive.stderr | 51 +++---------------- 3 files changed, 13 insertions(+), 55 deletions(-) diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index 0db15f0feea72..f66565af4c1d8 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -469,14 +469,10 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> { }; let span_field = self.span_field.as_ref().map(|(span, _)| span); - let applicability = match self.applicability.clone() { - Some((applicability, _)) => Some(applicability), - None if is_suggestion => { - span_err(self.span, "suggestion without `applicability`").emit(); - Some(quote! { rustc_errors::Applicability::Unspecified }) - } - None => None, - }; + let applicability = self.applicability.take().map_or_else( + || quote! { rustc_errors::Applicability::Unspecified }, + |(applicability, _)| applicability, + ); let diag = &self.diag; let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind); diff --git a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs index ddfc0d3365df0..2505a5a9975c1 100644 --- a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs +++ b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs @@ -401,7 +401,6 @@ struct AK { #[derive(SessionSubdiagnostic)] #[suggestion(parser::add_paren, code = "...")] -//~^ ERROR suggestion without `applicability` struct AL { #[primary_span] span: Span, @@ -412,7 +411,6 @@ struct AL { #[derive(SessionSubdiagnostic)] #[suggestion(parser::add_paren, code = "...")] -//~^ ERROR suggestion without `applicability` struct AM { #[primary_span] span: Span, @@ -448,8 +446,7 @@ struct AQ; #[derive(SessionSubdiagnostic)] #[suggestion(parser::add_paren, code = "...")] -//~^ ERROR suggestion without `applicability` -//~^^ ERROR suggestion without `#[primary_span]` field +//~^ ERROR suggestion without `#[primary_span]` field struct AR { var: String, } diff --git a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr index a289c4fffd936..68d33323db035 100644 --- a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr +++ b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr @@ -249,36 +249,13 @@ LL | #[applicability] | ^^^^^^^^^^^^^^^^ error: the `#[applicability]` attribute can only be applied to fields of type `Applicability` - --> $DIR/subdiagnostic-derive.rs:408:5 + --> $DIR/subdiagnostic-derive.rs:407:5 | LL | #[applicability] | ^^^^^^^^^^^^^^^^ -error: suggestion without `applicability` - --> $DIR/subdiagnostic-derive.rs:403:1 - | -LL | / #[suggestion(parser::add_paren, code = "...")] -LL | | -LL | | struct AL { -LL | | #[primary_span] -... | -LL | | applicability: Span, -LL | | } - | |_^ - -error: suggestion without `applicability` - --> $DIR/subdiagnostic-derive.rs:414:1 - | -LL | / #[suggestion(parser::add_paren, code = "...")] -LL | | -LL | | struct AM { -LL | | #[primary_span] -LL | | span: Span, -LL | | } - | |_^ - error: suggestion without `code = "..."` - --> $DIR/subdiagnostic-derive.rs:422:1 + --> $DIR/subdiagnostic-derive.rs:420:1 | LL | / #[suggestion(parser::add_paren)] LL | | @@ -290,47 +267,35 @@ LL | | } | |_^ error: invalid applicability - --> $DIR/subdiagnostic-derive.rs:432:46 + --> $DIR/subdiagnostic-derive.rs:430:46 | LL | #[suggestion(parser::add_paren, code ="...", applicability = "foo")] | ^^^^^^^^^^^^^^^^^^^^^ -error: suggestion without `applicability` - --> $DIR/subdiagnostic-derive.rs:450:1 - | -LL | / #[suggestion(parser::add_paren, code = "...")] -LL | | -LL | | -LL | | struct AR { -LL | | var: String, -LL | | } - | |_^ - error: suggestion without `#[primary_span]` field - --> $DIR/subdiagnostic-derive.rs:450:1 + --> $DIR/subdiagnostic-derive.rs:448:1 | LL | / #[suggestion(parser::add_paren, code = "...")] LL | | -LL | | LL | | struct AR { LL | | var: String, LL | | } | |_^ error: unsupported type attribute for subdiagnostic enum - --> $DIR/subdiagnostic-derive.rs:465:1 + --> $DIR/subdiagnostic-derive.rs:462:1 | LL | #[label] | ^^^^^^^^ error: `var` doesn't refer to a field on this type - --> $DIR/subdiagnostic-derive.rs:485:39 + --> $DIR/subdiagnostic-derive.rs:482:39 | LL | #[suggestion(parser::add_paren, code ="{var}", applicability = "machine-applicable")] | ^^^^^^^ error: `var` doesn't refer to a field on this type - --> $DIR/subdiagnostic-derive.rs:504:43 + --> $DIR/subdiagnostic-derive.rs:501:43 | LL | #[suggestion(parser::add_paren, code ="{var}", applicability = "machine-applicable")] | ^^^^^^^ @@ -395,6 +360,6 @@ error[E0425]: cannot find value `slug` in module `rustc_errors::fluent` LL | #[label(slug)] | ^^^^ not found in `rustc_errors::fluent` -error: aborting due to 52 previous errors +error: aborting due to 49 previous errors For more information about this error, try `rustc --explain E0425`. From 1f69fbc345bafc0839111eefff2501398a2681a2 Mon Sep 17 00:00:00 2001 From: Xiretza Date: Fri, 26 Aug 2022 11:17:42 +0200 Subject: [PATCH 20/24] Unify indentation in subdiagnostic-derive test --- .../subdiagnostic-derive.rs | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs index 2505a5a9975c1..0871ad6b22b39 100644 --- a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs +++ b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs @@ -167,8 +167,8 @@ enum P { #[derive(SessionSubdiagnostic)] enum Q { #[bar] -//~^ ERROR `#[bar]` is not a valid attribute -//~^^ ERROR cannot find attribute `bar` in this scope + //~^ ERROR `#[bar]` is not a valid attribute + //~^^ ERROR cannot find attribute `bar` in this scope A { #[primary_span] span: Span, @@ -179,8 +179,8 @@ enum Q { #[derive(SessionSubdiagnostic)] enum R { #[bar = "..."] -//~^ ERROR `#[bar = ...]` is not a valid attribute -//~^^ ERROR cannot find attribute `bar` in this scope + //~^ ERROR `#[bar = ...]` is not a valid attribute + //~^^ ERROR cannot find attribute `bar` in this scope A { #[primary_span] span: Span, @@ -191,8 +191,8 @@ enum R { #[derive(SessionSubdiagnostic)] enum S { #[bar = 4] -//~^ ERROR `#[bar = ...]` is not a valid attribute -//~^^ ERROR cannot find attribute `bar` in this scope + //~^ ERROR `#[bar = ...]` is not a valid attribute + //~^^ ERROR cannot find attribute `bar` in this scope A { #[primary_span] span: Span, @@ -203,8 +203,8 @@ enum S { #[derive(SessionSubdiagnostic)] enum T { #[bar("...")] -//~^ ERROR `#[bar("...")]` is not a valid attribute -//~^^ ERROR cannot find attribute `bar` in this scope + //~^ ERROR `#[bar(...)]` is not a valid attribute + //~^^ ERROR cannot find attribute `bar` in this scope A { #[primary_span] span: Span, @@ -215,7 +215,7 @@ enum T { #[derive(SessionSubdiagnostic)] enum U { #[label(code = "...")] -//~^ ERROR diagnostic slug must be first argument of a `#[label(...)]` attribute + //~^ ERROR diagnostic slug must be first argument of a `#[label(...)]` attribute A { #[primary_span] span: Span, @@ -232,7 +232,7 @@ enum V { var: String, }, B { -//~^ ERROR subdiagnostic kind not specified + //~^ ERROR subdiagnostic kind not specified #[primary_span] span: Span, var: String, @@ -331,16 +331,16 @@ struct AE { #[label(parser::add_paren)] struct AF { #[primary_span] -//~^ NOTE previously specified here + //~^ NOTE previously specified here span_a: Span, #[primary_span] -//~^ ERROR specified multiple times + //~^ ERROR specified multiple times span_b: Span, } #[derive(SessionSubdiagnostic)] struct AG { -//~^ ERROR subdiagnostic kind not specified + //~^ ERROR subdiagnostic kind not specified #[primary_span] span: Span, } @@ -392,10 +392,10 @@ struct AK { #[primary_span] span: Span, #[applicability] -//~^ NOTE previously specified here + //~^ NOTE previously specified here applicability_a: Applicability, #[applicability] -//~^ ERROR specified multiple times + //~^ ERROR specified multiple times applicability_b: Applicability, } @@ -405,7 +405,7 @@ struct AL { #[primary_span] span: Span, #[applicability] -//~^ ERROR the `#[applicability]` attribute can only be applied to fields of type `Applicability` + //~^ ERROR the `#[applicability]` attribute can only be applied to fields of type `Applicability` applicability: Span, } From 5c3490c90186a1b4d1ac66b57ddede55410409e1 Mon Sep 17 00:00:00 2001 From: Thom Chiovoloni Date: Tue, 30 Aug 2022 01:15:59 -0700 Subject: [PATCH 21/24] Replace `AlignedAs` with a more specific `Align8` type --- library/std/src/sys/windows/fs.rs | 27 +++++++++++++------------ library/std/src/sys/windows/mod.rs | 27 ++++++------------------- library/std/src/sys/windows/os/tests.rs | 18 ----------------- 3 files changed, 20 insertions(+), 52 deletions(-) diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs index 1545ba0d023fa..c702b8e744c89 100644 --- a/library/std/src/sys/windows/fs.rs +++ b/library/std/src/sys/windows/fs.rs @@ -11,7 +11,7 @@ use crate::slice; use crate::sync::Arc; use crate::sys::handle::Handle; use crate::sys::time::SystemTime; -use crate::sys::{c, cvt, AlignedAs}; +use crate::sys::{c, cvt, Align8}; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::thread; @@ -47,9 +47,6 @@ pub struct ReadDir { first: Option, } -type AlignedReparseBuf = - AlignedAs; - struct FindNextFileHandle(c::HANDLE); unsafe impl Send for FindNextFileHandle {} @@ -329,7 +326,7 @@ impl File { cvt(c::GetFileInformationByHandle(self.handle.as_raw_handle(), &mut info))?; let mut reparse_tag = 0; if info.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 { - let mut b = AlignedReparseBuf::new([0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]); + let mut b = Align8([0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]); if let Ok((_, buf)) = self.reparse_point(&mut b) { reparse_tag = (*buf).ReparseTag; } @@ -392,7 +389,7 @@ impl File { attr.file_size = info.AllocationSize as u64; attr.number_of_links = Some(info.NumberOfLinks); if attr.file_type().is_reparse_point() { - let mut b = AlignedReparseBuf::new([0; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]); + let mut b = Align8([0; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]); if let Ok((_, buf)) = self.reparse_point(&mut b) { attr.reparse_tag = (*buf).ReparseTag; } @@ -466,28 +463,32 @@ impl File { // avoid narrowing provenance to the actual `REPARSE_DATA_BUFFER`. fn reparse_point( &self, - space: &mut AlignedReparseBuf, + space: &mut Align8<[u8]>, ) -> io::Result<(c::DWORD, *const c::REPARSE_DATA_BUFFER)> { unsafe { let mut bytes = 0; cvt({ + // Grab this in advance to avoid it invalidating the pointer + // we get from `space.0.as_mut_ptr()`. + let len = space.0.len(); c::DeviceIoControl( self.handle.as_raw_handle(), c::FSCTL_GET_REPARSE_POINT, ptr::null_mut(), 0, - space.value.as_mut_ptr() as *mut _, - space.value.len() as c::DWORD, + space.0.as_mut_ptr().cast(), + len as c::DWORD, &mut bytes, ptr::null_mut(), ) })?; - Ok((bytes, space.value.as_ptr().cast::())) + const _: () = assert!(core::mem::align_of::() <= 8); + Ok((bytes, space.0.as_ptr().cast::())) } } fn readlink(&self) -> io::Result { - let mut space = AlignedReparseBuf::new([0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]); + let mut space = Align8([0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]); let (_bytes, buf) = self.reparse_point(&mut space)?; unsafe { let (path_buffer, subst_off, subst_len, relative) = match (*buf).ReparseTag { @@ -1345,8 +1346,8 @@ fn symlink_junction_inner(original: &Path, junction: &Path) -> io::Result<()> { let h = f.as_inner().as_raw_handle(); unsafe { - let mut data = AlignedReparseBuf::new([0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]); - let data_ptr = data.value.as_mut_ptr(); + let mut data = Align8([0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]); + let data_ptr = data.0.as_mut_ptr(); let db = data_ptr.cast::(); let buf = ptr::addr_of_mut!((*db).ReparseTarget).cast::(); let mut i = 0; diff --git a/library/std/src/sys/windows/mod.rs b/library/std/src/sys/windows/mod.rs index fbbc5cb79fb8b..340cae4066bf4 100644 --- a/library/std/src/sys/windows/mod.rs +++ b/library/std/src/sys/windows/mod.rs @@ -330,25 +330,10 @@ pub fn abort_internal() -> ! { crate::intrinsics::abort(); } -/// Used for some win32 buffers which are stack allocated, for example: -/// `AlignedAs` -#[repr(C)] +/// Align the inner value to 8 bytes. +/// +/// This is enough for almost all of the buffers we're likely to work with in +/// the Windows APIs we use. +#[repr(C, align(8))] #[derive(Copy, Clone)] -pub struct AlignedAs { - /// Use `[Aligner; 0]` as a sort of `PhantomAlignNextField`. This - /// is a bit of a hack, and could break (in a way that's caught by tests) if - /// #81996 is fixed. - aligner: [Aligner; 0], - /// The aligned value. Public rather than exposed via accessors so that if - /// needed it can be used with `addr_of` and such (also, this is less code). - pub value: Alignee, -} - -impl AlignedAs { - // This is frequently used with large stack buffers, so force-inline to - // try and avoid using 2x as much stack space in debug builds. - #[inline(always)] - pub const fn new(value: Alignee) -> Self { - Self { aligner: [], value } - } -} +pub(crate) struct Align8(pub T); diff --git a/library/std/src/sys/windows/os/tests.rs b/library/std/src/sys/windows/os/tests.rs index 532be0cf083ba..458d6e11c2098 100644 --- a/library/std/src/sys/windows/os/tests.rs +++ b/library/std/src/sys/windows/os/tests.rs @@ -11,21 +11,3 @@ fn ntstatus_error() { .contains("FormatMessageW() returned error") ); } - -#[test] -fn smoketest_aligned_as() { - use crate::{ - mem::{align_of, size_of}, - ptr::addr_of, - sys::{c, AlignedAs}, - }; - type AlignedReparseBuf = - AlignedAs; - assert!(size_of::() >= c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE); - assert_eq!(align_of::(), align_of::()); - let a = AlignedReparseBuf::new([0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]); - // Quick and dirty offsetof check. - assert_eq!(addr_of!(a).cast::(), addr_of!(a.value).cast::()); - // Smoke check that it's actually aligned. - assert!(addr_of!(a.value).is_aligned_to(align_of::())); -} From 31b939b3152ea70b2a84992905f48e125d85ca18 Mon Sep 17 00:00:00 2001 From: Xiretza Date: Tue, 23 Aug 2022 07:54:06 +0200 Subject: [PATCH 22/24] Rework SessionSubdiagnostic derive to support multipart_suggestion --- .../src/diagnostics/subdiagnostic.rs | 608 +++++++++++------- compiler/rustc_macros/src/lib.rs | 5 + .../subdiagnostic-derive.rs | 119 +++- .../subdiagnostic-derive.stderr | 207 ++++-- 4 files changed, 672 insertions(+), 267 deletions(-) diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index f66565af4c1d8..8b40e295bd8a7 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -12,7 +12,7 @@ use quote::{format_ident, quote}; use std::collections::HashMap; use std::fmt; use std::str::FromStr; -use syn::{parse_quote, spanned::Spanned, Meta, MetaList, MetaNameValue, NestedMeta, Path}; +use syn::{spanned::Spanned, Attribute, Meta, MetaList, MetaNameValue, NestedMeta, Path}; use synstructure::{BindingInfo, Structure, VariantInfo}; /// Which kind of suggestion is being created? @@ -62,7 +62,7 @@ impl SubdiagnosticSuggestionKind { } /// Which kind of subdiagnostic is being created from a variant? -#[derive(Clone, Copy)] +#[derive(Clone)] enum SubdiagnosticKind { /// `#[label(...)]` Label, @@ -73,29 +73,9 @@ enum SubdiagnosticKind { /// `#[warning(...)]` Warn, /// `#[suggestion{,_short,_hidden,_verbose}]` - Suggestion(SubdiagnosticSuggestionKind), -} - -impl FromStr for SubdiagnosticKind { - type Err = (); - - fn from_str(s: &str) -> Result { - match s { - "label" => Ok(SubdiagnosticKind::Label), - "note" => Ok(SubdiagnosticKind::Note), - "help" => Ok(SubdiagnosticKind::Help), - "warning" => Ok(SubdiagnosticKind::Warn), - _ => { - if let Some(suggestion_kind) = - s.strip_prefix("suggestion").and_then(|s| s.parse().ok()) - { - return Ok(SubdiagnosticKind::Suggestion(suggestion_kind)); - }; - - Err(()) - } - } - } + Suggestion { suggestion_kind: SubdiagnosticSuggestionKind, code: TokenStream }, + /// `#[multipart_suggestion{,_short,_hidden,_verbose}]` + MultipartSuggestion { suggestion_kind: SubdiagnosticSuggestionKind }, } impl quote::IdentFragment for SubdiagnosticKind { @@ -105,7 +85,10 @@ impl quote::IdentFragment for SubdiagnosticKind { SubdiagnosticKind::Note => write!(f, "note"), SubdiagnosticKind::Help => write!(f, "help"), SubdiagnosticKind::Warn => write!(f, "warn"), - SubdiagnosticKind::Suggestion(..) => write!(f, "suggestion_with_style"), + SubdiagnosticKind::Suggestion { .. } => write!(f, "suggestion_with_style"), + SubdiagnosticKind::MultipartSuggestion { .. } => { + write!(f, "multipart_suggestion_with_style") + } } } @@ -168,11 +151,9 @@ impl<'a> SessionSubdiagnosticDerive<'a> { variant, span, fields: fields_map, - kind: None, - slug: None, - code: None, span_field: None, applicability: None, + has_suggestion_parts: false, }; builder.into_tokens().unwrap_or_else(|v| v.to_compile_error()) }); @@ -213,21 +194,15 @@ struct SessionSubdiagnosticDeriveBuilder<'a> { /// derive builder. fields: HashMap, - /// Subdiagnostic kind of the type/variant. - kind: Option<(SubdiagnosticKind, proc_macro::Span)>, - - /// Slug of the subdiagnostic - corresponds to the Fluent identifier for the message - from the - /// `#[kind(slug)]` attribute on the type or variant. - slug: Option<(Path, proc_macro::Span)>, - /// If a suggestion, the code to suggest as a replacement - from the `#[kind(code = "...")]` - /// attribute on the type or variant. - code: Option<(TokenStream, proc_macro::Span)>, - /// Identifier for the binding to the `#[primary_span]` field. span_field: Option<(proc_macro2::Ident, proc_macro::Span)>, /// If a suggestion, the identifier for the binding to the `#[applicability]` field or a /// `rustc_errors::Applicability::*` variant directly. applicability: Option<(TokenStream, proc_macro::Span)>, + + /// Set to true when a `#[suggestion_part]` field is encountered, used to generate an error + /// during finalization if still `false`. + has_suggestion_parts: bool, } impl<'a> HasFieldMap for SessionSubdiagnosticDeriveBuilder<'a> { @@ -237,7 +212,11 @@ impl<'a> HasFieldMap for SessionSubdiagnosticDeriveBuilder<'a> { } impl<'a> SessionSubdiagnosticDeriveBuilder<'a> { - fn identify_kind(&mut self) -> Result<(), DiagnosticDeriveError> { + fn identify_kind( + &mut self, + ) -> Result, DiagnosticDeriveError> { + let mut kind_slug = None; + for attr in self.variant.ast().attrs { let span = attr.span().unwrap(); @@ -245,116 +224,121 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> { let name = name.as_str(); let meta = attr.parse_meta()?; - let kind = match meta { - Meta::List(MetaList { ref nested, .. }) => { - let mut nested_iter = nested.into_iter(); - if let Some(nested_attr) = nested_iter.next() { - match nested_attr { - NestedMeta::Meta(Meta::Path(path)) => { - self.slug.set_once((path.clone(), span)); - } - NestedMeta::Meta(meta @ Meta::NameValue(_)) - if matches!( - meta.path().segments.last().unwrap().ident.to_string().as_str(), - "code" | "applicability" - ) => - { - // don't error for valid follow-up attributes - } - nested_attr => { - throw_invalid_nested_attr!(attr, &nested_attr, |diag| { - diag.help( - "first argument of the attribute should be the diagnostic \ - slug", - ) - }) - } - }; - } + let Meta::List(MetaList { ref nested, .. }) = meta else { + throw_invalid_attr!(attr, &meta); + }; - for nested_attr in nested_iter { - let meta = match nested_attr { - NestedMeta::Meta(ref meta) => meta, - _ => throw_invalid_nested_attr!(attr, &nested_attr), - }; - - let span = meta.span().unwrap(); - let nested_name = meta.path().segments.last().unwrap().ident.to_string(); - let nested_name = nested_name.as_str(); - - match meta { - Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(s), .. }) => { - match nested_name { - "code" => { - let formatted_str = self.build_format(&s.value(), s.span()); - self.code.set_once((formatted_str, span)); - } - "applicability" => { - let value = match Applicability::from_str(&s.value()) { - Ok(v) => v, - Err(()) => { - span_err(span, "invalid applicability").emit(); - Applicability::Unspecified - } - }; - self.applicability.set_once((quote! { #value }, span)); - } - _ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| { - diag.help( - "only `code` and `applicability` are valid nested \ - attributes", - ) - }), - } - } - _ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| { - if matches!(meta, Meta::Path(_)) { - diag.help( - "a diagnostic slug must be the first argument to the \ - attribute", - ) - } else { - diag - } - }), - } + let mut kind = match name { + "label" => SubdiagnosticKind::Label, + "note" => SubdiagnosticKind::Note, + "help" => SubdiagnosticKind::Help, + "warning" => SubdiagnosticKind::Warn, + _ => { + if let Some(suggestion_kind) = + name.strip_prefix("suggestion").and_then(|s| s.parse().ok()) + { + SubdiagnosticKind::Suggestion { suggestion_kind, code: TokenStream::new() } + } else if let Some(suggestion_kind) = + name.strip_prefix("multipart_suggestion").and_then(|s| s.parse().ok()) + { + SubdiagnosticKind::MultipartSuggestion { suggestion_kind } + } else { + throw_invalid_attr!(attr, &meta); } - - let Ok(kind) = SubdiagnosticKind::from_str(name) else { - throw_invalid_attr!(attr, &meta) - }; - - kind } - _ => throw_invalid_attr!(attr, &meta), }; - if matches!( - kind, - SubdiagnosticKind::Label | SubdiagnosticKind::Help | SubdiagnosticKind::Note - ) && self.code.is_some() - { - throw_span_err!( - span, - &format!("`code` is not a valid nested attribute of a `{}` attribute", name) - ); + let mut slug = None; + let mut code = None; + + let mut nested_iter = nested.into_iter(); + if let Some(nested_attr) = nested_iter.next() { + match nested_attr { + NestedMeta::Meta(Meta::Path(path)) => { + slug.set_once((path.clone(), span)); + } + NestedMeta::Meta(meta @ Meta::NameValue(_)) + if matches!( + meta.path().segments.last().unwrap().ident.to_string().as_str(), + "code" | "applicability" + ) => + { + // Don't error for valid follow-up attributes. + } + nested_attr => { + throw_invalid_nested_attr!(attr, &nested_attr, |diag| { + diag.help( + "first argument of the attribute should be the diagnostic \ + slug", + ) + }) + } + }; } - if matches!( - kind, - SubdiagnosticKind::Label | SubdiagnosticKind::Help | SubdiagnosticKind::Note - ) && self.applicability.is_some() - { - throw_span_err!( - span, - &format!( - "`applicability` is not a valid nested attribute of a `{}` attribute", - name - ) - ); + for nested_attr in nested_iter { + let meta = match nested_attr { + NestedMeta::Meta(ref meta) => meta, + _ => throw_invalid_nested_attr!(attr, &nested_attr), + }; + + let span = meta.span().unwrap(); + let nested_name = meta.path().segments.last().unwrap().ident.to_string(); + let nested_name = nested_name.as_str(); + + let value = match meta { + Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(value), .. }) => value, + Meta::Path(_) => throw_invalid_nested_attr!(attr, &nested_attr, |diag| { + diag.help("a diagnostic slug must be the first argument to the attribute") + }), + _ => throw_invalid_nested_attr!(attr, &nested_attr), + }; + + match nested_name { + "code" => { + if matches!(kind, SubdiagnosticKind::Suggestion { .. }) { + let formatted_str = self.build_format(&value.value(), value.span()); + code.set_once((formatted_str, span)); + } else { + span_err( + span, + &format!( + "`code` is not a valid nested attribute of a `{}` attribute", + name + ), + ) + .emit(); + } + } + "applicability" => { + if matches!( + kind, + SubdiagnosticKind::Suggestion { .. } + | SubdiagnosticKind::MultipartSuggestion { .. } + ) { + let value = + Applicability::from_str(&value.value()).unwrap_or_else(|()| { + span_err(span, "invalid applicability").emit(); + Applicability::Unspecified + }); + self.applicability.set_once((quote! { #value }, span)); + } else { + span_err( + span, + &format!( + "`applicability` is not a valid nested attribute of a `{}` attribute", + name + ) + ).emit(); + } + } + _ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| { + diag.help("only `code` and `applicability` are valid nested attributes") + }), + } } - if self.slug.is_none() { + let Some((slug, _)) = slug else { throw_span_err!( span, &format!( @@ -362,112 +346,275 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> { name ) ); + }; + + match kind { + SubdiagnosticKind::Suggestion { code: ref mut code_field, .. } => { + let Some((code, _)) = code else { + throw_span_err!(span, "suggestion without `code = \"...\"`"); + }; + *code_field = code; + } + SubdiagnosticKind::Label + | SubdiagnosticKind::Note + | SubdiagnosticKind::Help + | SubdiagnosticKind::Warn + | SubdiagnosticKind::MultipartSuggestion { .. } => {} } - self.kind.set_once((kind, span)); + kind_slug.set_once(((kind, slug), span)) } - Ok(()) + Ok(kind_slug.map(|(kind_slug, _)| kind_slug)) } - fn generate_field_code( + /// Generates the code for a field with no attributes. + fn generate_field_set_arg(&mut self, binding: &BindingInfo<'_>) -> TokenStream { + let ast = binding.ast(); + assert_eq!(ast.attrs.len(), 0, "field with attribute used as diagnostic arg"); + + let diag = &self.diag; + let ident = ast.ident.as_ref().unwrap(); + quote! { + #diag.set_arg( + stringify!(#ident), + #binding + ); + } + } + + /// Generates the necessary code for all attributes on a field. + fn generate_field_attr_code( &mut self, binding: &BindingInfo<'_>, - is_suggestion: bool, - ) -> Result { + kind: &SubdiagnosticKind, + ) -> TokenStream { let ast = binding.ast(); + assert!(ast.attrs.len() > 0, "field without attributes generating attr code"); + // Abstract over `Vec` and `Option` fields using `FieldInnerTy`, which will + // apply the generated code on each element in the `Vec` or `Option`. let inner_ty = FieldInnerTy::from_type(&ast.ty); - let info = FieldInfo { - binding: binding, - ty: inner_ty.inner_type().unwrap_or(&ast.ty), - span: &ast.span(), - }; + ast.attrs + .iter() + .map(|attr| { + let info = FieldInfo { + binding, + ty: inner_ty.inner_type().unwrap_or(&ast.ty), + span: &ast.span(), + }; - for attr in &ast.attrs { - let name = attr.path.segments.last().unwrap().ident.to_string(); - let name = name.as_str(); - let span = attr.span().unwrap(); + let generated = self + .generate_field_code_inner(kind, attr, info) + .unwrap_or_else(|v| v.to_compile_error()); - let meta = attr.parse_meta()?; - match meta { - Meta::Path(_) => match name { - "primary_span" => { - report_error_if_not_applied_to_span(attr, &info)?; - self.span_field.set_once((binding.binding.clone(), span)); - return Ok(quote! {}); - } - "applicability" if is_suggestion => { - report_error_if_not_applied_to_applicability(attr, &info)?; - let binding = binding.binding.clone(); - self.applicability.set_once((quote! { #binding }, span)); - return Ok(quote! {}); - } - "applicability" => { - span_err(span, "`#[applicability]` is only valid on suggestions").emit(); - return Ok(quote! {}); - } - "skip_arg" => { - return Ok(quote! {}); - } - _ => throw_invalid_attr!(attr, &meta, |diag| { + inner_ty.with(binding, generated) + }) + .collect() + } + + fn generate_field_code_inner( + &mut self, + kind: &SubdiagnosticKind, + attr: &Attribute, + info: FieldInfo<'_>, + ) -> Result { + let meta = attr.parse_meta()?; + match meta { + Meta::Path(path) => self.generate_field_code_inner_path(kind, attr, info, path), + Meta::List(list @ MetaList { .. }) => { + self.generate_field_code_inner_list(kind, attr, info, list) + } + _ => throw_invalid_attr!(attr, &meta), + } + } + + /// Generates the code for a `[Meta::Path]`-like attribute on a field (e.g. `#[primary_span]`). + fn generate_field_code_inner_path( + &mut self, + kind: &SubdiagnosticKind, + attr: &Attribute, + info: FieldInfo<'_>, + path: Path, + ) -> Result { + let span = attr.span().unwrap(); + let ident = &path.segments.last().unwrap().ident; + let name = ident.to_string(); + let name = name.as_str(); + + match name { + "skip_arg" => Ok(quote! {}), + "primary_span" => { + if matches!(kind, SubdiagnosticKind::MultipartSuggestion { .. }) { + throw_invalid_attr!(attr, &Meta::Path(path), |diag| { diag.help( - "only `primary_span`, `applicability` and `skip_arg` are valid field \ - attributes", + "multipart suggestions use one or more `#[suggestion_part]`s rather \ + than one `#[primary_span]`", ) - }), - }, - _ => throw_invalid_attr!(attr, &meta), + }) + } + + report_error_if_not_applied_to_span(attr, &info)?; + + let binding = info.binding.binding.clone(); + self.span_field.set_once((binding, span)); + + Ok(quote! {}) + } + "suggestion_part" => { + self.has_suggestion_parts = true; + + match kind { + SubdiagnosticKind::MultipartSuggestion { .. } => { + span_err( + span, + "`#[suggestion_part(...)]` attribute without `code = \"...\"`", + ) + .emit(); + Ok(quote! {}) + } + SubdiagnosticKind::Label + | SubdiagnosticKind::Note + | SubdiagnosticKind::Help + | SubdiagnosticKind::Warn + | SubdiagnosticKind::Suggestion { .. } => { + throw_invalid_attr!(attr, &Meta::Path(path), |diag| { + diag.help( + "`#[suggestion_part(...)]` is only valid in multipart suggestions, use `#[primary_span]` instead", + ) + }); + } + } + } + "applicability" => { + if let SubdiagnosticKind::Suggestion { .. } + | SubdiagnosticKind::MultipartSuggestion { .. } = kind + { + report_error_if_not_applied_to_applicability(attr, &info)?; + + let binding = info.binding.binding.clone(); + self.applicability.set_once((quote! { #binding }, span)); + } else { + span_err(span, "`#[applicability]` is only valid on suggestions").emit(); + } + + Ok(quote! {}) } + _ => throw_invalid_attr!(attr, &Meta::Path(path), |diag| { + let span_attr = if let SubdiagnosticKind::MultipartSuggestion { .. } = kind { + "suggestion_part" + } else { + "primary_span" + }; + diag.help(format!( + "only `{span_attr}`, `applicability` and `skip_arg` are valid field attributes", + )) + }), } + } - let ident = ast.ident.as_ref().unwrap(); + /// Generates the code for a `[Meta::List]`-like attribute on a field (e.g. + /// `#[suggestion_part(code = "...")]`). + fn generate_field_code_inner_list( + &mut self, + kind: &SubdiagnosticKind, + attr: &Attribute, + info: FieldInfo<'_>, + list: MetaList, + ) -> Result { + let span = attr.span().unwrap(); + let ident = &list.path.segments.last().unwrap().ident; + let name = ident.to_string(); + let name = name.as_str(); + + match name { + "suggestion_part" => { + if !matches!(kind, SubdiagnosticKind::MultipartSuggestion { .. }) { + throw_invalid_attr!(attr, &Meta::List(list), |diag| { + diag.help( + "`#[suggestion_part(...)]` is only valid in multipart suggestions", + ) + }) + } - let diag = &self.diag; - let generated = quote! { - #diag.set_arg( - stringify!(#ident), - #binding - ); - }; + self.has_suggestion_parts = true; + + report_error_if_not_applied_to_span(attr, &info)?; + + let mut code = None; + for nested_attr in list.nested.iter() { + let NestedMeta::Meta(ref meta) = nested_attr else { + throw_invalid_nested_attr!(attr, &nested_attr); + }; + + let span = meta.span().unwrap(); + let nested_name = meta.path().segments.last().unwrap().ident.to_string(); + let nested_name = nested_name.as_str(); + + let Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(value), .. }) = meta else { + throw_invalid_nested_attr!(attr, &nested_attr); + }; + + match nested_name { + "code" => { + let formatted_str = self.build_format(&value.value(), value.span()); + code.set_once((formatted_str, span)); + } + _ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| { + diag.help("`code` is the only valid nested attribute") + }), + } + } - Ok(inner_ty.with(binding, generated)) + let Some((code, _)) = code else { + span_err(span, "`#[suggestion_part(...)]` attribute without `code = \"...\"`") + .emit(); + return Ok(quote! {}); + }; + let binding = info.binding; + + Ok(quote! { suggestions.push((#binding, #code)); }) + } + _ => throw_invalid_attr!(attr, &Meta::List(list), |diag| { + let span_attr = if let SubdiagnosticKind::MultipartSuggestion { .. } = kind { + "suggestion_part" + } else { + "primary_span" + }; + diag.help(format!( + "only `{span_attr}`, `applicability` and `skip_arg` are valid field attributes", + )) + }), + } } - fn into_tokens(&mut self) -> Result { - self.identify_kind()?; - let Some(kind) = self.kind.map(|(kind, _)| kind) else { + pub fn into_tokens(&mut self) -> Result { + let Some((kind, slug)) = self.identify_kind()? else { throw_span_err!( self.variant.ast().ident.span().unwrap(), "subdiagnostic kind not specified" ); }; - let is_suggestion = matches!(kind, SubdiagnosticKind::Suggestion(_)); - - let mut args = TokenStream::new(); - for binding in self.variant.bindings() { - let arg = self - .generate_field_code(binding, is_suggestion) - .unwrap_or_else(|v| v.to_compile_error()); - args.extend(arg); - } - - // Missing slug errors will already have been reported. - let slug = self - .slug - .as_ref() - .map(|(slug, _)| slug.clone()) - .unwrap_or_else(|| parse_quote! { you::need::to::specify::a::slug }); - let code = match self.code.as_ref() { - Some((code, _)) => Some(quote! { #code }), - None if is_suggestion => { - span_err(self.span, "suggestion without `code = \"...\"`").emit(); - Some(quote! { /* macro error */ "..." }) + let init = match &kind { + SubdiagnosticKind::Label + | SubdiagnosticKind::Note + | SubdiagnosticKind::Help + | SubdiagnosticKind::Warn + | SubdiagnosticKind::Suggestion { .. } => quote! {}, + SubdiagnosticKind::MultipartSuggestion { .. } => { + quote! { let mut suggestions = Vec::new(); } } - None => None, }; + let attr_args: TokenStream = self + .variant + .bindings() + .iter() + .filter(|binding| !binding.ast().attrs.is_empty()) + .map(|binding| self.generate_field_attr_code(binding, &kind)) + .collect(); + let span_field = self.span_field.as_ref().map(|(span, _)| span); let applicability = self.applicability.take().map_or_else( || quote! { rustc_errors::Applicability::Unspecified }, @@ -478,9 +625,9 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> { let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind); let message = quote! { rustc_errors::fluent::#slug }; let call = match kind { - SubdiagnosticKind::Suggestion(style) => { + SubdiagnosticKind::Suggestion { suggestion_kind, code } => { if let Some(span) = span_field { - let style = style.to_suggestion_style(); + let style = suggestion_kind.to_suggestion_style(); quote! { #diag.#name(#span, #message, #code, #applicability, #style); } } else { @@ -488,6 +635,19 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> { quote! { unreachable!(); } } } + SubdiagnosticKind::MultipartSuggestion { suggestion_kind } => { + if !self.has_suggestion_parts { + span_err( + self.span, + "multipart suggestion without any `#[suggestion_part(...)]` fields", + ) + .emit(); + } + + let style = suggestion_kind.to_suggestion_style(); + + quote! { #diag.#name(#message, suggestions, #applicability, #style); } + } SubdiagnosticKind::Label => { if let Some(span) = span_field { quote! { #diag.#name(#span, #message); } @@ -505,9 +665,19 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> { } }; + let plain_args: TokenStream = self + .variant + .bindings() + .iter() + .filter(|binding| binding.ast().attrs.is_empty()) + .map(|binding| self.generate_field_set_arg(binding)) + .collect(); + Ok(quote! { + #init + #attr_args #call - #args + #plain_args }) } } diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs index 8faac8ef36a53..20ee5dfc72798 100644 --- a/compiler/rustc_macros/src/lib.rs +++ b/compiler/rustc_macros/src/lib.rs @@ -171,8 +171,13 @@ decl_derive!( suggestion_short, suggestion_hidden, suggestion_verbose, + multipart_suggestion, + multipart_suggestion_short, + multipart_suggestion_hidden, + multipart_suggestion_verbose, // field attributes skip_arg, primary_span, + suggestion_part, applicability)] => diagnostics::session_subdiagnostic_derive ); diff --git a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs index 0871ad6b22b39..89eaec78c6f11 100644 --- a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs +++ b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs @@ -310,10 +310,8 @@ union AC { #[derive(SessionSubdiagnostic)] #[label(parser::add_paren)] //~^ NOTE previously specified here -//~^^ NOTE previously specified here #[label(parser::add_paren)] //~^ ERROR specified multiple times -//~^^ ERROR specified multiple times struct AD { #[primary_span] span: Span, @@ -516,3 +514,120 @@ struct AZ { #[primary_span] span: Span, } + +#[derive(SessionSubdiagnostic)] +#[suggestion(parser::add_paren, code = "...")] +//~^ ERROR suggestion without `#[primary_span]` field +struct BA { + #[suggestion_part] + //~^ ERROR `#[suggestion_part]` is not a valid attribute + span: Span, + #[suggestion_part(code = "...")] + //~^ ERROR `#[suggestion_part(...)]` is not a valid attribute + span2: Span, + #[applicability] + applicability: Applicability, + var: String, +} + +#[derive(SessionSubdiagnostic)] +#[multipart_suggestion(parser::add_paren, code = "...", applicability = "machine-applicable")] +//~^ ERROR multipart suggestion without any `#[suggestion_part(...)]` fields +//~| ERROR `code` is not a valid nested attribute of a `multipart_suggestion` attribute +struct BBa { + var: String, +} + +#[derive(SessionSubdiagnostic)] +#[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")] +struct BBb { + #[suggestion_part] + //~^ ERROR `#[suggestion_part(...)]` attribute without `code = "..."` + span1: Span, +} + +#[derive(SessionSubdiagnostic)] +#[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")] +struct BBc { + #[suggestion_part()] + //~^ ERROR `#[suggestion_part(...)]` attribute without `code = "..."` + span1: Span, +} + +#[derive(SessionSubdiagnostic)] +#[multipart_suggestion(parser::add_paren)] +//~^ ERROR multipart suggestion without any `#[suggestion_part(...)]` fields +struct BC { + #[primary_span] + //~^ ERROR `#[primary_span]` is not a valid attribute + span: Span, +} + +#[derive(SessionSubdiagnostic)] +#[multipart_suggestion(parser::add_paren)] +struct BD { + #[suggestion_part] + //~^ ERROR `#[suggestion_part(...)]` attribute without `code = "..."` + span1: Span, + #[suggestion_part()] + //~^ ERROR `#[suggestion_part(...)]` attribute without `code = "..."` + span2: Span, + #[suggestion_part(foo = "bar")] + //~^ ERROR `#[suggestion_part(foo = ...)]` is not a valid attribute + span4: Span, + #[suggestion_part(code = "...")] + //~^ ERROR the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` + s1: String, + #[suggestion_part()] + //~^ ERROR the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` + s2: String, +} + +#[derive(SessionSubdiagnostic)] +#[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")] +struct BE { + #[suggestion_part(code = "...", code = ",,,")] + //~^ ERROR specified multiple times + //~| NOTE previously specified here + span: Span, +} + +#[derive(SessionSubdiagnostic)] +#[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")] +struct BF { + #[suggestion_part(code = "(")] + first: Span, + #[suggestion_part(code = ")")] + second: Span, +} + +#[derive(SessionSubdiagnostic)] +#[multipart_suggestion(parser::add_paren)] +struct BG { + #[applicability] + appl: Applicability, + #[suggestion_part(code = "(")] + first: Span, + #[suggestion_part(code = ")")] + second: Span, +} + +#[derive(SessionSubdiagnostic)] +#[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")] +//~^ NOTE previously specified here +struct BH { + #[applicability] + //~^ ERROR specified multiple times + appl: Applicability, + #[suggestion_part(code = "(")] + first: Span, + #[suggestion_part(code = ")")] + second: Span, +} + +#[derive(SessionSubdiagnostic)] +#[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")] +struct BI { + #[suggestion_part(code = "")] + spans: Vec, +} diff --git a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr index 68d33323db035..75a34f44bbe72 100644 --- a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr +++ b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr @@ -65,16 +65,16 @@ LL | #[label()] | ^^^^^^^^^^ error: `code` is not a valid nested attribute of a `label` attribute - --> $DIR/subdiagnostic-derive.rs:137:1 + --> $DIR/subdiagnostic-derive.rs:137:28 | LL | #[label(parser::add_paren, code = "...")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^ error: `applicability` is not a valid nested attribute of a `label` attribute - --> $DIR/subdiagnostic-derive.rs:146:1 + --> $DIR/subdiagnostic-derive.rs:146:28 | LL | #[label(parser::add_paren, applicability = "machine-applicable")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unsupported type attribute for subdiagnostic enum --> $DIR/subdiagnostic-derive.rs:155:1 @@ -100,13 +100,11 @@ error: `#[bar = ...]` is not a valid attribute LL | #[bar = 4] | ^^^^^^^^^^ -error: `#[bar("...")]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:205:11 +error: `#[bar(...)]` is not a valid attribute + --> $DIR/subdiagnostic-derive.rs:205:5 | LL | #[bar("...")] - | ^^^^^ - | - = help: first argument of the attribute should be the diagnostic slug + | ^^^^^^^^^^^^^ error: diagnostic slug must be first argument of a `#[label(...)]` attribute --> $DIR/subdiagnostic-derive.rs:217:5 @@ -163,6 +161,8 @@ error: `#[bar(...)]` is not a valid attribute | LL | #[bar("...")] | ^^^^^^^^^^^^^ + | + = help: only `primary_span`, `applicability` and `skip_arg` are valid field attributes error: unexpected unsupported untagged union --> $DIR/subdiagnostic-derive.rs:304:1 @@ -175,19 +175,7 @@ LL | | } | |_^ error: specified multiple times - --> $DIR/subdiagnostic-derive.rs:314:1 - | -LL | #[label(parser::add_paren)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: previously specified here - --> $DIR/subdiagnostic-derive.rs:311:1 - | -LL | #[label(parser::add_paren)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: specified multiple times - --> $DIR/subdiagnostic-derive.rs:314:1 + --> $DIR/subdiagnostic-derive.rs:313:1 | LL | #[label(parser::add_paren)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -199,7 +187,7 @@ LL | #[label(parser::add_paren)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `#[label(parser::add_paren)]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:323:28 + --> $DIR/subdiagnostic-derive.rs:321:28 | LL | #[label(parser::add_paren, parser::add_paren)] | ^^^^^^^^^^^^^^^^^ @@ -207,73 +195,67 @@ LL | #[label(parser::add_paren, parser::add_paren)] = help: a diagnostic slug must be the first argument to the attribute error: specified multiple times - --> $DIR/subdiagnostic-derive.rs:336:5 + --> $DIR/subdiagnostic-derive.rs:334:5 | LL | #[primary_span] | ^^^^^^^^^^^^^^^ | note: previously specified here - --> $DIR/subdiagnostic-derive.rs:333:5 + --> $DIR/subdiagnostic-derive.rs:331:5 | LL | #[primary_span] | ^^^^^^^^^^^^^^^ error: subdiagnostic kind not specified - --> $DIR/subdiagnostic-derive.rs:342:8 + --> $DIR/subdiagnostic-derive.rs:340:8 | LL | struct AG { | ^^ error: specified multiple times - --> $DIR/subdiagnostic-derive.rs:379:47 + --> $DIR/subdiagnostic-derive.rs:377:47 | LL | #[suggestion(parser::add_paren, code = "...", code = "...")] | ^^^^^^^^^^^^ | note: previously specified here - --> $DIR/subdiagnostic-derive.rs:379:33 + --> $DIR/subdiagnostic-derive.rs:377:33 | LL | #[suggestion(parser::add_paren, code = "...", code = "...")] | ^^^^^^^^^^^^ error: specified multiple times - --> $DIR/subdiagnostic-derive.rs:397:5 + --> $DIR/subdiagnostic-derive.rs:395:5 | LL | #[applicability] | ^^^^^^^^^^^^^^^^ | note: previously specified here - --> $DIR/subdiagnostic-derive.rs:394:5 + --> $DIR/subdiagnostic-derive.rs:392:5 | LL | #[applicability] | ^^^^^^^^^^^^^^^^ error: the `#[applicability]` attribute can only be applied to fields of type `Applicability` - --> $DIR/subdiagnostic-derive.rs:407:5 + --> $DIR/subdiagnostic-derive.rs:405:5 | LL | #[applicability] | ^^^^^^^^^^^^^^^^ error: suggestion without `code = "..."` - --> $DIR/subdiagnostic-derive.rs:420:1 + --> $DIR/subdiagnostic-derive.rs:418:1 | -LL | / #[suggestion(parser::add_paren)] -LL | | -LL | | struct AN { -LL | | #[primary_span] -... | -LL | | applicability: Applicability, -LL | | } - | |_^ +LL | #[suggestion(parser::add_paren)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: invalid applicability - --> $DIR/subdiagnostic-derive.rs:430:46 + --> $DIR/subdiagnostic-derive.rs:428:46 | LL | #[suggestion(parser::add_paren, code ="...", applicability = "foo")] | ^^^^^^^^^^^^^^^^^^^^^ error: suggestion without `#[primary_span]` field - --> $DIR/subdiagnostic-derive.rs:448:1 + --> $DIR/subdiagnostic-derive.rs:446:1 | LL | / #[suggestion(parser::add_paren, code = "...")] LL | | @@ -283,23 +265,156 @@ LL | | } | |_^ error: unsupported type attribute for subdiagnostic enum - --> $DIR/subdiagnostic-derive.rs:462:1 + --> $DIR/subdiagnostic-derive.rs:460:1 | LL | #[label] | ^^^^^^^^ error: `var` doesn't refer to a field on this type - --> $DIR/subdiagnostic-derive.rs:482:39 + --> $DIR/subdiagnostic-derive.rs:480:39 | LL | #[suggestion(parser::add_paren, code ="{var}", applicability = "machine-applicable")] | ^^^^^^^ error: `var` doesn't refer to a field on this type - --> $DIR/subdiagnostic-derive.rs:501:43 + --> $DIR/subdiagnostic-derive.rs:499:43 | LL | #[suggestion(parser::add_paren, code ="{var}", applicability = "machine-applicable")] | ^^^^^^^ +error: `#[suggestion_part]` is not a valid attribute + --> $DIR/subdiagnostic-derive.rs:522:5 + | +LL | #[suggestion_part] + | ^^^^^^^^^^^^^^^^^^ + | + = help: `#[suggestion_part(...)]` is only valid in multipart suggestions, use `#[primary_span]` instead + +error: `#[suggestion_part(...)]` is not a valid attribute + --> $DIR/subdiagnostic-derive.rs:525:5 + | +LL | #[suggestion_part(code = "...")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[suggestion_part(...)]` is only valid in multipart suggestions + +error: suggestion without `#[primary_span]` field + --> $DIR/subdiagnostic-derive.rs:519:1 + | +LL | / #[suggestion(parser::add_paren, code = "...")] +LL | | +LL | | struct BA { +LL | | #[suggestion_part] +... | +LL | | var: String, +LL | | } + | |_^ + +error: `code` is not a valid nested attribute of a `multipart_suggestion` attribute + --> $DIR/subdiagnostic-derive.rs:534:43 + | +LL | #[multipart_suggestion(parser::add_paren, code = "...", applicability = "machine-applicable")] + | ^^^^^^^^^^^^ + +error: multipart suggestion without any `#[suggestion_part(...)]` fields + --> $DIR/subdiagnostic-derive.rs:534:1 + | +LL | / #[multipart_suggestion(parser::add_paren, code = "...", applicability = "machine-applicable")] +LL | | +LL | | +LL | | struct BBa { +LL | | var: String, +LL | | } + | |_^ + +error: `#[suggestion_part(...)]` attribute without `code = "..."` + --> $DIR/subdiagnostic-derive.rs:544:5 + | +LL | #[suggestion_part] + | ^^^^^^^^^^^^^^^^^^ + +error: `#[suggestion_part(...)]` attribute without `code = "..."` + --> $DIR/subdiagnostic-derive.rs:552:5 + | +LL | #[suggestion_part()] + | ^^^^^^^^^^^^^^^^^^^^ + +error: `#[primary_span]` is not a valid attribute + --> $DIR/subdiagnostic-derive.rs:561:5 + | +LL | #[primary_span] + | ^^^^^^^^^^^^^^^ + | + = help: multipart suggestions use one or more `#[suggestion_part]`s rather than one `#[primary_span]` + +error: multipart suggestion without any `#[suggestion_part(...)]` fields + --> $DIR/subdiagnostic-derive.rs:558:1 + | +LL | / #[multipart_suggestion(parser::add_paren)] +LL | | +LL | | struct BC { +LL | | #[primary_span] +LL | | +LL | | span: Span, +LL | | } + | |_^ + +error: `#[suggestion_part(...)]` attribute without `code = "..."` + --> $DIR/subdiagnostic-derive.rs:569:5 + | +LL | #[suggestion_part] + | ^^^^^^^^^^^^^^^^^^ + +error: `#[suggestion_part(...)]` attribute without `code = "..."` + --> $DIR/subdiagnostic-derive.rs:572:5 + | +LL | #[suggestion_part()] + | ^^^^^^^^^^^^^^^^^^^^ + +error: `#[suggestion_part(foo = ...)]` is not a valid attribute + --> $DIR/subdiagnostic-derive.rs:575:23 + | +LL | #[suggestion_part(foo = "bar")] + | ^^^^^^^^^^^ + | + = help: `code` is the only valid nested attribute + +error: the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` + --> $DIR/subdiagnostic-derive.rs:578:5 + | +LL | #[suggestion_part(code = "...")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` + --> $DIR/subdiagnostic-derive.rs:581:5 + | +LL | #[suggestion_part()] + | ^^^^^^^^^^^^^^^^^^^^ + +error: specified multiple times + --> $DIR/subdiagnostic-derive.rs:589:37 + | +LL | #[suggestion_part(code = "...", code = ",,,")] + | ^^^^^^^^^^^^ + | +note: previously specified here + --> $DIR/subdiagnostic-derive.rs:589:23 + | +LL | #[suggestion_part(code = "...", code = ",,,")] + | ^^^^^^^^^^^^ + +error: specified multiple times + --> $DIR/subdiagnostic-derive.rs:619:5 + | +LL | #[applicability] + | ^^^^^^^^^^^^^^^^ + | +note: previously specified here + --> $DIR/subdiagnostic-derive.rs:616:43 + | +LL | #[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error: cannot find attribute `foo` in this scope --> $DIR/subdiagnostic-derive.rs:63:3 | @@ -360,6 +475,6 @@ error[E0425]: cannot find value `slug` in module `rustc_errors::fluent` LL | #[label(slug)] | ^^^^ not found in `rustc_errors::fluent` -error: aborting due to 49 previous errors +error: aborting due to 64 previous errors For more information about this error, try `rustc --explain E0425`. From c41f21b3e4e992d5c5c11dae880dc81192a12653 Mon Sep 17 00:00:00 2001 From: Thom Chiovoloni Date: Tue, 30 Aug 2022 01:45:30 -0700 Subject: [PATCH 23/24] Fix UB in Windows `DirBuffIter` (provenance and alignment) --- library/std/src/sys/windows/fs.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs index c702b8e744c89..0aa7c50ded1c7 100644 --- a/library/std/src/sys/windows/fs.rs +++ b/library/std/src/sys/windows/fs.rs @@ -658,18 +658,18 @@ impl File { /// A buffer for holding directory entries. struct DirBuff { - buffer: Vec, + buffer: Box>, } impl DirBuff { + const BUFFER_SIZE: usize = 1024; fn new() -> Self { - const BUFFER_SIZE: usize = 1024; - Self { buffer: vec![0_u8; BUFFER_SIZE] } + Self { buffer: Box::new(Align8([0u8; Self::BUFFER_SIZE])) } } fn capacity(&self) -> usize { - self.buffer.len() + self.buffer.0.len() } fn as_mut_ptr(&mut self) -> *mut u8 { - self.buffer.as_mut_ptr().cast() + self.buffer.0.as_mut_ptr().cast() } /// Returns a `DirBuffIter`. fn iter(&self) -> DirBuffIter<'_> { @@ -678,7 +678,7 @@ impl DirBuff { } impl AsRef<[u8]> for DirBuff { fn as_ref(&self) -> &[u8] { - &self.buffer + &self.buffer.0 } } @@ -706,9 +706,12 @@ impl<'a> Iterator for DirBuffIter<'a> { // used to get the file name slice. let (name, is_directory, next_entry) = unsafe { let info = buffer.as_ptr().cast::(); + // Guaranteed to be aligned in documentation for + // https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_id_both_dir_info + assert!(info.is_aligned()); let next_entry = (*info).NextEntryOffset as usize; let name = crate::slice::from_raw_parts( - (*info).FileName.as_ptr().cast::(), + ptr::addr_of!((*info).FileName).cast::(), (*info).FileNameLength as usize / size_of::(), ); let is_directory = ((*info).FileAttributes & c::FILE_ATTRIBUTE_DIRECTORY) != 0; From 79d4f09942a2fc6f96f56f7ae85196c4aa7df56c Mon Sep 17 00:00:00 2001 From: JeanCASPAR <55629512+JeanCASPAR@users.noreply.github.com> Date: Tue, 30 Aug 2022 14:26:09 +0200 Subject: [PATCH 24/24] Change fatal diagnostic to an error. Co-authored-by: Esteban Kuber --- compiler/rustc_ast_lowering/src/expr.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 6d8ce62d91327..176047616881f 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -1264,7 +1264,13 @@ impl<'hir> LoweringContext<'_, 'hir> { (Some(..), Some(..), HalfOpen) => hir::LangItem::Range, (None, Some(..), Closed) => hir::LangItem::RangeToInclusive, (Some(..), Some(..), Closed) => unreachable!(), - (_, None, Closed) => self.tcx.sess.emit_fatal(InclusiveRangeWithNoEnd { span }), + (start, None, Closed) => { + self.tcx.sess.emit_err(InclusiveRangeWithNoEnd { span }); + match start { + Some(..) => hir::LangItem::RangeFrom, + None => hir::LangItem::RangeFull, + } + } }; let fields = self.arena.alloc_from_iter(