diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 04bad6142b6cc..333cfce47abe2 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1724,6 +1724,12 @@ pub enum StructRest { Rest(Span), /// No trailing `..` or expression. None, + /// No trailing `..` or expression, and also, a parse error occurred inside the struct braces. + /// + /// This struct should be treated similarly to as if it had an `..` in it, + /// in particular rather than reporting missing fields, because the parse error + /// makes which fields the struct was intended to have not fully known. + NoneWithError(ErrorGuaranteed), } #[derive(Clone, Encodable, Decodable, Debug, Walkable)] diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 5b31c421bf346..4a2992038003c 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -340,12 +340,13 @@ impl<'hir> LoweringContext<'_, 'hir> { self.arena.alloc_from_iter(fields.iter().map(|&ident| self.lower_ident(ident))), ), ExprKind::Struct(se) => { - let rest = match &se.rest { - StructRest::Base(e) => hir::StructTailExpr::Base(self.lower_expr(e)), + let rest = match se.rest { + StructRest::Base(ref e) => hir::StructTailExpr::Base(self.lower_expr(e)), StructRest::Rest(sp) => { - hir::StructTailExpr::DefaultFields(self.lower_span(*sp)) + hir::StructTailExpr::DefaultFields(self.lower_span(sp)) } StructRest::None => hir::StructTailExpr::None, + StructRest::NoneWithError(guar) => hir::StructTailExpr::NoneWithError(guar), }; hir::ExprKind::Struct( self.arena.alloc(self.lower_qpath( @@ -1435,7 +1436,7 @@ impl<'hir> LoweringContext<'_, 'hir> { Some(self.lower_span(e.span)) } StructRest::Rest(span) => Some(self.lower_span(*span)), - StructRest::None => None, + StructRest::None | StructRest::NoneWithError(_) => None, }; let struct_pat = hir::PatKind::Struct(qpath, field_pats, fields_omitted); return self.pat_without_dbm(lhs.span, struct_pat); diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index 7dce7f6d41954..9b4ff2b63bd45 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -162,7 +162,7 @@ impl<'a> State<'a> { self.word("{"); let has_rest = match rest { ast::StructRest::Base(_) | ast::StructRest::Rest(_) => true, - ast::StructRest::None => false, + ast::StructRest::None | ast::StructRest::NoneWithError(_) => false, }; if fields.is_empty() && !has_rest { self.word("}"); diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs index 460f4afea963f..9e6b366464341 100644 --- a/compiler/rustc_codegen_llvm/src/va_arg.rs +++ b/compiler/rustc_codegen_llvm/src/va_arg.rs @@ -8,7 +8,7 @@ use rustc_codegen_ssa::traits::{ use rustc_middle::bug; use rustc_middle::ty::Ty; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout}; -use rustc_target::spec::{Abi, Arch, Env}; +use rustc_target::spec::{Arch, Env, RustcAbi}; use crate::builder::Builder; use crate::llvm::{Type, Value}; @@ -272,7 +272,7 @@ fn emit_powerpc_va_arg<'ll, 'tcx>( // Rust does not currently support any powerpc softfloat targets. let target = &bx.cx.tcx.sess.target; - let is_soft_float_abi = target.abi == Abi::SoftFloat; + let is_soft_float_abi = target.rustc_abi == Some(RustcAbi::Softfloat); assert!(!is_soft_float_abi); // All instances of VaArgSafe are passed directly. @@ -1077,7 +1077,7 @@ pub(super) fn emit_va_arg<'ll, 'tcx>( AllowHigherAlign::Yes, ForceRightAdjust::Yes, ), - Arch::RiscV32 if target.abi == Abi::Ilp32e => { + Arch::RiscV32 if target.llvm_abiname == "ilp32e" => { // FIXME: clang manually adjusts the alignment for this ABI. It notes: // // > To be compatible with GCC's behaviors, we force arguments with diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index de00cbc8e7098..326eb2cd13384 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2658,7 +2658,9 @@ impl Expr<'_> { ExprKind::Struct(_, fields, init) => { let init_side_effects = match init { StructTailExpr::Base(init) => init.can_have_side_effects(), - StructTailExpr::DefaultFields(_) | StructTailExpr::None => false, + StructTailExpr::DefaultFields(_) + | StructTailExpr::None + | StructTailExpr::NoneWithError(_) => false, }; fields.iter().map(|field| field.expr).any(|e| e.can_have_side_effects()) || init_side_effects @@ -2950,6 +2952,12 @@ pub enum StructTailExpr<'hir> { /// fields' default values will be used to populate any fields not explicitly mentioned: /// `Foo { .. }`. DefaultFields(Span), + /// No trailing `..` was written, and also, a parse error occurred inside the struct braces. + /// + /// This struct should be treated similarly to as if it had an `..` in it, + /// in particular rather than reporting missing fields, because the parse error + /// makes which fields the struct was intended to have not fully known. + NoneWithError(ErrorGuaranteed), } /// Represents an optionally `Self`-qualified value/type path or associated extension. diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 6a69326d8c847..25ef56f8b0f2c 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -830,7 +830,9 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) walk_list!(visitor, visit_expr_field, fields); match optional_base { StructTailExpr::Base(base) => try_visit!(visitor.visit_expr(base)), - StructTailExpr::None | StructTailExpr::DefaultFields(_) => {} + StructTailExpr::None + | StructTailExpr::NoneWithError(_) + | StructTailExpr::DefaultFields(_) => {} } } ExprKind::Tup(subexpressions) => { diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index ec0688b64f3dd..369c7c077474a 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -1314,6 +1314,7 @@ impl<'a> State<'a> { self.end(ib); } hir::StructTailExpr::None => {} + hir::StructTailExpr::NoneWithError(_) => {} } self.space(); self.word("}"); diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index fd83f6f9ae1eb..9f2134e050bc0 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -2000,229 +2000,256 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return; } - if let hir::StructTailExpr::DefaultFields(span) = *base_expr { - let mut missing_mandatory_fields = Vec::new(); - let mut missing_optional_fields = Vec::new(); - for f in &variant.fields { - let ident = self.tcx.adjust_ident(f.ident(self.tcx), variant.def_id); - if let Some(_) = remaining_fields.remove(&ident) { - if f.value.is_none() { - missing_mandatory_fields.push(ident); - } else { - missing_optional_fields.push(ident); + match *base_expr { + hir::StructTailExpr::DefaultFields(span) => { + let mut missing_mandatory_fields = Vec::new(); + let mut missing_optional_fields = Vec::new(); + for f in &variant.fields { + let ident = self.tcx.adjust_ident(f.ident(self.tcx), variant.def_id); + if let Some(_) = remaining_fields.remove(&ident) { + if f.value.is_none() { + missing_mandatory_fields.push(ident); + } else { + missing_optional_fields.push(ident); + } } } - } - if !self.tcx.features().default_field_values() { - let sugg = self.tcx.crate_level_attribute_injection_span(); - self.dcx().emit_err(BaseExpressionDoubleDot { - span: span.shrink_to_hi(), - // We only mention enabling the feature if this is a nightly rustc *and* the - // expression would make sense with the feature enabled. - default_field_values_suggestion: if self.tcx.sess.is_nightly_build() - && missing_mandatory_fields.is_empty() - && !missing_optional_fields.is_empty() - { - Some(sugg) - } else { - None - }, - add_expr: if !missing_mandatory_fields.is_empty() - || !missing_optional_fields.is_empty() - { - Some(BaseExpressionDoubleDotAddExpr { span: span.shrink_to_hi() }) - } else { - None - }, - remove_dots: if missing_mandatory_fields.is_empty() - && missing_optional_fields.is_empty() - { - Some(BaseExpressionDoubleDotRemove { span }) - } else { - None - }, - }); - return; - } - if variant.fields.is_empty() { - let mut err = self.dcx().struct_span_err( - span, - format!( - "`{adt_ty}` has no fields, `..` needs at least one default field in the \ - struct definition", - ), - ); - err.span_label(path_span, "this type has no fields"); - err.emit(); - } - if !missing_mandatory_fields.is_empty() { - let s = pluralize!(missing_mandatory_fields.len()); - let fields = listify(&missing_mandatory_fields, |f| format!("`{f}`")).unwrap(); - self.dcx() - .struct_span_err( - span.shrink_to_lo(), - format!("missing field{s} {fields} in initializer"), - ) - .with_span_label( - span.shrink_to_lo(), - "fields that do not have a defaulted value must be provided explicitly", - ) - .emit(); - return; - } - let fru_tys = match adt_ty.kind() { - ty::Adt(adt, args) if adt.is_struct() => variant - .fields - .iter() - .map(|f| self.normalize(span, f.ty(self.tcx, args))) - .collect(), - ty::Adt(adt, args) if adt.is_enum() => variant - .fields - .iter() - .map(|f| self.normalize(span, f.ty(self.tcx, args))) - .collect(), - _ => { - self.dcx().emit_err(FunctionalRecordUpdateOnNonStruct { span }); + if !self.tcx.features().default_field_values() { + let sugg = self.tcx.crate_level_attribute_injection_span(); + self.dcx().emit_err(BaseExpressionDoubleDot { + span: span.shrink_to_hi(), + // We only mention enabling the feature if this is a nightly rustc *and* the + // expression would make sense with the feature enabled. + default_field_values_suggestion: if self.tcx.sess.is_nightly_build() + && missing_mandatory_fields.is_empty() + && !missing_optional_fields.is_empty() + { + Some(sugg) + } else { + None + }, + add_expr: if !missing_mandatory_fields.is_empty() + || !missing_optional_fields.is_empty() + { + Some(BaseExpressionDoubleDotAddExpr { span: span.shrink_to_hi() }) + } else { + None + }, + remove_dots: if missing_mandatory_fields.is_empty() + && missing_optional_fields.is_empty() + { + Some(BaseExpressionDoubleDotRemove { span }) + } else { + None + }, + }); return; } - }; - self.typeck_results.borrow_mut().fru_field_types_mut().insert(expr.hir_id, fru_tys); - } else if let hir::StructTailExpr::Base(base_expr) = base_expr { - // FIXME: We are currently creating two branches here in order to maintain - // consistency. But they should be merged as much as possible. - let fru_tys = if self.tcx.features().type_changing_struct_update() { - if adt.is_struct() { - // Make some fresh generic parameters for our ADT type. - let fresh_args = self.fresh_args_for_item(base_expr.span, adt.did()); - // We do subtyping on the FRU fields first, so we can - // learn exactly what types we expect the base expr - // needs constrained to be compatible with the struct - // type we expect from the expectation value. - let fru_tys = variant - .fields - .iter() - .map(|f| { - let fru_ty = self - .normalize(expr.span, self.field_ty(base_expr.span, f, fresh_args)); - let ident = self.tcx.adjust_ident(f.ident(self.tcx), variant.def_id); - if let Some(_) = remaining_fields.remove(&ident) { - let target_ty = self.field_ty(base_expr.span, f, args); - let cause = self.misc(base_expr.span); - match self.at(&cause, self.param_env).sup( - // We're already using inference variables for any params, and don't allow converting - // between different structs, so there is no way this ever actually defines an opaque type. - // Thus choosing `Yes` is fine. - DefineOpaqueTypes::Yes, - target_ty, - fru_ty, - ) { - Ok(InferOk { obligations, value: () }) => { - self.register_predicates(obligations) - } - Err(_) => { - span_bug!( - cause.span, - "subtyping remaining fields of type changing FRU failed: {target_ty} != {fru_ty}: {}::{}", - variant.name, - ident.name, - ); - } - } - } - self.resolve_vars_if_possible(fru_ty) - }) - .collect(); - // The use of fresh args that we have subtyped against - // our base ADT type's fields allows us to guide inference - // along so that, e.g. - // ``` - // MyStruct<'a, F1, F2, const C: usize> { - // f: F1, - // // Other fields that reference `'a`, `F2`, and `C` - // } - // - // let x = MyStruct { - // f: 1usize, - // ..other_struct - // }; - // ``` - // will have the `other_struct` expression constrained to - // `MyStruct<'a, _, F2, C>`, as opposed to just `_`... - // This is important to allow coercions to happen in - // `other_struct` itself. See `coerce-in-base-expr.rs`. - let fresh_base_ty = Ty::new_adt(self.tcx, *adt, fresh_args); - self.check_expr_has_type_or_error( - base_expr, - self.resolve_vars_if_possible(fresh_base_ty), - |_| {}, + if variant.fields.is_empty() { + let mut err = self.dcx().struct_span_err( + span, + format!( + "`{adt_ty}` has no fields, `..` needs at least one default field in \ + the struct definition", + ), ); - fru_tys - } else { - // Check the base_expr, regardless of a bad expected adt_ty, so we can get - // type errors on that expression, too. - self.check_expr(base_expr); - self.dcx().emit_err(FunctionalRecordUpdateOnNonStruct { span: base_expr.span }); - return; + err.span_label(path_span, "this type has no fields"); + err.emit(); } - } else { - self.check_expr_has_type_or_error(base_expr, adt_ty, |_| { - let base_ty = self.typeck_results.borrow().expr_ty(*base_expr); - let same_adt = matches!((adt_ty.kind(), base_ty.kind()), - (ty::Adt(adt, _), ty::Adt(base_adt, _)) if adt == base_adt); - if self.tcx.sess.is_nightly_build() && same_adt { - feature_err( - &self.tcx.sess, - sym::type_changing_struct_update, - base_expr.span, - "type changing struct updating is experimental", + if !missing_mandatory_fields.is_empty() { + let s = pluralize!(missing_mandatory_fields.len()); + let fields = listify(&missing_mandatory_fields, |f| format!("`{f}`")).unwrap(); + self.dcx() + .struct_span_err( + span.shrink_to_lo(), + format!("missing field{s} {fields} in initializer"), + ) + .with_span_label( + span.shrink_to_lo(), + "fields that do not have a defaulted value must be provided explicitly", ) .emit(); - } - }); - match adt_ty.kind() { + return; + } + let fru_tys = match adt_ty.kind() { ty::Adt(adt, args) if adt.is_struct() => variant .fields .iter() - .map(|f| self.normalize(expr.span, f.ty(self.tcx, args))) + .map(|f| self.normalize(span, f.ty(self.tcx, args))) + .collect(), + ty::Adt(adt, args) if adt.is_enum() => variant + .fields + .iter() + .map(|f| self.normalize(span, f.ty(self.tcx, args))) .collect(), _ => { + self.dcx().emit_err(FunctionalRecordUpdateOnNonStruct { span }); + return; + } + }; + self.typeck_results.borrow_mut().fru_field_types_mut().insert(expr.hir_id, fru_tys); + } + hir::StructTailExpr::Base(base_expr) => { + // FIXME: We are currently creating two branches here in order to maintain + // consistency. But they should be merged as much as possible. + let fru_tys = if self.tcx.features().type_changing_struct_update() { + if adt.is_struct() { + // Make some fresh generic parameters for our ADT type. + let fresh_args = self.fresh_args_for_item(base_expr.span, adt.did()); + // We do subtyping on the FRU fields first, so we can + // learn exactly what types we expect the base expr + // needs constrained to be compatible with the struct + // type we expect from the expectation value. + let fru_tys = variant + .fields + .iter() + .map(|f| { + let fru_ty = self.normalize( + expr.span, + self.field_ty(base_expr.span, f, fresh_args), + ); + let ident = + self.tcx.adjust_ident(f.ident(self.tcx), variant.def_id); + if let Some(_) = remaining_fields.remove(&ident) { + let target_ty = self.field_ty(base_expr.span, f, args); + let cause = self.misc(base_expr.span); + match self.at(&cause, self.param_env).sup( + // We're already using inference variables for any params, + // and don't allow converting between different structs, + // so there is no way this ever actually defines an opaque + // type. Thus choosing `Yes` is fine. + DefineOpaqueTypes::Yes, + target_ty, + fru_ty, + ) { + Ok(InferOk { obligations, value: () }) => { + self.register_predicates(obligations) + } + Err(_) => { + span_bug!( + cause.span, + "subtyping remaining fields of type changing FRU \ + failed: {target_ty} != {fru_ty}: {}::{}", + variant.name, + ident.name, + ); + } + } + } + self.resolve_vars_if_possible(fru_ty) + }) + .collect(); + // The use of fresh args that we have subtyped against + // our base ADT type's fields allows us to guide inference + // along so that, e.g. + // ``` + // MyStruct<'a, F1, F2, const C: usize> { + // f: F1, + // // Other fields that reference `'a`, `F2`, and `C` + // } + // + // let x = MyStruct { + // f: 1usize, + // ..other_struct + // }; + // ``` + // will have the `other_struct` expression constrained to + // `MyStruct<'a, _, F2, C>`, as opposed to just `_`... + // This is important to allow coercions to happen in + // `other_struct` itself. See `coerce-in-base-expr.rs`. + let fresh_base_ty = Ty::new_adt(self.tcx, *adt, fresh_args); + self.check_expr_has_type_or_error( + base_expr, + self.resolve_vars_if_possible(fresh_base_ty), + |_| {}, + ); + fru_tys + } else { + // Check the base_expr, regardless of a bad expected adt_ty, so we can get + // type errors on that expression, too. + self.check_expr(base_expr); self.dcx() .emit_err(FunctionalRecordUpdateOnNonStruct { span: base_expr.span }); return; } + } else { + self.check_expr_has_type_or_error(base_expr, adt_ty, |_| { + let base_ty = self.typeck_results.borrow().expr_ty(base_expr); + let same_adt = matches!((adt_ty.kind(), base_ty.kind()), + (ty::Adt(adt, _), ty::Adt(base_adt, _)) if adt == base_adt); + if self.tcx.sess.is_nightly_build() && same_adt { + feature_err( + &self.tcx.sess, + sym::type_changing_struct_update, + base_expr.span, + "type changing struct updating is experimental", + ) + .emit(); + } + }); + match adt_ty.kind() { + ty::Adt(adt, args) if adt.is_struct() => variant + .fields + .iter() + .map(|f| self.normalize(expr.span, f.ty(self.tcx, args))) + .collect(), + _ => { + self.dcx().emit_err(FunctionalRecordUpdateOnNonStruct { + span: base_expr.span, + }); + return; + } + } + }; + self.typeck_results.borrow_mut().fru_field_types_mut().insert(expr.hir_id, fru_tys); + } + rustc_hir::StructTailExpr::NoneWithError(ErrorGuaranteed { .. }) => { + // If parsing the struct recovered from a syntax error, do not report missing + // fields. This prevents spurious errors when a field is intended to be present + // but a preceding syntax error caused it not to be parsed. For example, if a + // struct type `StructName` has fields `foo` and `bar`, then + // StructName { foo(), bar: 2 } + // will not successfully parse a field `foo`, but we will not mention that, + // since the syntax error has already been reported. + } + rustc_hir::StructTailExpr::None => { + if adt_kind != AdtKind::Union + && !remaining_fields.is_empty() + //~ non_exhaustive already reported, which will only happen for extern modules + && !variant.field_list_has_applicable_non_exhaustive() + { + debug!(?remaining_fields); + + // Report missing fields. + + let private_fields: Vec<&ty::FieldDef> = variant + .fields + .iter() + .filter(|field| { + !field.vis.is_accessible_from(tcx.parent_module(expr.hir_id), tcx) + }) + .collect(); + + if !private_fields.is_empty() { + self.report_private_fields( + adt_ty, + path_span, + expr.span, + private_fields, + hir_fields, + ); + } else { + self.report_missing_fields( + adt_ty, + path_span, + expr.span, + remaining_fields, + variant, + hir_fields, + args, + ); + } } - }; - self.typeck_results.borrow_mut().fru_field_types_mut().insert(expr.hir_id, fru_tys); - } else if adt_kind != AdtKind::Union - && !remaining_fields.is_empty() - //~ non_exhaustive already reported, which will only happen for extern modules - && !variant.field_list_has_applicable_non_exhaustive() - { - debug!(?remaining_fields); - let private_fields: Vec<&ty::FieldDef> = variant - .fields - .iter() - .filter(|field| !field.vis.is_accessible_from(tcx.parent_module(expr.hir_id), tcx)) - .collect(); - - if !private_fields.is_empty() { - self.report_private_fields( - adt_ty, - path_span, - expr.span, - private_fields, - hir_fields, - ); - } else { - self.report_missing_fields( - adt_ty, - path_span, - expr.span, - remaining_fields, - variant, - hir_fields, - args, - ); } } } diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index 94f682a8859e9..0a492c795b29c 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -673,7 +673,9 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx let with_expr = match *opt_with { hir::StructTailExpr::Base(w) => &*w, - hir::StructTailExpr::DefaultFields(_) | hir::StructTailExpr::None => { + hir::StructTailExpr::DefaultFields(_) + | hir::StructTailExpr::None + | hir::StructTailExpr::NoneWithError(_) => { return Ok(()); } }; diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index b63feb022ff0c..8e6e9af5a4867 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -632,7 +632,8 @@ impl<'tcx> ThirBuildCx<'tcx> { .collect(), ) } - hir::StructTailExpr::None => AdtExprBase::None, + hir::StructTailExpr::None + | hir::StructTailExpr::NoneWithError(_) => AdtExprBase::None, }, })) } @@ -669,7 +670,10 @@ impl<'tcx> ThirBuildCx<'tcx> { hir::StructTailExpr::Base(base) => { span_bug!(base.span, "unexpected res: {:?}", res); } - hir::StructTailExpr::None => AdtExprBase::None, + hir::StructTailExpr::None + | hir::StructTailExpr::NoneWithError(_) => { + AdtExprBase::None + } }, })) } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 1a4c5238f72bc..e6f73e41f0821 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -3842,6 +3842,15 @@ impl<'a> Parser<'a> { recovered_async = Some(guar); } + // If we encountered an error which we are recovering from, treat the struct + // as if it has a `..` in it, because we don’t know what fields the user + // might have *intended* it to have. + // + // This assignment will be overwritten if we actually parse a `..` later. + // + // (Note that this code is duplicated between here and below in comma parsing. + base = ast::StructRest::NoneWithError(guar); + // If the next token is a comma, then try to parse // what comes next as additional fields, rather than // bailing out until next `}`. @@ -3892,6 +3901,10 @@ impl<'a> Parser<'a> { } else if let Some(f) = field_ident(self, guar) { fields.push(f); } + + // See comment above on this same assignment inside of field parsing. + base = ast::StructRest::NoneWithError(guar); + self.recover_stmt_(SemiColonMode::Comma, BlockMode::Ignore); let _ = self.eat(exp!(Comma)); } diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 287e8579e080a..63860dfdd1a65 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -1100,7 +1100,7 @@ impl<'tcx> Visitor<'tcx> for NamePrivacyVisitor<'tcx> { qpath.span(), ); } - hir::StructTailExpr::None => { + hir::StructTailExpr::None | hir::StructTailExpr::NoneWithError(_) => { let mut failed_fields = vec![]; for field in fields { let (hir_id, use_ctxt) = (field.hir_id, field.ident.span); diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 3e9476b124f67..6e9d4a38d987c 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -5066,7 +5066,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { match &se.rest { StructRest::Base(expr) => self.visit_expr(expr), StructRest::Rest(_span) => {} - StructRest::None => {} + StructRest::None | StructRest::NoneWithError(_) => {} } } diff --git a/compiler/rustc_target/src/callconv/aarch64.rs b/compiler/rustc_target/src/callconv/aarch64.rs index 13be3888611f1..e9a19aa7024bb 100644 --- a/compiler/rustc_target/src/callconv/aarch64.rs +++ b/compiler/rustc_target/src/callconv/aarch64.rs @@ -3,7 +3,7 @@ use std::iter; use rustc_abi::{BackendRepr, HasDataLayout, Primitive, TyAbiInterface}; use crate::callconv::{ArgAbi, FnAbi, Reg, RegKind, Uniform}; -use crate::spec::{Abi, HasTargetSpec, Target}; +use crate::spec::{HasTargetSpec, RustcAbi, Target}; /// Indicates the variant of the AArch64 ABI we are compiling for. /// Used to accommodate Apple and Microsoft's deviations from the usual AAPCS ABI. @@ -34,7 +34,7 @@ where RegKind::Integer => false, // The softfloat ABI treats floats like integers, so they // do not get homogeneous aggregate treatment. - RegKind::Float => cx.target_spec().abi != Abi::SoftFloat, + RegKind::Float => cx.target_spec().rustc_abi != Some(RustcAbi::Softfloat), RegKind::Vector => size.bits() == 64 || size.bits() == 128, }; @@ -43,7 +43,7 @@ where } fn softfloat_float_abi(target: &Target, arg: &mut ArgAbi<'_, Ty>) { - if target.abi != Abi::SoftFloat { + if target.rustc_abi != Some(RustcAbi::Softfloat) { return; } // Do *not* use the float registers for passing arguments, as that would make LLVM pick the ABI diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index d9c872c9b4352..a4460a3725174 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -2210,8 +2210,10 @@ pub struct TargetOptions { pub env: Env, /// ABI name to distinguish multiple ABIs on the same OS and architecture. For instance, `"eabi"` /// or `"eabihf"`. Defaults to [`Abi::Unspecified`]. - /// This field is *not* forwarded directly to LLVM; its primary purpose is `cfg(target_abi)`. - /// However, parts of the backend do check this field for specific values to enable special behavior. + /// This field is *not* forwarded directly to LLVM and therefore does not control which ABI (in + /// the sense of function calling convention) is actually used; its primary purpose is + /// `cfg(target_abi)`. The actual calling convention is controlled by `llvm_abiname`, + /// `llvm_floatabi`, and `rustc_abi`. pub abi: Abi, /// Vendor name to use for conditional compilation (`target_vendor`). Defaults to "unknown". #[rustc_lint_opt_deny_field_access( @@ -3228,8 +3230,8 @@ impl Target { ), RustcAbi::Softfloat => check_matches!( self.arch, - Arch::X86 | Arch::X86_64 | Arch::S390x, - "`softfloat` ABI is only valid for x86 and s390x targets" + Arch::X86 | Arch::X86_64 | Arch::S390x | Arch::AArch64, + "`softfloat` ABI is only valid for x86, s390x, and aarch64 targets" ), } } diff --git a/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_none_softfloat.rs b/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_none_softfloat.rs index 07512c01dc4a8..e204c6466e297 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_none_softfloat.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_none_softfloat.rs @@ -8,13 +8,14 @@ use rustc_abi::Endian; use crate::spec::{ - Abi, Arch, Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, SanitizerSet, StackProbeType, - Target, TargetMetadata, TargetOptions, + Abi, Arch, Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, RustcAbi, SanitizerSet, + StackProbeType, Target, TargetMetadata, TargetOptions, }; pub(crate) fn target() -> Target { let opts = TargetOptions { abi: Abi::SoftFloat, + rustc_abi: Some(RustcAbi::Softfloat), linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), features: "+v8a,+strict-align,-neon".into(), diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_none_softfloat.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_none_softfloat.rs index ad444c139bff6..ed2e2fb6ab70b 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_none_softfloat.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_none_softfloat.rs @@ -7,13 +7,14 @@ // For example, `-C target-cpu=cortex-a53`. use crate::spec::{ - Abi, Arch, Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, SanitizerSet, StackProbeType, - Target, TargetMetadata, TargetOptions, + Abi, Arch, Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, RustcAbi, SanitizerSet, + StackProbeType, Target, TargetMetadata, TargetOptions, }; pub(crate) fn target() -> Target { let opts = TargetOptions { abi: Abi::SoftFloat, + rustc_abi: Some(RustcAbi::Softfloat), linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), features: "+v8a,+strict-align,-neon".into(), diff --git a/compiler/rustc_target/src/spec/targets/aarch64v8r_unknown_none_softfloat.rs b/compiler/rustc_target/src/spec/targets/aarch64v8r_unknown_none_softfloat.rs index 63f518873c609..6f11f97e3d1df 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64v8r_unknown_none_softfloat.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64v8r_unknown_none_softfloat.rs @@ -1,11 +1,12 @@ use crate::spec::{ - Abi, Arch, Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, SanitizerSet, StackProbeType, - Target, TargetMetadata, TargetOptions, + Abi, Arch, Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, RustcAbi, SanitizerSet, + StackProbeType, Target, TargetMetadata, TargetOptions, }; pub(crate) fn target() -> Target { let opts = TargetOptions { abi: Abi::SoftFloat, + rustc_abi: Some(RustcAbi::Softfloat), linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), relocation_model: RelocModel::Static, diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index c3de7e257662f..80674ab4cf8a2 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -5,7 +5,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_macros::HashStable_Generic; use rustc_span::{Symbol, sym}; -use crate::spec::{Abi, Arch, FloatAbi, RustcAbi, Target}; +use crate::spec::{Arch, FloatAbi, RustcAbi, Target}; /// Features that control behaviour of rustc, rather than the codegen. /// These exist globally and are not in the target-specific lists below. @@ -1154,17 +1154,21 @@ impl Target { Arch::AArch64 | Arch::Arm64EC => { // Aarch64 has no sane ABI specifier, and LLVM doesn't even have a way to force // the use of soft-float, so all we can do here is some crude hacks. - if self.abi == Abi::SoftFloat { - // LLVM will use float registers when `fp-armv8` is available, e.g. for - // calls to built-ins. The only way to ensure a consistent softfloat ABI - // on aarch64 is to never enable `fp-armv8`, so we enforce that. - // In Rust we tie `neon` and `fp-armv8` together, therefore `neon` is the - // feature we have to mark as incompatible. - FeatureConstraints { required: &[], incompatible: &["neon"] } - } else { - // Everything else is assumed to use a hardfloat ABI. neon and fp-armv8 must be enabled. - // `FeatureConstraints` uses Rust feature names, hence only "neon" shows up. - FeatureConstraints { required: &["neon"], incompatible: &[] } + match self.rustc_abi { + Some(RustcAbi::Softfloat) => { + // LLVM will use float registers when `fp-armv8` is available, e.g. for + // calls to built-ins. The only way to ensure a consistent softfloat ABI + // on aarch64 is to never enable `fp-armv8`, so we enforce that. + // In Rust we tie `neon` and `fp-armv8` together, therefore `neon` is the + // feature we have to mark as incompatible. + FeatureConstraints { required: &[], incompatible: &["neon"] } + } + None => { + // Everything else is assumed to use a hardfloat ABI. neon and fp-armv8 must be enabled. + // `FeatureConstraints` uses Rust feature names, hence only "neon" shows up. + FeatureConstraints { required: &["neon"], incompatible: &[] } + } + Some(r) => panic!("invalid Rust ABI for aarch64: {r:?}"), } } Arch::RiscV32 | Arch::RiscV64 => { diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 386c75dad9dd3..814333859488f 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -3883,7 +3883,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { err.span_note(assoc_span, msg); } ObligationCauseCode::TrivialBound => { - err.help("see issue #48214"); tcx.disabled_nightly_features(err, [(String::new(), sym::trivial_bounds)]); } ObligationCauseCode::OpaqueReturnType(expr_info) => { diff --git a/library/std/src/os/hermit/ffi.rs b/library/std/src/os/hermit/ffi.rs index 19761fd99b400..01a54e1ac8df8 100644 --- a/library/std/src/os/hermit/ffi.rs +++ b/library/std/src/os/hermit/ffi.rs @@ -1,4 +1,4 @@ -//! HermitCore-specific extension to the primitives in the `std::ffi` module +//! Hermit-specific extension to the primitives in the `std::ffi` module //! //! # Examples //! diff --git a/library/std/src/sys/pal/hermit/mod.rs b/library/std/src/sys/pal/hermit/mod.rs index db64f8d882e24..f10a090a6e919 100644 --- a/library/std/src/sys/pal/hermit/mod.rs +++ b/library/std/src/sys/pal/hermit/mod.rs @@ -1,7 +1,7 @@ -//! System bindings for HermitCore +//! System bindings for Hermit //! //! This module contains the facade (aka platform-specific) implementations of -//! OS level functionality for HermitCore. +//! OS level functionality for Hermit. //! //! This is all super highly experimental and not actually intended for //! wide/production use yet, it's still all in the experimental category. This @@ -30,7 +30,7 @@ pub fn unsupported() -> io::Result { } pub fn unsupported_err() -> io::Error { - io::const_error!(io::ErrorKind::Unsupported, "operation not supported on HermitCore yet") + io::const_error!(io::ErrorKind::Unsupported, "operation not supported on Hermit yet") } pub fn abort_internal() -> ! { diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index 485af5ab1d016..3ec2bc3af8d42 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -429,7 +429,7 @@ impl fmt::Display for Display<'_> { "freebsd" => "FreeBSD", "fuchsia" => "Fuchsia", "haiku" => "Haiku", - "hermit" => "HermitCore", + "hermit" => "Hermit", "illumos" => "illumos", "ios" => "iOS", "l4re" => "L4Re", diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 375f8338319b6..79dd50c4682de 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -285,7 +285,11 @@ pub(crate) fn create_config( crate_check_cfg: check_cfgs, input, output_file: None, - output_dir: None, + output_dir: if render_options.output_to_stdout { + None + } else { + Some(render_options.output.clone()) + }, file_loader: None, lint_caps, psess_created: None, diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs index fdb8e1b475c1d..3620e5425a947 100644 --- a/src/tools/clippy/clippy_lints/src/no_effect.rs +++ b/src/tools/clippy/clippy_lints/src/no_effect.rs @@ -242,7 +242,7 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { !expr_ty_has_significant_drop(cx, expr) && fields.iter().all(|field| has_no_effect(cx, field.expr)) && match &base { - StructTailExpr::None | StructTailExpr::DefaultFields(_) => true, + StructTailExpr::None | StructTailExpr::NoneWithError(_) | StructTailExpr::DefaultFields(_) => true, StructTailExpr::Base(base) => has_no_effect(cx, base), } }, @@ -353,7 +353,7 @@ fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option Some(base), - StructTailExpr::None | StructTailExpr::DefaultFields(_) => None, + StructTailExpr::None | StructTailExpr::NoneWithError(_) | StructTailExpr::DefaultFields(_) => None, }; Some(fields.iter().map(|f| &f.expr).chain(base).map(Deref::deref).collect()) } diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index d5191794b6b06..7ba28f95a719f 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -673,7 +673,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { bind!(self, qpath, fields); let base = OptionPat::new(match base { StructTailExpr::Base(base) => Some(self.bind("base", base)), - StructTailExpr::None | StructTailExpr::DefaultFields(_) => None, + StructTailExpr::None | StructTailExpr::NoneWithError(_) | StructTailExpr::DefaultFields(_) => None, }); kind!("Struct({qpath}, {fields}, {base})"); self.qpath(qpath, &expr.name, expr.value.hir_id); diff --git a/src/tools/rustfmt/src/expr.rs b/src/tools/rustfmt/src/expr.rs index b79137c44427e..51b73351fe6a5 100644 --- a/src/tools/rustfmt/src/expr.rs +++ b/src/tools/rustfmt/src/expr.rs @@ -1809,7 +1809,7 @@ fn rewrite_struct_lit<'a>( match struct_rest { ast::StructRest::Base(expr) => Some(StructLitField::Base(&**expr)), ast::StructRest::Rest(span) => Some(StructLitField::Rest(*span)), - ast::StructRest::None => None, + ast::StructRest::None | ast::StructRest::NoneWithError(_) => None, } .into_iter(), ); diff --git a/tests/run-make/rustdoc-dep-info/rmake.rs b/tests/run-make/rustdoc-dep-info/rmake.rs index 5d6176b18e886..11901c97fd6a0 100644 --- a/tests/run-make/rustdoc-dep-info/rmake.rs +++ b/tests/run-make/rustdoc-dep-info/rmake.rs @@ -7,6 +7,8 @@ use run_make_support::assertion_helpers::assert_contains; use run_make_support::{path, rfs, rustdoc}; fn main() { + rfs::create_dir("doc"); + // We're only emitting dep info, so we shouldn't be running static analysis to // figure out that this program is erroneous. // Ensure that all kinds of input reading flags end up in dep-info. @@ -20,7 +22,7 @@ fn main() { .emit("dep-info") .run(); - let content = rfs::read_to_string("foo.d"); + let content = rfs::read_to_string("doc/foo.d"); assert_contains(&content, "lib.rs:"); assert_contains(&content, "foo.rs:"); assert_contains(&content, "bar.rs:"); diff --git a/tests/run-make/rustdoc-scrape-examples-dep-info/rmake.rs b/tests/run-make/rustdoc-scrape-examples-dep-info/rmake.rs index 00a87477ab5e4..5a612fd130052 100644 --- a/tests/run-make/rustdoc-scrape-examples-dep-info/rmake.rs +++ b/tests/run-make/rustdoc-scrape-examples-dep-info/rmake.rs @@ -5,15 +5,17 @@ use run_make_support::{assert_contains, rfs}; mod scrape; fn main() { + rfs::create_dir("rustdoc"); + scrape::scrape( &["--scrape-tests", "--emit=dep-info"], &["--emit=dep-info,invocation-specific"], ); - let content = rfs::read_to_string("foobar.d").replace(r"\", "/"); + let content = rfs::read_to_string("rustdoc/foobar.d").replace(r"\", "/"); assert_contains(&content, "lib.rs:"); assert_contains(&content, "rustdoc/ex.calls:"); - let content = rfs::read_to_string("ex.d").replace(r"\", "/"); + let content = rfs::read_to_string("rustdoc/ex.d").replace(r"\", "/"); assert_contains(&content, "examples/ex.rs:"); } diff --git a/tests/ui/const-generics/issues/issue-67185-2.stderr b/tests/ui/const-generics/issues/issue-67185-2.stderr index 85f916974e383..8a170201832c9 100644 --- a/tests/ui/const-generics/issues/issue-67185-2.stderr +++ b/tests/ui/const-generics/issues/issue-67185-2.stderr @@ -11,7 +11,6 @@ LL | impl Bar for [u16; 4] {} | ^^^^^^^^^^^^^^^^^^^^^ `[u16; 4]` LL | impl Bar for [[u16; 3]; 3] {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `[[u16; 3]; 3]` - = help: see issue #48214 help: add `#![feature(trivial_bounds)]` to the crate attributes to enable | LL + #![feature(trivial_bounds)] @@ -30,7 +29,6 @@ LL | impl Bar for [u16; 4] {} | ^^^^^^^^^^^^^^^^^^^^^ `[u16; 4]` LL | impl Bar for [[u16; 3]; 3] {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `[[u16; 3]; 3]` - = help: see issue #48214 help: add `#![feature(trivial_bounds)]` to the crate attributes to enable | LL + #![feature(trivial_bounds)] diff --git a/tests/ui/cross/cross-fn-cache-hole.stderr b/tests/ui/cross/cross-fn-cache-hole.stderr index b7bcbc8933ff4..ca43119dde7f6 100644 --- a/tests/ui/cross/cross-fn-cache-hole.stderr +++ b/tests/ui/cross/cross-fn-cache-hole.stderr @@ -9,7 +9,6 @@ help: this trait has no implementations, consider adding one | LL | trait Bar { } | ^^^^^^^^^^^^ - = help: see issue #48214 help: add `#![feature(trivial_bounds)]` to the crate attributes to enable | LL + #![feature(trivial_bounds)] diff --git a/tests/ui/delegation/generics/impl-to-trait-method.stderr b/tests/ui/delegation/generics/impl-to-trait-method.stderr index 2c0b466dba3c9..74ccf61bea703 100644 --- a/tests/ui/delegation/generics/impl-to-trait-method.stderr +++ b/tests/ui/delegation/generics/impl-to-trait-method.stderr @@ -14,7 +14,6 @@ help: this trait has no implementations, consider adding one | LL | trait Trait0 {} | ^^^^^^^^^^^^ - = help: see issue #48214 help: add `#![feature(trivial_bounds)]` to the crate attributes to enable | LL + #![feature(trivial_bounds)] diff --git a/tests/ui/feature-gates/feature-gate-trivial_bounds.stderr b/tests/ui/feature-gates/feature-gate-trivial_bounds.stderr index 10a7498d5a1c3..e9bee1597c5cd 100644 --- a/tests/ui/feature-gates/feature-gate-trivial_bounds.stderr +++ b/tests/ui/feature-gates/feature-gate-trivial_bounds.stderr @@ -9,7 +9,6 @@ help: the trait `Foo` is implemented for `()` | LL | impl Foo for () where i32: Foo { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = help: see issue #48214 help: add `#![feature(trivial_bounds)]` to the crate attributes to enable | LL + #![feature(trivial_bounds)] @@ -26,7 +25,6 @@ help: the trait `Foo` is implemented for `()` | LL | impl Foo for () where i32: Foo { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = help: see issue #48214 help: add `#![feature(trivial_bounds)]` to the crate attributes to enable | LL + #![feature(trivial_bounds)] @@ -43,7 +41,6 @@ help: the trait `Foo` is implemented for `()` | LL | impl Foo for () where i32: Foo { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = help: see issue #48214 help: add `#![feature(trivial_bounds)]` to the crate attributes to enable | LL + #![feature(trivial_bounds)] @@ -60,7 +57,6 @@ help: the trait `Foo` is implemented for `()` | LL | impl Foo for () where i32: Foo { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = help: see issue #48214 help: add `#![feature(trivial_bounds)]` to the crate attributes to enable | LL + #![feature(trivial_bounds)] @@ -77,7 +73,6 @@ help: the trait `Foo` is implemented for `()` | LL | impl Foo for () where i32: Foo { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = help: see issue #48214 help: add `#![feature(trivial_bounds)]` to the crate attributes to enable | LL + #![feature(trivial_bounds)] @@ -94,7 +89,6 @@ help: the trait `Foo` is implemented for `()` | LL | impl Foo for () where i32: Foo { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = help: see issue #48214 help: add `#![feature(trivial_bounds)]` to the crate attributes to enable | LL + #![feature(trivial_bounds)] @@ -106,7 +100,6 @@ error[E0277]: the trait bound `String: Neg` is not satisfied LL | fn use_op(s: String) -> String where String: ::std::ops::Neg { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Neg` is not implemented for `String` | - = help: see issue #48214 help: add `#![feature(trivial_bounds)]` to the crate attributes to enable | LL + #![feature(trivial_bounds)] @@ -119,7 +112,6 @@ LL | fn use_for() where i32: Iterator { | ^^^^^^^^^^^^^ `i32` is not an iterator | = help: the trait `Iterator` is not implemented for `i32` - = help: see issue #48214 help: add `#![feature(trivial_bounds)]` to the crate attributes to enable | LL + #![feature(trivial_bounds)] @@ -132,7 +124,6 @@ LL | struct TwoStrs(str, str) where str: Sized; | ^^^^^^^^^^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `str` - = help: see issue #48214 help: add `#![feature(trivial_bounds)]` to the crate attributes to enable | LL + #![feature(trivial_bounds)] @@ -150,7 +141,6 @@ note: required because it appears within the type `Dst<(dyn A + 'static)>` | LL | struct Dst { | ^^^ - = help: see issue #48214 help: add `#![feature(trivial_bounds)]` to the crate attributes to enable | LL + #![feature(trivial_bounds)] @@ -163,7 +153,6 @@ LL | fn return_str() -> str where str: Sized { | ^^^^^^^^^^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `str` - = help: see issue #48214 help: add `#![feature(trivial_bounds)]` to the crate attributes to enable | LL + #![feature(trivial_bounds)] diff --git a/tests/ui/impl-trait/in-trait/synthetic-hir-has-parent.stderr b/tests/ui/impl-trait/in-trait/synthetic-hir-has-parent.stderr index 8ff8f12cdf462..5d19e20757f4c 100644 --- a/tests/ui/impl-trait/in-trait/synthetic-hir-has-parent.stderr +++ b/tests/ui/impl-trait/in-trait/synthetic-hir-has-parent.stderr @@ -4,7 +4,6 @@ error[E0277]: the trait bound `String: Copy` is not satisfied LL | String: Copy; | ^^^^^^^^^^^^ the trait `Copy` is not implemented for `String` | - = help: see issue #48214 help: add `#![feature(trivial_bounds)]` to the crate attributes to enable | LL + #![feature(trivial_bounds)] @@ -16,7 +15,6 @@ error[E0277]: the trait bound `String: Copy` is not satisfied LL | fn demo() -> impl Foo | ^^^^^^^^ the trait `Copy` is not implemented for `String` | - = help: see issue #48214 help: add `#![feature(trivial_bounds)]` to the crate attributes to enable | LL + #![feature(trivial_bounds)] diff --git a/tests/ui/parser/impl-item-type-no-body-semantic-fail.stderr b/tests/ui/parser/impl-item-type-no-body-semantic-fail.stderr index 9800420072be4..5c50a580fd148 100644 --- a/tests/ui/parser/impl-item-type-no-body-semantic-fail.stderr +++ b/tests/ui/parser/impl-item-type-no-body-semantic-fail.stderr @@ -88,7 +88,6 @@ error[E0277]: the trait bound `X: Eq` is not satisfied LL | type W: Ord where Self: Eq; | ^^^^^^^^ the trait `Eq` is not implemented for `X` | - = help: see issue #48214 help: consider annotating `X` with `#[derive(Eq)]` | LL + #[derive(Eq)] @@ -105,7 +104,6 @@ error[E0277]: the trait bound `X: Eq` is not satisfied LL | type W where Self: Eq; | ^^^^^^^^ the trait `Eq` is not implemented for `X` | - = help: see issue #48214 help: consider annotating `X` with `#[derive(Eq)]` | LL + #[derive(Eq)] diff --git a/tests/ui/parser/issues/issue-102806.rs b/tests/ui/parser/issues/issue-102806.rs index 5ee8c5c1e3de0..804e727272d4c 100644 --- a/tests/ui/parser/issues/issue-102806.rs +++ b/tests/ui/parser/issues/issue-102806.rs @@ -16,7 +16,6 @@ fn pz(v: V3) { let _ = V3 { z: 0.0, ... }; //~^ ERROR expected identifier - //~| ERROR missing fields `x` and `y` in initializer of `V3` let V3 { z: val, ... } = v; //~^ ERROR expected field pattern diff --git a/tests/ui/parser/issues/issue-102806.stderr b/tests/ui/parser/issues/issue-102806.stderr index cd447c6dec0d2..d4c899ed58114 100644 --- a/tests/ui/parser/issues/issue-102806.stderr +++ b/tests/ui/parser/issues/issue-102806.stderr @@ -31,7 +31,7 @@ LL | let _ = V3 { z: 0.0, ... }; | while parsing this struct error: expected field pattern, found `...` - --> $DIR/issue-102806.rs:21:22 + --> $DIR/issue-102806.rs:20:22 | LL | let V3 { z: val, ... } = v; | ^^^ @@ -42,12 +42,5 @@ LL - let V3 { z: val, ... } = v; LL + let V3 { z: val, .. } = v; | -error[E0063]: missing fields `x` and `y` in initializer of `V3` - --> $DIR/issue-102806.rs:17:13 - | -LL | let _ = V3 { z: 0.0, ... }; - | ^^ missing `x` and `y` - -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors -For more information about this error, try `rustc --explain E0063`. diff --git a/tests/ui/parser/issues/issue-52496.rs b/tests/ui/parser/issues/issue-52496.rs index 05461f8b8c413..3e88a92aae9bf 100644 --- a/tests/ui/parser/issues/issue-52496.rs +++ b/tests/ui/parser/issues/issue-52496.rs @@ -7,6 +7,5 @@ fn main() { let bar = 1.5f32; let _ = Foo { bar.into(), bat: -1, . }; //~^ ERROR expected one of - //~| ERROR missing fields `bar` and `baz` in initializer of `Foo` //~| ERROR expected identifier, found `.` } diff --git a/tests/ui/parser/issues/issue-52496.stderr b/tests/ui/parser/issues/issue-52496.stderr index a97effb4e0cd8..da22f5ce13562 100644 --- a/tests/ui/parser/issues/issue-52496.stderr +++ b/tests/ui/parser/issues/issue-52496.stderr @@ -37,12 +37,6 @@ error[E0063]: missing field `bat` in initializer of `Foo` LL | let _ = Foo { bar: .5, baz: 42 }; | ^^^ missing `bat` -error[E0063]: missing fields `bar` and `baz` in initializer of `Foo` - --> $DIR/issue-52496.rs:8:13 - | -LL | let _ = Foo { bar.into(), bat: -1, . }; - | ^^^ missing `bar` and `baz` - -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0063`. diff --git a/tests/ui/parser/removed-syntax/removed-syntax-with-2.rs b/tests/ui/parser/removed-syntax/removed-syntax-with-2.rs index 451057c66a127..119afdcc75e36 100644 --- a/tests/ui/parser/removed-syntax/removed-syntax-with-2.rs +++ b/tests/ui/parser/removed-syntax/removed-syntax-with-2.rs @@ -7,5 +7,4 @@ fn main() { let a = S { foo: (), bar: () }; let b = S { foo: (), with a }; //~^ ERROR expected one of `,`, `:`, or `}`, found `a` - //~| ERROR missing field `bar` in initializer of `S` } diff --git a/tests/ui/parser/removed-syntax/removed-syntax-with-2.stderr b/tests/ui/parser/removed-syntax/removed-syntax-with-2.stderr index e75c5bcd64319..7768aeccfad99 100644 --- a/tests/ui/parser/removed-syntax/removed-syntax-with-2.stderr +++ b/tests/ui/parser/removed-syntax/removed-syntax-with-2.stderr @@ -7,12 +7,5 @@ LL | let b = S { foo: (), with a }; | | while parsing this struct field | while parsing this struct -error[E0063]: missing field `bar` in initializer of `S` - --> $DIR/removed-syntax-with-2.rs:8:13 - | -LL | let b = S { foo: (), with a }; - | ^ missing `bar` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0063`. diff --git a/tests/ui/parser/struct-field-numeric-shorthand.rs b/tests/ui/parser/struct-field-numeric-shorthand.rs index 645abd9c7192d..aa342eb02a916 100644 --- a/tests/ui/parser/struct-field-numeric-shorthand.rs +++ b/tests/ui/parser/struct-field-numeric-shorthand.rs @@ -5,5 +5,4 @@ fn main() { //~^ ERROR expected identifier, found `0` //~| ERROR expected identifier, found `1` //~| ERROR expected identifier, found `2` - //~| ERROR missing fields `0`, `1` and `2` in initializer of `Rgb` } diff --git a/tests/ui/parser/struct-field-numeric-shorthand.stderr b/tests/ui/parser/struct-field-numeric-shorthand.stderr index bfb8a931b6406..9878cfbbdceb8 100644 --- a/tests/ui/parser/struct-field-numeric-shorthand.stderr +++ b/tests/ui/parser/struct-field-numeric-shorthand.stderr @@ -22,12 +22,5 @@ LL | let _ = Rgb { 0, 1, 2 }; | | | while parsing this struct -error[E0063]: missing fields `0`, `1` and `2` in initializer of `Rgb` - --> $DIR/struct-field-numeric-shorthand.rs:4:13 - | -LL | let _ = Rgb { 0, 1, 2 }; - | ^^^ missing `0`, `1` and `2` - -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0063`. diff --git a/tests/ui/structs/syntax-error-not-missing-field.rs b/tests/ui/structs/syntax-error-not-missing-field.rs new file mode 100644 index 0000000000000..f1080d3908cf4 --- /dev/null +++ b/tests/ui/structs/syntax-error-not-missing-field.rs @@ -0,0 +1,38 @@ +// Check that a syntax error inside a struct literal does not also report missing fields, +// because the field might be present but hidden by the syntax error. +// +// The stderr for this test should contain ONLY one syntax error per struct literal, +// and not any errors about missing fields. + +struct Foo { a: isize, b: isize } + +fn make_a() -> isize { 1234 } + +fn expr_wrong_separator() { + let f = Foo { a: make_a(); b: 2 }; //~ ERROR found `;` +} + +fn expr_missing_separator() { + let f = Foo { a: make_a() b: 2 }; //~ ERROR found `b` +} + +fn expr_rest_trailing_comma() { + let f = Foo { a: make_a(), ..todo!(), }; //~ ERROR cannot use a comma +} + +fn expr_missing_field_name() { + let f = Foo { make_a(), b: 2, }; //~ ERROR found `(` +} + +fn pat_wrong_separator(Foo { a; b }: Foo) { //~ ERROR expected `,` + let _ = (a, b); +} + +fn pat_missing_separator(Foo { a b }: Foo) { //~ ERROR expected `,` + let _ = (a, b); +} + +fn pat_rest_trailing_comma(Foo { a, .., }: Foo) { //~ ERROR expected `}`, found `,` +} + +fn main() {} diff --git a/tests/ui/structs/syntax-error-not-missing-field.stderr b/tests/ui/structs/syntax-error-not-missing-field.stderr new file mode 100644 index 0000000000000..d781a41300f36 --- /dev/null +++ b/tests/ui/structs/syntax-error-not-missing-field.stderr @@ -0,0 +1,74 @@ +error: expected one of `,`, `.`, `?`, `}`, or an operator, found `;` + --> $DIR/syntax-error-not-missing-field.rs:12:30 + | +LL | let f = Foo { a: make_a(); b: 2 }; + | --- ^ + | | | + | | expected one of `,`, `.`, `?`, `}`, or an operator + | | help: try adding a comma: `,` + | while parsing this struct + +error: expected one of `,`, `.`, `?`, `}`, or an operator, found `b` + --> $DIR/syntax-error-not-missing-field.rs:16:31 + | +LL | let f = Foo { a: make_a() b: 2 }; + | --- -^ expected one of `,`, `.`, `?`, `}`, or an operator + | | | + | | help: try adding a comma: `,` + | while parsing this struct + +error: cannot use a comma after the base struct + --> $DIR/syntax-error-not-missing-field.rs:20:32 + | +LL | let f = Foo { a: make_a(), ..todo!(), }; + | ^^^^^^^^^ + | + = note: the base struct must always be the last field +help: remove this comma + | +LL - let f = Foo { a: make_a(), ..todo!(), }; +LL + let f = Foo { a: make_a(), ..todo!() }; + | + +error: expected one of `,`, `:`, or `}`, found `(` + --> $DIR/syntax-error-not-missing-field.rs:24:25 + | +LL | let f = Foo { make_a(), b: 2, }; + | --- ------^ expected one of `,`, `:`, or `}` + | | | + | | while parsing this struct field + | while parsing this struct + | +help: try naming a field + | +LL | let f = Foo { make_a: make_a(), b: 2, }; + | +++++++ + +error: expected `,` + --> $DIR/syntax-error-not-missing-field.rs:27:31 + | +LL | fn pat_wrong_separator(Foo { a; b }: Foo) { + | --- ^ + | | + | while parsing the fields for this pattern + +error: expected `,` + --> $DIR/syntax-error-not-missing-field.rs:31:34 + | +LL | fn pat_missing_separator(Foo { a b }: Foo) { + | --- ^ + | | + | while parsing the fields for this pattern + +error: expected `}`, found `,` + --> $DIR/syntax-error-not-missing-field.rs:35:39 + | +LL | fn pat_rest_trailing_comma(Foo { a, .., }: Foo) { + | --^ + | | | + | | expected `}` + | | help: remove this comma + | `..` must be at the end and cannot have a trailing comma + +error: aborting due to 7 previous errors + diff --git a/tests/ui/suggestions/struct-initializer-comma.fixed b/tests/ui/suggestions/struct-initializer-comma.fixed index 556bfbca58df4..9bd9434a3b59d 100644 --- a/tests/ui/suggestions/struct-initializer-comma.fixed +++ b/tests/ui/suggestions/struct-initializer-comma.fixed @@ -7,7 +7,6 @@ pub struct Foo { fn main() { let _ = Foo { - //~^ ERROR missing field first: true, second: 25 //~^ ERROR expected one of diff --git a/tests/ui/suggestions/struct-initializer-comma.rs b/tests/ui/suggestions/struct-initializer-comma.rs index 7b93bf187b870..4b225df08df0a 100644 --- a/tests/ui/suggestions/struct-initializer-comma.rs +++ b/tests/ui/suggestions/struct-initializer-comma.rs @@ -7,7 +7,6 @@ pub struct Foo { fn main() { let _ = Foo { - //~^ ERROR missing field first: true second: 25 //~^ ERROR expected one of diff --git a/tests/ui/suggestions/struct-initializer-comma.stderr b/tests/ui/suggestions/struct-initializer-comma.stderr index 5eff43f32cda1..5fdc95a30cc44 100644 --- a/tests/ui/suggestions/struct-initializer-comma.stderr +++ b/tests/ui/suggestions/struct-initializer-comma.stderr @@ -1,9 +1,8 @@ error: expected one of `,`, `.`, `?`, `}`, or an operator, found `second` - --> $DIR/struct-initializer-comma.rs:12:9 + --> $DIR/struct-initializer-comma.rs:11:9 | LL | let _ = Foo { | --- while parsing this struct -LL | LL | first: true | - | | @@ -12,12 +11,5 @@ LL | first: true LL | second: 25 | ^^^^^^ unexpected token -error[E0063]: missing field `second` in initializer of `Foo` - --> $DIR/struct-initializer-comma.rs:9:13 - | -LL | let _ = Foo { - | ^^^ missing `second` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0063`. diff --git a/tests/ui/trait-bounds/super-assoc-mismatch.stderr b/tests/ui/trait-bounds/super-assoc-mismatch.stderr index 780535283a3a9..11d764d111eed 100644 --- a/tests/ui/trait-bounds/super-assoc-mismatch.stderr +++ b/tests/ui/trait-bounds/super-assoc-mismatch.stderr @@ -77,7 +77,6 @@ help: this trait has no implementations, consider adding one | LL | trait Sub: Super {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = help: see issue #48214 help: add `#![feature(trivial_bounds)]` to the crate attributes to enable | LL + #![feature(trivial_bounds)]