diff --git a/Cargo.lock b/Cargo.lock index b8c8466ffe9b5..15d1d8fe68f29 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3575,6 +3575,7 @@ dependencies = [ "rustc_errors", "rustc_hir", "rustc_index", + "rustc_macros", "rustc_middle", "rustc_query_system", "rustc_session", diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index 85d9687c600dc..bd4ad2873c07c 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -436,6 +436,26 @@ impl Token { || self == &OpenDelim(Delimiter::Parenthesis) } + /// Returns `true` if the token can appear at the start of a item + pub fn can_begin_item(&self) -> bool { + self.is_keyword(kw::Use) + || self.is_keyword(kw::Fn) + || self.is_keyword(kw::Extern) + || self.is_keyword(kw::Crate) + || self.is_keyword(kw::Mod) + || self.is_keyword(kw::Const) + || self.is_keyword(kw::Static) + || self.is_keyword(kw::Trait) + || self.is_keyword(kw::Impl) + || self.is_keyword(kw::Mod) + || self.is_keyword(kw::Type) + || self.is_keyword(kw::Enum) + || self.is_keyword(kw::Struct) + || self.is_keyword(kw::Union) + || self.is_keyword(kw::Macro) + || self == &OpenDelim(Delimiter::Parenthesis) + } + /// Returns `true` if the token is any literal. pub fn is_lit(&self) -> bool { matches!(self.kind, Literal(..)) diff --git a/compiler/rustc_ast_lowering/Cargo.toml b/compiler/rustc_ast_lowering/Cargo.toml index 39ba62ef2ffc5..474aff2e2aac0 100644 --- a/compiler/rustc_ast_lowering/Cargo.toml +++ b/compiler/rustc_ast_lowering/Cargo.toml @@ -15,6 +15,7 @@ rustc_target = { path = "../rustc_target" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_index = { path = "../rustc_index" } rustc_middle = { path = "../rustc_middle" } +rustc_macros = { path = "../rustc_macros" } rustc_query_system = { path = "../rustc_query_system" } rustc_span = { path = "../rustc_span" } rustc_errors = { path = "../rustc_errors" } diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs index 4166b4fc2e5bc..0dba9da63da2a 100644 --- a/compiler/rustc_ast_lowering/src/asm.rs +++ b/compiler/rustc_ast_lowering/src/asm.rs @@ -1,11 +1,17 @@ use crate::{ImplTraitContext, ImplTraitPosition, ParamMode, ResolverAstLoweringExt}; +use super::errors::{ + AbiSpecifiedMultipleTimes, AttSyntaxOnlyX86, ClobberAbiNotSupported, + InlineAsmUnsupportedTarget, InvalidAbiClobberAbi, InvalidAsmTemplateModifierConst, + InvalidAsmTemplateModifierRegClass, InvalidAsmTemplateModifierRegClassSub, + InvalidAsmTemplateModifierSym, InvalidRegister, InvalidRegisterClass, RegisterClassOnlyClobber, + RegisterConflict, +}; use super::LoweringContext; use rustc_ast::ptr::P; use rustc_ast::*; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::definitions::DefPathData; @@ -26,13 +32,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let asm_arch = if self.tcx.sess.opts.actually_rustdoc { None } else { self.tcx.sess.asm_arch }; if asm_arch.is_none() && !self.tcx.sess.opts.actually_rustdoc { - struct_span_err!( - self.tcx.sess, - sp, - E0472, - "inline assembly is unsupported on this target" - ) - .emit(); + self.tcx.sess.emit_err(InlineAsmUnsupportedTarget { span: sp }); } if let Some(asm_arch) = asm_arch { // Inline assembly is currently only stable for these architectures. @@ -59,10 +59,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { && !matches!(asm_arch, Some(asm::InlineAsmArch::X86 | asm::InlineAsmArch::X86_64)) && !self.tcx.sess.opts.actually_rustdoc { - self.tcx - .sess - .struct_span_err(sp, "the `att_syntax` option is only supported on x86") - .emit(); + self.tcx.sess.emit_err(AttSyntaxOnlyX86 { span: sp }); } if asm.options.contains(InlineAsmOptions::MAY_UNWIND) && !self.tcx.features().asm_unwind { feature_err( @@ -82,51 +79,37 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // If the abi was already in the list, emit an error match clobber_abis.get(&abi) { Some((prev_name, prev_sp)) => { - let mut err = self.tcx.sess.struct_span_err( - *abi_span, - &format!("`{}` ABI specified multiple times", prev_name), - ); - err.span_label(*prev_sp, "previously specified here"); - // Multiple different abi names may actually be the same ABI // If the specified ABIs are not the same name, alert the user that they resolve to the same ABI let source_map = self.tcx.sess.source_map(); - if source_map.span_to_snippet(*prev_sp) - != source_map.span_to_snippet(*abi_span) - { - err.note("these ABIs are equivalent on the current target"); - } + let equivalent = (source_map.span_to_snippet(*prev_sp) + != source_map.span_to_snippet(*abi_span)) + .then_some(()); - err.emit(); + self.tcx.sess.emit_err(AbiSpecifiedMultipleTimes { + abi_span: *abi_span, + prev_name: *prev_name, + prev_span: *prev_sp, + equivalent, + }); } None => { - clobber_abis.insert(abi, (abi_name, *abi_span)); + clobber_abis.insert(abi, (*abi_name, *abi_span)); } } } Err(&[]) => { - self.tcx - .sess - .struct_span_err( - *abi_span, - "`clobber_abi` is not supported on this target", - ) - .emit(); + self.tcx.sess.emit_err(ClobberAbiNotSupported { abi_span: *abi_span }); } Err(supported_abis) => { - let mut err = self - .tcx - .sess - .struct_span_err(*abi_span, "invalid ABI for `clobber_abi`"); let mut abis = format!("`{}`", supported_abis[0]); for m in &supported_abis[1..] { let _ = write!(abis, ", `{}`", m); } - err.note(&format!( - "the following ABIs are supported on this target: {}", - abis - )); - err.emit(); + self.tcx.sess.emit_err(InvalidAbiClobberAbi { + abi_span: *abi_span, + supported_abis: abis, + }); } } } @@ -141,24 +124,28 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { .iter() .map(|(op, op_sp)| { let lower_reg = |reg| match reg { - InlineAsmRegOrRegClass::Reg(s) => { + InlineAsmRegOrRegClass::Reg(reg) => { asm::InlineAsmRegOrRegClass::Reg(if let Some(asm_arch) = asm_arch { - asm::InlineAsmReg::parse(asm_arch, s).unwrap_or_else(|e| { - let msg = format!("invalid register `{}`: {}", s, e); - sess.struct_span_err(*op_sp, &msg).emit(); + asm::InlineAsmReg::parse(asm_arch, reg).unwrap_or_else(|error| { + sess.emit_err(InvalidRegister { op_span: *op_sp, reg, error }); asm::InlineAsmReg::Err }) } else { asm::InlineAsmReg::Err }) } - InlineAsmRegOrRegClass::RegClass(s) => { + InlineAsmRegOrRegClass::RegClass(reg_class) => { asm::InlineAsmRegOrRegClass::RegClass(if let Some(asm_arch) = asm_arch { - asm::InlineAsmRegClass::parse(asm_arch, s).unwrap_or_else(|e| { - let msg = format!("invalid register class `{}`: {}", s, e); - sess.struct_span_err(*op_sp, &msg).emit(); - asm::InlineAsmRegClass::Err - }) + asm::InlineAsmRegClass::parse(asm_arch, reg_class).unwrap_or_else( + |error| { + sess.emit_err(InvalidRegisterClass { + op_span: *op_sp, + reg_class, + error, + }); + asm::InlineAsmRegClass::Err + }, + ) } else { asm::InlineAsmRegClass::Err }) @@ -282,50 +269,39 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } let valid_modifiers = class.valid_modifiers(asm_arch.unwrap()); if !valid_modifiers.contains(&modifier) { - let mut err = sess.struct_span_err( - placeholder_span, - "invalid asm template modifier for this register class", - ); - err.span_label(placeholder_span, "template modifier"); - err.span_label(op_sp, "argument"); - if !valid_modifiers.is_empty() { + let sub = if !valid_modifiers.is_empty() { let mut mods = format!("`{}`", valid_modifiers[0]); for m in &valid_modifiers[1..] { let _ = write!(mods, ", `{}`", m); } - err.note(&format!( - "the `{}` register class supports \ - the following template modifiers: {}", - class.name(), - mods - )); + InvalidAsmTemplateModifierRegClassSub::SupportModifier { + class_name: class.name(), + modifiers: mods, + } } else { - err.note(&format!( - "the `{}` register class does not support template modifiers", - class.name() - )); - } - err.emit(); + InvalidAsmTemplateModifierRegClassSub::DoesNotSupportModifier { + class_name: class.name(), + } + }; + sess.emit_err(InvalidAsmTemplateModifierRegClass { + placeholder_span, + op_span: op_sp, + sub, + }); } } hir::InlineAsmOperand::Const { .. } => { - let mut err = sess.struct_span_err( + sess.emit_err(InvalidAsmTemplateModifierConst { placeholder_span, - "asm template modifiers are not allowed for `const` arguments", - ); - err.span_label(placeholder_span, "template modifier"); - err.span_label(op_sp, "argument"); - err.emit(); + op_span: op_sp, + }); } hir::InlineAsmOperand::SymFn { .. } | hir::InlineAsmOperand::SymStatic { .. } => { - let mut err = sess.struct_span_err( + sess.emit_err(InvalidAsmTemplateModifierSym { placeholder_span, - "asm template modifiers are not allowed for `sym` arguments", - ); - err.span_label(placeholder_span, "template modifier"); - err.span_label(op_sp, "argument"); - err.emit(); + op_span: op_sp, + }); } } } @@ -346,12 +322,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // require that the operand name an explicit register, not a // register class. if reg_class.is_clobber_only(asm_arch.unwrap()) && !op.is_clobber() { - let msg = format!( - "register class `{}` can only be used as a clobber, \ - not as an input or output", - reg_class.name() - ); - sess.struct_span_err(op_sp, &msg).emit(); + sess.emit_err(RegisterClassOnlyClobber { + op_span: op_sp, + reg_class_name: reg_class.name(), + }); continue; } @@ -391,16 +365,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { unreachable!(); }; - let msg = format!( - "register `{}` conflicts with register `{}`", - reg.name(), - reg2.name() - ); - let mut err = sess.struct_span_err(op_sp, &msg); - err.span_label(op_sp, &format!("register `{}`", reg.name())); - err.span_label(op_sp2, &format!("register `{}`", reg2.name())); - - match (op, op2) { + let in_out = match (op, op2) { ( hir::InlineAsmOperand::In { .. }, hir::InlineAsmOperand::Out { late, .. }, @@ -411,14 +376,18 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ) => { assert!(!*late); let out_op_sp = if input { op_sp2 } else { op_sp }; - let msg = "use `lateout` instead of \ - `out` to avoid conflict"; - err.span_help(out_op_sp, msg); - } - _ => {} - } + Some(out_op_sp) + }, + _ => None, + }; - err.emit(); + sess.emit_err(RegisterConflict { + op_span1: op_sp, + op_span2: op_sp2, + reg1_name: reg.name(), + reg2_name: reg2.name(), + in_out + }); } Entry::Vacant(v) => { if r == reg { diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs new file mode 100644 index 0000000000000..70ddb80edc160 --- /dev/null +++ b/compiler/rustc_ast_lowering/src/errors.rs @@ -0,0 +1,329 @@ +use rustc_errors::{fluent, AddSubdiagnostic, Applicability, Diagnostic, DiagnosticArgFromDisplay}; +use rustc_macros::{SessionDiagnostic, SessionSubdiagnostic}; +use rustc_span::{symbol::Ident, Span, Symbol}; + +#[derive(SessionDiagnostic, Clone, Copy)] +#[error(ast_lowering::generic_type_with_parentheses, code = "E0214")] +pub struct GenericTypeWithParentheses { + #[primary_span] + #[label] + pub span: Span, + #[subdiagnostic] + pub sub: Option, +} + +#[derive(Clone, Copy)] +pub struct UseAngleBrackets { + pub open_param: Span, + pub close_param: Span, +} + +impl AddSubdiagnostic for UseAngleBrackets { + fn add_to_diagnostic(self, diag: &mut Diagnostic) { + diag.multipart_suggestion( + fluent::ast_lowering::use_angle_brackets, + vec![(self.open_param, String::from("<")), (self.close_param, String::from(">"))], + Applicability::MaybeIncorrect, + ); + } +} + +#[derive(SessionDiagnostic)] +#[help] +#[error(ast_lowering::invalid_abi, code = "E0703")] +pub struct InvalidAbi { + #[primary_span] + #[label] + pub span: Span, + pub abi: Symbol, + pub valid_abis: String, +} + +#[derive(SessionDiagnostic, Clone, Copy)] +#[error(ast_lowering::assoc_ty_parentheses)] +pub struct AssocTyParentheses { + #[primary_span] + pub span: Span, + #[subdiagnostic] + pub sub: AssocTyParenthesesSub, +} + +#[derive(Clone, Copy)] +pub enum AssocTyParenthesesSub { + Empty { parentheses_span: Span }, + NotEmpty { open_param: Span, close_param: Span }, +} + +impl AddSubdiagnostic for AssocTyParenthesesSub { + fn add_to_diagnostic(self, diag: &mut Diagnostic) { + match self { + Self::Empty { parentheses_span } => diag.multipart_suggestion( + fluent::ast_lowering::remove_parentheses, + vec![(parentheses_span, String::new())], + Applicability::MaybeIncorrect, + ), + Self::NotEmpty { open_param, close_param } => diag.multipart_suggestion( + fluent::ast_lowering::use_angle_brackets, + vec![(open_param, String::from("<")), (close_param, String::from(">"))], + Applicability::MaybeIncorrect, + ), + }; + } +} + +#[derive(SessionDiagnostic)] +#[error(ast_lowering::misplaced_impl_trait, code = "E0562")] +pub struct MisplacedImplTrait<'a> { + #[primary_span] + pub span: Span, + pub position: DiagnosticArgFromDisplay<'a>, +} + +#[derive(SessionDiagnostic, Clone, Copy)] +#[error(ast_lowering::rustc_box_attribute_error)] +pub struct RustcBoxAttributeError { + #[primary_span] + pub span: Span, +} + +#[derive(SessionDiagnostic, Clone, Copy)] +#[error(ast_lowering::underscore_expr_lhs_assign)] +pub struct UnderscoreExprLhsAssign { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(SessionDiagnostic, Clone, Copy)] +#[error(ast_lowering::base_expression_double_dot)] +pub struct BaseExpressionDoubleDot { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(SessionDiagnostic, Clone, Copy)] +#[error(ast_lowering::await_only_in_async_fn_and_blocks, code = "E0728")] +pub struct AwaitOnlyInAsyncFnAndBlocks { + #[primary_span] + #[label] + pub dot_await_span: Span, + #[label(ast_lowering::this_not_async)] + pub item_span: Option, +} + +#[derive(SessionDiagnostic, Clone, Copy)] +#[error(ast_lowering::generator_too_many_parameters, code = "E0628")] +pub struct GeneratorTooManyParameters { + #[primary_span] + pub fn_decl_span: Span, +} + +#[derive(SessionDiagnostic, Clone, Copy)] +#[error(ast_lowering::closure_cannot_be_static, code = "E0697")] +pub struct ClosureCannotBeStatic { + #[primary_span] + pub fn_decl_span: Span, +} + +#[derive(SessionDiagnostic, Clone, Copy)] +#[help] +#[error(ast_lowering::async_non_move_closure_not_supported, code = "E0708")] +pub struct AsyncNonMoveClosureNotSupported { + #[primary_span] + pub fn_decl_span: Span, +} + +#[derive(SessionDiagnostic, Clone, Copy)] +#[error(ast_lowering::functional_record_update_destructuring_assignment)] +pub struct FunctionalRecordUpdateDestructuringAssignemnt { + #[primary_span] + #[suggestion(code = "", applicability = "machine-applicable")] + pub span: Span, +} + +#[derive(SessionDiagnostic, Clone, Copy)] +#[error(ast_lowering::async_generators_not_supported, code = "E0727")] +pub struct AsyncGeneratorsNotSupported { + #[primary_span] + pub span: Span, +} + +#[derive(SessionDiagnostic, Clone, Copy)] +#[error(ast_lowering::inline_asm_unsupported_target, code = "E0472")] +pub struct InlineAsmUnsupportedTarget { + #[primary_span] + pub span: Span, +} + +#[derive(SessionDiagnostic, Clone, Copy)] +#[error(ast_lowering::att_syntax_only_x86)] +pub struct AttSyntaxOnlyX86 { + #[primary_span] + pub span: Span, +} + +#[derive(SessionDiagnostic, Clone, Copy)] +#[error(ast_lowering::abi_specified_multiple_times)] +pub struct AbiSpecifiedMultipleTimes { + #[primary_span] + pub abi_span: Span, + pub prev_name: Symbol, + #[label] + pub prev_span: Span, + #[note] + pub equivalent: Option<()>, +} + +#[derive(SessionDiagnostic, Clone, Copy)] +#[error(ast_lowering::clobber_abi_not_supported)] +pub struct ClobberAbiNotSupported { + #[primary_span] + pub abi_span: Span, +} + +#[derive(SessionDiagnostic)] +#[note] +#[error(ast_lowering::invalid_abi_clobber_abi)] +pub struct InvalidAbiClobberAbi { + #[primary_span] + pub abi_span: Span, + pub supported_abis: String, +} + +#[derive(SessionDiagnostic, Clone, Copy)] +#[error(ast_lowering::invalid_register)] +pub struct InvalidRegister<'a> { + #[primary_span] + pub op_span: Span, + pub reg: Symbol, + pub error: &'a str, +} + +#[derive(SessionDiagnostic, Clone, Copy)] +#[error(ast_lowering::invalid_register_class)] +pub struct InvalidRegisterClass<'a> { + #[primary_span] + pub op_span: Span, + pub reg_class: Symbol, + pub error: &'a str, +} + +#[derive(SessionDiagnostic)] +#[error(ast_lowering::invalid_asm_template_modifier_reg_class)] +pub struct InvalidAsmTemplateModifierRegClass { + #[primary_span] + #[label(ast_lowering::template_modifier)] + pub placeholder_span: Span, + #[label(ast_lowering::argument)] + pub op_span: Span, + #[subdiagnostic] + pub sub: InvalidAsmTemplateModifierRegClassSub, +} + +#[derive(SessionSubdiagnostic)] +pub enum InvalidAsmTemplateModifierRegClassSub { + #[note(ast_lowering::support_modifiers)] + SupportModifier { class_name: Symbol, modifiers: String }, + #[note(ast_lowering::does_not_support_modifiers)] + DoesNotSupportModifier { class_name: Symbol }, +} + +#[derive(SessionDiagnostic, Clone, Copy)] +#[error(ast_lowering::invalid_asm_template_modifier_const)] +pub struct InvalidAsmTemplateModifierConst { + #[primary_span] + #[label(ast_lowering::template_modifier)] + pub placeholder_span: Span, + #[label(ast_lowering::argument)] + pub op_span: Span, +} + +#[derive(SessionDiagnostic, Clone, Copy)] +#[error(ast_lowering::invalid_asm_template_modifier_sym)] +pub struct InvalidAsmTemplateModifierSym { + #[primary_span] + #[label(ast_lowering::template_modifier)] + pub placeholder_span: Span, + #[label(ast_lowering::argument)] + pub op_span: Span, +} + +#[derive(SessionDiagnostic, Clone, Copy)] +#[error(ast_lowering::register_class_only_clobber)] +pub struct RegisterClassOnlyClobber { + #[primary_span] + pub op_span: Span, + pub reg_class_name: Symbol, +} + +#[derive(SessionDiagnostic, Clone, Copy)] +#[error(ast_lowering::register_conflict)] +pub struct RegisterConflict<'a> { + #[primary_span] + #[label(ast_lowering::register1)] + pub op_span1: Span, + #[label(ast_lowering::register2)] + pub op_span2: Span, + pub reg1_name: &'a str, + pub reg2_name: &'a str, + #[help] + pub in_out: Option, +} + +#[derive(SessionDiagnostic, Clone, Copy)] +#[help] +#[error(ast_lowering::sub_tuple_binding)] +pub struct SubTupleBinding<'a> { + #[primary_span] + #[label] + #[suggestion_verbose( + ast_lowering::sub_tuple_binding_suggestion, + code = "..", + applicability = "maybe-incorrect" + )] + pub span: Span, + pub ident: Ident, + pub ident_name: Symbol, + pub ctx: &'a str, +} + +#[derive(SessionDiagnostic, Clone, Copy)] +#[error(ast_lowering::extra_double_dot)] +pub struct ExtraDoubleDot<'a> { + #[primary_span] + #[label] + pub span: Span, + #[label(ast_lowering::previously_used_here)] + pub prev_span: Span, + pub ctx: &'a str, +} + +#[derive(SessionDiagnostic, Clone, Copy)] +#[note] +#[error(ast_lowering::misplaced_double_dot)] +pub struct MisplacedDoubleDot { + #[primary_span] + pub span: Span, +} + +#[derive(SessionDiagnostic, Clone, Copy)] +#[error(ast_lowering::misplaced_relax_trait_bound)] +pub struct MisplacedRelaxTraitBound { + #[primary_span] + pub span: Span, +} + +#[derive(SessionDiagnostic, Clone, Copy)] +#[error(ast_lowering::not_supported_for_lifetime_binder_async_closure)] +pub struct NotSupportedForLifetimeBinderAsyncClosure { + #[primary_span] + pub span: Span, +} + +#[derive(SessionDiagnostic, Clone, Copy)] +#[error(ast_lowering::arbitrary_expression_in_pattern)] +pub struct ArbitraryExpressionInPattern { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 32dbd2ff47d6b..ee164aae70ebc 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -1,3 +1,9 @@ +use super::errors::{ + AsyncGeneratorsNotSupported, AsyncNonMoveClosureNotSupported, AwaitOnlyInAsyncFnAndBlocks, + BaseExpressionDoubleDot, ClosureCannotBeStatic, FunctionalRecordUpdateDestructuringAssignemnt, + GeneratorTooManyParameters, NotSupportedForLifetimeBinderAsyncClosure, RustcBoxAttributeError, + UnderscoreExprLhsAssign, +}; use super::ResolverAstLoweringExt; use super::{ImplTraitContext, LoweringContext, ParamMode, ParenthesizedGenericArgs}; use crate::{FnDeclKind, ImplTraitPosition}; @@ -7,7 +13,6 @@ use rustc_ast::ptr::P as AstP; use rustc_ast::*; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_data_structures::thin_vec::ThinVec; -use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::definitions::DefPathData; @@ -46,13 +51,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let hir_id = self.lower_node_id(e.id); return hir::Expr { hir_id, kind, span: self.lower_span(e.span) }; } else { - self.tcx.sess - .struct_span_err( - e.span, - "#[rustc_box] requires precisely one argument \ - and no other attributes are allowed", - ) - .emit(); + self.tcx.sess.emit_err(RustcBoxAttributeError { span: e.span }); hir::ExprKind::Err } } else if let Some(legacy_args) = self.resolver.legacy_const_generic_args(f) { @@ -212,13 +211,7 @@ impl<'hir> LoweringContext<'_, 'hir> { self.lower_expr_range(e.span, e1.as_deref(), e2.as_deref(), lims) } ExprKind::Underscore => { - self.tcx - .sess.struct_span_err( - e.span, - "in expressions, `_` can only be used on the left-hand side of an assignment", - ) - .span_label(e.span, "`_` not allowed here") - .emit(); + self.tcx.sess.emit_err(UnderscoreExprLhsAssign { span: e.span }); hir::ExprKind::Err } ExprKind::Path(ref qself, ref path) => { @@ -250,11 +243,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let rest = match &se.rest { StructRest::Base(e) => Some(self.lower_expr(e)), StructRest::Rest(sp) => { - self.tcx - .sess - .struct_span_err(*sp, "base expression required after `..`") - .span_label(*sp, "add a base expression here") - .emit(); + self.tcx.sess.emit_err(BaseExpressionDoubleDot { span: *sp }); Some(&*self.arena.alloc(self.expr_err(*sp))) } StructRest::None => None, @@ -663,17 +652,10 @@ impl<'hir> LoweringContext<'_, 'hir> { match self.generator_kind { Some(hir::GeneratorKind::Async(_)) => {} Some(hir::GeneratorKind::Gen) | None => { - let mut err = struct_span_err!( - self.tcx.sess, + self.tcx.sess.emit_err(AwaitOnlyInAsyncFnAndBlocks { dot_await_span, - E0728, - "`await` is only allowed inside `async` functions and blocks" - ); - err.span_label(dot_await_span, "only allowed inside `async` functions and blocks"); - if let Some(item_sp) = self.current_item { - err.span_label(item_sp, "this is not `async`"); - } - err.emit(); + item_span: self.current_item, + }); } } let span = self.mark_span_with_reason(DesugaringKind::Await, dot_await_span, None); @@ -893,13 +875,7 @@ impl<'hir> LoweringContext<'_, 'hir> { match generator_kind { Some(hir::GeneratorKind::Gen) => { if decl.inputs.len() > 1 { - struct_span_err!( - self.tcx.sess, - fn_decl_span, - E0628, - "too many parameters for a generator (expected 0 or 1 parameters)" - ) - .emit(); + self.tcx.sess.emit_err(GeneratorTooManyParameters { fn_decl_span }); } Some(movability) } @@ -908,13 +884,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } None => { if movability == Movability::Static { - struct_span_err!( - self.tcx.sess, - fn_decl_span, - E0697, - "closures cannot be static" - ) - .emit(); + self.tcx.sess.emit_err(ClosureCannotBeStatic { fn_decl_span }); } None } @@ -947,10 +917,7 @@ impl<'hir> LoweringContext<'_, 'hir> { fn_decl_span: Span, ) -> hir::ExprKind<'hir> { if let &ClosureBinder::For { span, .. } = binder { - self.tcx.sess.span_err( - span, - "`for<...>` binders on `async` closures are not currently supported", - ); + self.tcx.sess.emit_err(NotSupportedForLifetimeBinderAsyncClosure { span }); } let (binder_clause, generic_params) = self.lower_closure_binder(binder); @@ -961,17 +928,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let body = self.with_new_scopes(|this| { // FIXME(cramertj): allow `async` non-`move` closures with arguments. if capture_clause == CaptureBy::Ref && !decl.inputs.is_empty() { - struct_span_err!( - this.tcx.sess, - fn_decl_span, - E0708, - "`async` non-`move` closures with parameters are not currently supported", - ) - .help( - "consider using `let` statements to manually capture \ - variables by reference before entering an `async move` closure", - ) - .emit(); + this.tcx.sess.emit_err(AsyncNonMoveClosureNotSupported { fn_decl_span }); } // Transform `async |x: u8| -> X { ... }` into @@ -1211,20 +1168,9 @@ impl<'hir> LoweringContext<'_, 'hir> { ); let fields_omitted = match &se.rest { StructRest::Base(e) => { - self.tcx - .sess - .struct_span_err( - e.span, - "functional record updates are not allowed in destructuring \ - assignments", - ) - .span_suggestion( - e.span, - "consider removing the trailing pattern", - "", - rustc_errors::Applicability::MachineApplicable, - ) - .emit(); + self.tcx.sess.emit_err(FunctionalRecordUpdateDestructuringAssignemnt { + span: e.span, + }); true } StructRest::Rest(_) => true, @@ -1421,13 +1367,7 @@ impl<'hir> LoweringContext<'_, 'hir> { match self.generator_kind { Some(hir::GeneratorKind::Gen) => {} Some(hir::GeneratorKind::Async(_)) => { - struct_span_err!( - self.tcx.sess, - span, - E0727, - "`async` generators are not yet supported" - ) - .emit(); + self.tcx.sess.emit_err(AsyncGeneratorsNotSupported { span }); } None => self.generator_kind = Some(hir::GeneratorKind::Gen), } diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 0f1bab24f96ca..fd338ffc0c5e8 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -1,3 +1,4 @@ +use super::errors::{InvalidAbi, MisplacedRelaxTraitBound}; use super::ResolverAstLoweringExt; use super::{AstOwner, ImplTraitContext, ImplTraitPosition}; use super::{FnDeclKind, LoweringContext, ParamMode}; @@ -7,7 +8,6 @@ use rustc_ast::visit::AssocCtxt; use rustc_ast::*; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sorted_map::SortedMap; -use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID}; @@ -1260,10 +1260,11 @@ impl<'hir> LoweringContext<'_, 'hir> { } fn error_on_invalid_abi(&self, abi: StrLit) { - struct_span_err!(self.tcx.sess, abi.span, E0703, "invalid ABI: found `{}`", abi.symbol) - .span_label(abi.span, "invalid ABI") - .help(&format!("valid ABIs: {}", abi::all_names().join(", "))) - .emit(); + self.tcx.sess.emit_err(InvalidAbi { + span: abi.span, + abi: abi.symbol, + valid_abis: abi::all_names().join(", "), + }); } fn lower_asyncness(&mut self, a: Async) -> hir::IsAsync { @@ -1338,11 +1339,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } let is_param = *is_param.get_or_insert_with(compute_is_param); if !is_param { - self.diagnostic().span_err( - bound.span(), - "`?Trait` bounds are only permitted at the \ - point where a type parameter is declared", - ); + self.tcx.sess.emit_err(MisplacedRelaxTraitBound { span: bound.span() }); } } } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 1ac1d689efbdb..ed28f81f88ead 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -39,6 +39,8 @@ #[macro_use] extern crate tracing; +use crate::errors::{AssocTyParentheses, AssocTyParenthesesSub, MisplacedImplTrait}; + use rustc_ast::ptr::P; use rustc_ast::visit; use rustc_ast::{self as ast, *}; @@ -49,7 +51,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::Lrc; -use rustc_errors::{struct_span_err, Applicability, Handler, StashKey}; +use rustc_errors::{DiagnosticArgFromDisplay, Handler, StashKey}; use rustc_hir as hir; use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res}; use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID}; @@ -75,6 +77,7 @@ macro_rules! arena_vec { mod asm; mod block; +mod errors; mod expr; mod index; mod item; @@ -1070,19 +1073,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } fn emit_bad_parenthesized_trait_in_assoc_ty(&self, data: &ParenthesizedArgs) { - let mut err = self.tcx.sess.struct_span_err( - data.span, - "parenthesized generic arguments cannot be used in associated type constraints", - ); // Suggest removing empty parentheses: "Trait()" -> "Trait" - if data.inputs.is_empty() { + let sub = if data.inputs.is_empty() { let parentheses_span = data.inputs_span.shrink_to_lo().to(data.inputs_span.shrink_to_hi()); - err.multipart_suggestion( - "remove these parentheses", - vec![(parentheses_span, String::new())], - Applicability::MaybeIncorrect, - ); + AssocTyParenthesesSub::Empty { parentheses_span } } // Suggest replacing parentheses with angle brackets `Trait(params...)` to `Trait` else { @@ -1096,13 +1091,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // End of last argument to end of parameters let close_param = data.inputs.last().unwrap().span.shrink_to_hi().to(data.inputs_span.shrink_to_hi()); - err.multipart_suggestion( - &format!("use angle brackets instead",), - vec![(open_param, String::from("<")), (close_param, String::from(">"))], - Applicability::MaybeIncorrect, - ); - } - err.emit(); + AssocTyParenthesesSub::NotEmpty { open_param, close_param } + }; + self.tcx.sess.emit_err(AssocTyParentheses { span: data.span, sub }); } #[instrument(level = "debug", skip(self))] @@ -1341,14 +1332,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { path } ImplTraitContext::Disallowed(position) => { - let mut err = struct_span_err!( - self.tcx.sess, - t.span, - E0562, - "`impl Trait` only allowed in function and inherent method return types, not in {}", - position - ); - err.emit(); + self.tcx.sess.emit_err(MisplacedImplTrait { + span: t.span, + position: DiagnosticArgFromDisplay(&position), + }); hir::TyKind::Err } } diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index 51f67e505f4ee..1efa19a3a8286 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -1,3 +1,6 @@ +use super::errors::{ + ArbitraryExpressionInPattern, ExtraDoubleDot, MisplacedDoubleDot, SubTupleBinding, +}; use super::ResolverAstLoweringExt; use super::{ImplTraitContext, LoweringContext, ParamMode}; use crate::ImplTraitPosition; @@ -5,7 +8,6 @@ use crate::ImplTraitPosition; use rustc_ast::ptr::P; use rustc_ast::*; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::Res; use rustc_span::symbol::Ident; @@ -134,20 +136,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // This is not allowed as a sub-tuple pattern PatKind::Ident(ref _bm, ident, Some(ref sub)) if sub.is_rest() => { let sp = pat.span; - self.diagnostic() - .struct_span_err( - sp, - &format!("`{} @` is not allowed in a {}", ident.name, ctx), - ) - .span_label(sp, "this is only allowed in slice patterns") - .help("remove this and bind each tuple field independently") - .span_suggestion_verbose( - sp, - &format!("if you don't need to use the contents of {}, discard the tuple's remaining fields", ident), - "..", - Applicability::MaybeIncorrect, - ) - .emit(); + self.tcx.sess.emit_err(SubTupleBinding { + span: sp, + ident_name: ident.name, + ident, + ctx, + }); } _ => {} } @@ -296,19 +290,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { /// Emit a friendly error for extra `..` patterns in a tuple/tuple struct/slice pattern. pub(crate) fn ban_extra_rest_pat(&self, sp: Span, prev_sp: Span, ctx: &str) { - self.diagnostic() - .struct_span_err(sp, &format!("`..` can only be used once per {} pattern", ctx)) - .span_label(sp, &format!("can only be used once per {} pattern", ctx)) - .span_label(prev_sp, "previously used here") - .emit(); + self.tcx.sess.emit_err(ExtraDoubleDot { span: sp, prev_span: prev_sp, ctx }); } /// Used to ban the `..` pattern in places it shouldn't be semantically. fn ban_illegal_rest_pat(&self, sp: Span) -> hir::PatKind<'hir> { - self.diagnostic() - .struct_span_err(sp, "`..` patterns are not allowed here") - .note("only allowed in tuple, tuple struct, and slice patterns") - .emit(); + self.tcx.sess.emit_err(MisplacedDoubleDot { span: sp }); // We're not in a list context so `..` can be reasonably treated // as `_` because it should always be valid and roughly matches the @@ -345,8 +332,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ExprKind::Path(..) if allow_paths => {} ExprKind::Unary(UnOp::Neg, ref inner) if matches!(inner.kind, ExprKind::Lit(_)) => {} _ => { - self.diagnostic() - .span_err(expr.span, "arbitrary expressions aren't allowed in patterns"); + self.tcx.sess.emit_err(ArbitraryExpressionInPattern { span: expr.span }); return self.arena.alloc(self.expr_err(expr.span)); } } diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index 393be3b454c37..5874d08a94fe0 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -1,11 +1,11 @@ use crate::ImplTraitPosition; +use super::errors::{GenericTypeWithParentheses, UseAngleBrackets}; use super::ResolverAstLoweringExt; use super::{GenericArgsCtor, LifetimeRes, ParenthesizedGenericArgs}; use super::{ImplTraitContext, LoweringContext, ParamMode}; use rustc_ast::{self as ast, *}; -use rustc_errors::{struct_span_err, Applicability}; use rustc_hir as hir; use rustc_hir::def::{DefKind, PartialRes, Res}; use rustc_hir::GenericArg; @@ -185,7 +185,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ) -> hir::PathSegment<'hir> { debug!("path_span: {:?}, lower_path_segment(segment: {:?})", path_span, segment,); let (mut generic_args, infer_args) = if let Some(ref generic_args) = segment.args { - let msg = "parenthesized type parameters may only be used with a `Fn` trait"; match **generic_args { GenericArgs::AngleBracketed(ref data) => { self.lower_angle_bracketed_parameter_data(data, param_mode, itctx) @@ -193,10 +192,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { GenericArgs::Parenthesized(ref data) => match parenthesized_generic_args { ParenthesizedGenericArgs::Ok => self.lower_parenthesized_parameter_data(data), ParenthesizedGenericArgs::Err => { - let mut err = struct_span_err!(self.tcx.sess, data.span, E0214, "{}", msg); - err.span_label(data.span, "only `Fn` traits may use parentheses"); // Suggest replacing parentheses with angle brackets `Trait(params...)` to `Trait` - if !data.inputs.is_empty() { + let sub = if !data.inputs.is_empty() { // Start of the span to the 1st character of 1st argument let open_param = data.inputs_span.shrink_to_lo().to(data .inputs @@ -212,16 +209,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { .span .shrink_to_hi() .to(data.inputs_span.shrink_to_hi()); - err.multipart_suggestion( - &format!("use angle brackets instead",), - vec![ - (open_param, String::from("<")), - (close_param, String::from(">")), - ], - Applicability::MaybeIncorrect, - ); - } - err.emit(); + + Some(UseAngleBrackets { open_param, close_param }) + } else { + None + }; + self.tcx.sess.emit_err(GenericTypeWithParentheses { span: data.span, sub }); ( self.lower_angle_bracketed_parameter_data( &data.as_angle_bracketed_args(), diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 97eee56f94807..68fca08101891 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -2,10 +2,10 @@ use rustc_ast as ast; use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor}; use rustc_ast::{AssocConstraint, AssocConstraintKind, NodeId}; use rustc_ast::{PatKind, RangeEnd, VariantData}; -use rustc_errors::{struct_span_err, Applicability}; +use rustc_errors::{struct_span_err, Applicability, StashKey}; +use rustc_feature::Features; use rustc_feature::{AttributeGate, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP}; -use rustc_feature::{Features, GateIssue}; -use rustc_session::parse::{feature_err, feature_err_issue}; +use rustc_session::parse::{feature_err, feature_warn}; use rustc_session::Session; use rustc_span::source_map::Spanned; use rustc_span::symbol::sym; @@ -20,9 +20,7 @@ macro_rules! gate_feature_fn { let has_feature: bool = has_feature(visitor.features); debug!("gate_feature(feature = {:?}, span = {:?}); has? {}", name, span, has_feature); if !has_feature && !span.allows_unstable($name) { - feature_err_issue(&visitor.sess.parse_sess, name, span, GateIssue::Language, explain) - .help(help) - .emit(); + feature_err(&visitor.sess.parse_sess, name, span, explain).help(help).emit(); } }}; ($visitor: expr, $has_feature: expr, $span: expr, $name: expr, $explain: expr) => {{ @@ -31,8 +29,19 @@ macro_rules! gate_feature_fn { let has_feature: bool = has_feature(visitor.features); debug!("gate_feature(feature = {:?}, span = {:?}); has? {}", name, span, has_feature); if !has_feature && !span.allows_unstable($name) { - feature_err_issue(&visitor.sess.parse_sess, name, span, GateIssue::Language, explain) - .emit(); + feature_err(&visitor.sess.parse_sess, name, span, explain).emit(); + } + }}; + (future_incompatible; $visitor: expr, $has_feature: expr, $span: expr, $name: expr, $explain: expr) => {{ + let (visitor, has_feature, span, name, explain) = + (&*$visitor, $has_feature, $span, $name, $explain); + let has_feature: bool = has_feature(visitor.features); + debug!( + "gate_feature(feature = {:?}, span = {:?}); has? {} (future_incompatible)", + name, span, has_feature + ); + if !has_feature && !span.allows_unstable($name) { + feature_warn(&visitor.sess.parse_sess, name, span, explain); } }}; } @@ -44,6 +53,9 @@ macro_rules! gate_feature_post { ($visitor: expr, $feature: ident, $span: expr, $explain: expr) => { gate_feature_fn!($visitor, |x: &Features| x.$feature, $span, sym::$feature, $explain) }; + (future_incompatible; $visitor: expr, $feature: ident, $span: expr, $explain: expr) => { + gate_feature_fn!(future_incompatible; $visitor, |x: &Features| x.$feature, $span, sym::$feature, $explain) + }; } pub fn check_attribute(attr: &ast::Attribute, sess: &Session, features: &Features) { @@ -588,11 +600,10 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { { // When we encounter a statement of the form `foo: Ty = val;`, this will emit a type // ascription error, but the likely intention was to write a `let` statement. (#78907). - feature_err_issue( + feature_err( &self.sess.parse_sess, sym::type_ascription, lhs.span, - GateIssue::Language, "type ascription is experimental", ).span_suggestion_verbose( lhs.span.shrink_to_lo(), @@ -615,15 +626,22 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { ); } ast::ExprKind::Type(..) => { - // To avoid noise about type ascription in common syntax errors, only emit if it - // is the *only* error. if self.sess.parse_sess.span_diagnostic.err_count() == 0 { + // To avoid noise about type ascription in common syntax errors, + // only emit if it is the *only* error. gate_feature_post!( &self, type_ascription, e.span, "type ascription is experimental" ); + } else { + // And if it isn't, cancel the early-pass warning. + self.sess + .parse_sess + .span_diagnostic + .steal_diagnostic(e.span, StashKey::EarlySyntaxWarning) + .map(|err| err.cancel()); } } ast::ExprKind::TryBlock(_) => { @@ -789,14 +807,12 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) { // All uses of `gate_all!` below this point were added in #65742, // and subsequently disabled (with the non-early gating readded). + // We emit an early future-incompatible warning for these. + // New syntax gates should go above here to get a hard error gate. macro_rules! gate_all { ($gate:ident, $msg:literal) => { - // FIXME(eddyb) do something more useful than always - // disabling these uses of early feature-gatings. - if false { - for span in spans.get(&sym::$gate).unwrap_or(&vec![]) { - gate_feature_post!(&visitor, $gate, *span, $msg); - } + for span in spans.get(&sym::$gate).unwrap_or(&vec![]) { + gate_feature_post!(future_incompatible; &visitor, $gate, *span, $msg); } }; } @@ -809,11 +825,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) { gate_all!(try_blocks, "`try` blocks are unstable"); gate_all!(label_break_value, "labels on blocks are unstable"); gate_all!(box_syntax, "box expression syntax is experimental; you can call `Box::new` instead"); - // To avoid noise about type ascription in common syntax errors, - // only emit if it is the *only* error. (Also check it last.) - if sess.parse_sess.span_diagnostic.err_count() == 0 { - gate_all!(type_ascription, "type ascription is experimental"); - } + gate_all!(type_ascription, "type ascription is experimental"); visit::walk_crate(&mut visitor, krate); } diff --git a/compiler/rustc_error_messages/locales/en-US/ast_lowering.ftl b/compiler/rustc_error_messages/locales/en-US/ast_lowering.ftl new file mode 100644 index 0000000000000..dcb1e2b08306f --- /dev/null +++ b/compiler/rustc_error_messages/locales/en-US/ast_lowering.ftl @@ -0,0 +1,131 @@ +ast_lowering_generic_type_with_parentheses = + parenthesized type parameters may only be used with a `Fn` trait + .label = only `Fn` traits may use parentheses + +ast_lowering_use_angle_brackets = use angle brackets instead + +ast_lowering_invalid_abi = + invalid ABI: found `{$abi}` + .label = invalid ABI + .help = valid ABIs: {$valid_abis} + +ast_lowering_assoc_ty_parentheses = + parenthesized generic arguments cannot be used in associated type constraints + +ast_lowering_remove_parentheses = remove these parentheses + +ast_lowering_misplaced_impl_trait = + `impl Trait` only allowed in function and inherent method return types, not in {$position} + +ast_lowering_rustc_box_attribute_error = + #[rustc_box] requires precisely one argument and no other attributes are allowed + +ast_lowering_underscore_expr_lhs_assign = + in expressions, `_` can only be used on the left-hand side of an assignment + .label = `_` not allowed here + +ast_lowering_base_expression_double_dot = + base expression required after `..` + .label = add a base expression here + +ast_lowering_await_only_in_async_fn_and_blocks = + `await` is only allowed inside `async` functions and blocks + .label = only allowed inside `async` functions and blocks + +ast_lowering_this_not_async = this is not `async` + +ast_lowering_generator_too_many_parameters = + too many parameters for a generator (expected 0 or 1 parameters) + +ast_lowering_closure_cannot_be_static = closures cannot be static + +ast_lowering_async_non_move_closure_not_supported = + `async` non-`move` closures with parameters are not currently supported + .help = consider using `let` statements to manually capture variables by reference before entering an `async move` closure + +ast_lowering_functional_record_update_destructuring_assignment = + functional record updates are not allowed in destructuring assignments + .suggestion = consider removing the trailing pattern + +ast_lowering_async_generators_not_supported = + `async` generators are not yet supported + +ast_lowering_inline_asm_unsupported_target = + inline assembly is unsupported on this target + +ast_lowering_att_syntax_only_x86 = + the `att_syntax` option is only supported on x86 + +ast_lowering_abi_specified_multiple_times = + `{$prev_name}` ABI specified multiple times + .label = previously specified here + .note = these ABIs are equivalent on the current target + +ast_lowering_clobber_abi_not_supported = + `clobber_abi` is not supported on this target + +ast_lowering_invalid_abi_clobber_abi = + invalid ABI for `clobber_abi` + .note = the following ABIs are supported on this target: {$supported_abis} + +ast_lowering_invalid_register = + invalid register `{$reg}`: {$error} + +ast_lowering_invalid_register_class = + invalid register class `{$reg_class}`: {$error} + +ast_lowering_invalid_asm_template_modifier_reg_class = + invalid asm template modifier for this register class + +ast_lowering_argument = argument + +ast_lowering_template_modifier = template modifier + +ast_lowering_support_modifiers = + the `{$class_name}` register class supports the following template modifiers: {$modifiers} + +ast_lowering_does_not_support_modifiers = + the `{$class_name}` register class does not support template modifiers + +ast_lowering_invalid_asm_template_modifier_const = + asm template modifiers are not allowed for `const` arguments + +ast_lowering_invalid_asm_template_modifier_sym = + asm template modifiers are not allowed for `sym` arguments + +ast_lowering_register_class_only_clobber = + register class `{$reg_class_name}` can only be used as a clobber, not as an input or output + +ast_lowering_register_conflict = + register `{$reg1_name}` conflicts with register `{$reg2_name}` + .help = use `lateout` instead of `out` to avoid conflict + +ast_lowering_register1 = register `{$reg1_name}` + +ast_lowering_register2 = register `{$reg2_name}` + +ast_lowering_sub_tuple_binding = + `{$ident_name} @` is not allowed in a {$ctx} + .label = this is only allowed in slice patterns + .help = remove this and bind each tuple field independently + +ast_lowering_sub_tuple_binding_suggestion = if you don't need to use the contents of {$ident}, discard the tuple's remaining fields + +ast_lowering_extra_double_dot = + `..` can only be used once per {$ctx} pattern + .label = can only be used once per {$ctx} pattern + +ast_lowering_previously_used_here = previously used here + +ast_lowering_misplaced_double_dot = + `..` patterns are not allowed here + .note = only allowed in tuple, tuple struct, and slice patterns + +ast_lowering_misplaced_relax_trait_bound = + `?Trait` bounds are only permitted at the point where a type parameter is declared + +ast_lowering_not_supported_for_lifetime_binder_async_closure = + `for<...>` binders on `async` closures are not currently supported + +ast_lowering_arbitrary_expression_in_pattern = + arbitrary expressions aren't allowed in patterns diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index 6ae4dab3a35eb..2856afdbbae8e 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -30,6 +30,7 @@ pub use unic_langid::{langid, LanguageIdentifier}; // Generates `DEFAULT_LOCALE_RESOURCES` static and `fluent_generated` module. fluent_messages! { + ast_lowering => "../locales/en-US/ast_lowering.ftl", borrowck => "../locales/en-US/borrowck.ftl", builtin_macros => "../locales/en-US/builtin_macros.ftl", const_eval => "../locales/en-US/const_eval.ftl", diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 6555b93ac0b1b..18e84b70b1b10 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -459,6 +459,7 @@ struct HandlerInner { pub enum StashKey { ItemNoType, UnderscoreForArrayLengths, + EarlySyntaxWarning, } fn default_track_diagnostic(_: &Diagnostic) {} @@ -626,19 +627,13 @@ impl Handler { /// Stash a given diagnostic with the given `Span` and `StashKey` as the key for later stealing. pub fn stash_diagnostic(&self, span: Span, key: StashKey, diag: Diagnostic) { let mut inner = self.inner.borrow_mut(); - // FIXME(Centril, #69537): Consider reintroducing panic on overwriting a stashed diagnostic - // if/when we have a more robust macro-friendly replacement for `(span, key)` as a key. - // See the PR for a discussion. - inner.stashed_diagnostics.insert((span, key), diag); + inner.stash((span, key), diag); } /// Steal a previously stashed diagnostic with the given `Span` and `StashKey` as the key. pub fn steal_diagnostic(&self, span: Span, key: StashKey) -> Option> { - self.inner - .borrow_mut() - .stashed_diagnostics - .remove(&(span, key)) - .map(|diag| DiagnosticBuilder::new_diagnostic(self, diag)) + let mut inner = self.inner.borrow_mut(); + inner.steal((span, key)).map(|diag| DiagnosticBuilder::new_diagnostic(self, diag)) } /// Emit all stashed diagnostics. @@ -1106,13 +1101,31 @@ impl HandlerInner { /// Emit all stashed diagnostics. fn emit_stashed_diagnostics(&mut self) -> Option { + let has_errors = self.has_errors(); let diags = self.stashed_diagnostics.drain(..).map(|x| x.1).collect::>(); let mut reported = None; for mut diag in diags { + // Decrement the count tracking the stash; emitting will increment it. if diag.is_error() { - reported = Some(ErrorGuaranteed(())); + if matches!(diag.level, Level::Error { lint: true }) { + self.lint_err_count -= 1; + } else { + self.err_count -= 1; + } + } else { + if diag.is_force_warn() { + self.warn_count -= 1; + } else { + // Unless they're forced, don't flush stashed warnings when + // there are errors, to avoid causing warning overload. The + // stash would've been stolen already if it were important. + if has_errors { + continue; + } + } } - self.emit_diagnostic(&mut diag); + let reported_this = self.emit_diagnostic(&mut diag); + reported = reported.or(reported_this); } reported } @@ -1302,9 +1315,47 @@ impl HandlerInner { } } + fn stash(&mut self, key: (Span, StashKey), diagnostic: Diagnostic) { + // Track the diagnostic for counts, but don't panic-if-treat-err-as-bug + // yet; that happens when we actually emit the diagnostic. + if diagnostic.is_error() { + if matches!(diagnostic.level, Level::Error { lint: true }) { + self.lint_err_count += 1; + } else { + self.err_count += 1; + } + } else { + // Warnings are only automatically flushed if they're forced. + if diagnostic.is_force_warn() { + self.warn_count += 1; + } + } + + // FIXME(Centril, #69537): Consider reintroducing panic on overwriting a stashed diagnostic + // if/when we have a more robust macro-friendly replacement for `(span, key)` as a key. + // See the PR for a discussion. + self.stashed_diagnostics.insert(key, diagnostic); + } + + fn steal(&mut self, key: (Span, StashKey)) -> Option { + let diagnostic = self.stashed_diagnostics.remove(&key)?; + if diagnostic.is_error() { + if matches!(diagnostic.level, Level::Error { lint: true }) { + self.lint_err_count -= 1; + } else { + self.err_count -= 1; + } + } else { + if diagnostic.is_force_warn() { + self.warn_count -= 1; + } + } + Some(diagnostic) + } + #[inline] fn err_count(&self) -> usize { - self.err_count + self.stashed_diagnostics.len() + self.err_count } fn has_errors(&self) -> bool { diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 3c7dac2bfd816..8fb5f159136be 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -66,6 +66,7 @@ use rustc_hir::lang_items::LangItem; use rustc_hir::Node; use rustc_middle::dep_graph::DepContext; use rustc_middle::ty::print::with_no_trimmed_paths; +use rustc_middle::ty::relate::{self, RelateResult, TypeRelation}; use rustc_middle::ty::{ self, error::TypeError, Binder, List, Region, Subst, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, @@ -2079,7 +2080,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { diag.span_suggestion( span, *msg, - format!("{}.as_ref()", snippet), + // HACK: fix issue# 100605, suggesting convert from &Option to Option<&T>, remove the extra `&` + format!("{}.as_ref()", snippet.trim_start_matches('&')), Applicability::MachineApplicable, ); } @@ -2660,67 +2662,92 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// Float types, respectively). When comparing two ADTs, these rules apply recursively. pub fn same_type_modulo_infer(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> bool { let (a, b) = self.resolve_vars_if_possible((a, b)); - match (a.kind(), b.kind()) { - (&ty::Adt(def_a, substs_a), &ty::Adt(def_b, substs_b)) => { - if def_a != def_b { - return false; - } + SameTypeModuloInfer(self).relate(a, b).is_ok() + } +} - substs_a - .types() - .zip(substs_b.types()) - .all(|(a, b)| self.same_type_modulo_infer(a, b)) - } - (&ty::FnDef(did_a, substs_a), &ty::FnDef(did_b, substs_b)) => { - if did_a != did_b { - return false; - } +struct SameTypeModuloInfer<'a, 'tcx>(&'a InferCtxt<'a, 'tcx>); - substs_a - .types() - .zip(substs_b.types()) - .all(|(a, b)| self.same_type_modulo_infer(a, b)) - } - (&ty::Int(_) | &ty::Uint(_), &ty::Infer(ty::InferTy::IntVar(_))) +impl<'tcx> TypeRelation<'tcx> for SameTypeModuloInfer<'_, 'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.0.tcx + } + + fn param_env(&self) -> ty::ParamEnv<'tcx> { + // Unused, only for consts which we treat as always equal + ty::ParamEnv::empty() + } + + fn tag(&self) -> &'static str { + "SameTypeModuloInfer" + } + + fn a_is_expected(&self) -> bool { + true + } + + fn relate_with_variance>( + &mut self, + _variance: ty::Variance, + _info: ty::VarianceDiagInfo<'tcx>, + a: T, + b: T, + ) -> relate::RelateResult<'tcx, T> { + self.relate(a, b) + } + + fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + match (a.kind(), b.kind()) { + (ty::Int(_) | ty::Uint(_), ty::Infer(ty::InferTy::IntVar(_))) | ( - &ty::Infer(ty::InferTy::IntVar(_)), - &ty::Int(_) | &ty::Uint(_) | &ty::Infer(ty::InferTy::IntVar(_)), + ty::Infer(ty::InferTy::IntVar(_)), + ty::Int(_) | ty::Uint(_) | ty::Infer(ty::InferTy::IntVar(_)), ) - | (&ty::Float(_), &ty::Infer(ty::InferTy::FloatVar(_))) + | (ty::Float(_), ty::Infer(ty::InferTy::FloatVar(_))) | ( - &ty::Infer(ty::InferTy::FloatVar(_)), - &ty::Float(_) | &ty::Infer(ty::InferTy::FloatVar(_)), + ty::Infer(ty::InferTy::FloatVar(_)), + ty::Float(_) | ty::Infer(ty::InferTy::FloatVar(_)), ) - | (&ty::Infer(ty::InferTy::TyVar(_)), _) - | (_, &ty::Infer(ty::InferTy::TyVar(_))) => true, - (&ty::Ref(_, ty_a, mut_a), &ty::Ref(_, ty_b, mut_b)) => { - mut_a == mut_b && self.same_type_modulo_infer(ty_a, ty_b) - } - (&ty::RawPtr(a), &ty::RawPtr(b)) => { - a.mutbl == b.mutbl && self.same_type_modulo_infer(a.ty, b.ty) - } - (&ty::Slice(a), &ty::Slice(b)) => self.same_type_modulo_infer(a, b), - (&ty::Array(a_ty, a_ct), &ty::Array(b_ty, b_ct)) => { - self.same_type_modulo_infer(a_ty, b_ty) && a_ct == b_ct - } - (&ty::Tuple(a), &ty::Tuple(b)) => { - if a.len() != b.len() { - return false; - } - std::iter::zip(a.iter(), b.iter()).all(|(a, b)| self.same_type_modulo_infer(a, b)) - } - (&ty::FnPtr(a), &ty::FnPtr(b)) => { - let a = a.skip_binder().inputs_and_output; - let b = b.skip_binder().inputs_and_output; - if a.len() != b.len() { - return false; - } - std::iter::zip(a.iter(), b.iter()).all(|(a, b)| self.same_type_modulo_infer(a, b)) - } - // FIXME(compiler-errors): This needs to be generalized more - _ => a == b, + | (ty::Infer(ty::InferTy::TyVar(_)), _) + | (_, ty::Infer(ty::InferTy::TyVar(_))) => Ok(a), + (ty::Infer(_), _) | (_, ty::Infer(_)) => Err(TypeError::Mismatch), + _ => relate::super_relate_tys(self, a, b), } } + + fn regions( + &mut self, + a: ty::Region<'tcx>, + b: ty::Region<'tcx>, + ) -> RelateResult<'tcx, ty::Region<'tcx>> { + if (a.is_var() && b.is_free_or_static()) || (b.is_var() && a.is_free_or_static()) || a == b + { + Ok(a) + } else { + Err(TypeError::Mismatch) + } + } + + fn binders( + &mut self, + a: ty::Binder<'tcx, T>, + b: ty::Binder<'tcx, T>, + ) -> relate::RelateResult<'tcx, ty::Binder<'tcx, T>> + where + T: relate::Relate<'tcx>, + { + Ok(ty::Binder::dummy(self.relate(a.skip_binder(), b.skip_binder())?)) + } + + fn consts( + &mut self, + a: ty::Const<'tcx>, + _b: ty::Const<'tcx>, + ) -> relate::RelateResult<'tcx, ty::Const<'tcx>> { + // FIXME(compiler-errors): This could at least do some first-order + // relation + Ok(a) + } } impl<'a, 'tcx> InferCtxt<'a, 'tcx> { diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index f00165cd3b370..95e34da734d6a 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -3212,6 +3212,56 @@ declare_lint! { }; } +declare_lint! { + /// The `unstable_syntax_pre_expansion` lint detects the use of unstable + /// syntax that is discarded during attribute expansion. + /// + /// ### Example + /// + /// ```rust + /// #[cfg(FALSE)] + /// macro foo() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The input to active attributes such as `#[cfg]` or procedural macro + /// attributes is required to be valid syntax. Previously, the compiler only + /// gated the use of unstable syntax features after resolving `#[cfg]` gates + /// and expanding procedural macros. + /// + /// To avoid relying on unstable syntax, move the use of unstable syntax + /// into a position where the compiler does not parse the syntax, such as a + /// functionlike macro. + /// + /// ```rust + /// # #![deny(unstable_syntax_pre_expansion)] + /// + /// macro_rules! identity { + /// ( $($tokens:tt)* ) => { $($tokens)* } + /// } + /// + /// #[cfg(FALSE)] + /// identity! { + /// macro foo() {} + /// } + /// ``` + /// + /// This is a [future-incompatible] lint to transition this + /// to a hard error in the future. See [issue #65860] for more details. + /// + /// [issue #65860]: https://github.com/rust-lang/rust/issues/65860 + /// [future-incompatible]: ../index.md#future-incompatible-lints + pub UNSTABLE_SYNTAX_PRE_EXPANSION, + Warn, + "unstable syntax can change at any point in the future, causing a hard error!", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #65860 ", + }; +} + declare_lint_pass! { /// Does nothing as a lint pass, but registers some `Lint`s /// that are used by other parts of the compiler. @@ -3280,6 +3330,7 @@ declare_lint_pass! { POINTER_STRUCTURAL_MATCH, NONTRIVIAL_STRUCTURAL_MATCH, SOFT_UNSTABLE, + UNSTABLE_SYNTAX_PRE_EXPANSION, INLINE_NO_SANITIZE, BAD_ASM_STYLE, ASM_SUB_REGISTER, diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 52c3a38861e65..0070575f2136a 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1617,6 +1617,10 @@ impl<'tcx> Region<'tcx> { _ => self.is_free(), } } + + pub fn is_var(self) -> bool { + matches!(self.kind(), ty::ReVar(_)) + } } /// Type utilities diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 93e70e9abdaca..9854beff3a711 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -22,7 +22,7 @@ use rustc_errors::{ use rustc_errors::{pluralize, struct_span_err, Diagnostic, ErrorGuaranteed}; use rustc_macros::{SessionDiagnostic, SessionSubdiagnostic}; use rustc_span::source_map::Spanned; -use rustc_span::symbol::{kw, Ident}; +use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::{Span, SpanSnippetError, DUMMY_SP}; use std::ops::{Deref, DerefMut}; @@ -602,15 +602,13 @@ impl<'a> Parser<'a> { self.last_unexpected_token_span = Some(self.token.span); let mut err = self.struct_span_err(self.token.span, &msg_exp); - if let TokenKind::Ident(symbol, _) = &self.prev_token.kind { - if symbol.as_str() == "public" { - err.span_suggestion_short( - self.prev_token.span, - "write `pub` instead of `public` to make the item public", - "pub", - appl, - ); - } + if self.prev_token.is_ident_named(sym::public) && self.token.can_begin_item() { + err.span_suggestion_short( + self.prev_token.span, + "write `pub` instead of `public` to make the item public", + "pub", + appl, + ); } // Add suggestion for a missing closing angle bracket if '>' is included in expected_tokens diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index f31d52147b47b..9f0886cb2089c 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -2,15 +2,17 @@ //! It also serves as an input to the parser itself. use crate::config::CheckCfg; -use crate::lint::{BufferedEarlyLint, BuiltinLintDiagnostics, Lint, LintId}; +use crate::lint::{ + builtin::UNSTABLE_SYNTAX_PRE_EXPANSION, BufferedEarlyLint, BuiltinLintDiagnostics, Lint, LintId, +}; use crate::SessionDiagnostic; use rustc_ast::node_id::NodeId; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::{Lock, Lrc}; use rustc_errors::{emitter::SilentEmitter, ColorConfig, Handler}; use rustc_errors::{ - error_code, fallback_fluent_bundle, Applicability, Diagnostic, DiagnosticBuilder, - DiagnosticMessage, ErrorGuaranteed, MultiSpan, + error_code, fallback_fluent_bundle, Applicability, Diagnostic, DiagnosticBuilder, DiagnosticId, + DiagnosticMessage, ErrorGuaranteed, MultiSpan, StashKey, }; use rustc_feature::{find_feature_issue, GateIssue, UnstableFeatures}; use rustc_span::edition::Edition; @@ -101,11 +103,58 @@ pub fn feature_err_issue<'a>( issue: GateIssue, explain: &str, ) -> DiagnosticBuilder<'a, ErrorGuaranteed> { + let span = span.into(); + + // Cancel an earlier warning for this same error, if it exists. + if let Some(span) = span.primary_span() { + sess.span_diagnostic + .steal_diagnostic(span, StashKey::EarlySyntaxWarning) + .map(|err| err.cancel()); + } + let mut err = sess.span_diagnostic.struct_span_err_with_code(span, explain, error_code!(E0658)); add_feature_diagnostics_for_issue(&mut err, sess, feature, issue); err } +/// Construct a future incompatibility diagnostic for a feature gate. +/// +/// This diagnostic is only a warning and *does not cause compilation to fail*. +pub fn feature_warn<'a>(sess: &'a ParseSess, feature: Symbol, span: Span, explain: &str) { + feature_warn_issue(sess, feature, span, GateIssue::Language, explain); +} + +/// Construct a future incompatibility diagnostic for a feature gate. +/// +/// This diagnostic is only a warning and *does not cause compilation to fail*. +/// +/// This variant allows you to control whether it is a library or language feature. +/// Almost always, you want to use this for a language feature. If so, prefer `feature_warn`. +pub fn feature_warn_issue<'a>( + sess: &'a ParseSess, + feature: Symbol, + span: Span, + issue: GateIssue, + explain: &str, +) { + let mut err = sess.span_diagnostic.struct_span_warn(span, explain); + add_feature_diagnostics_for_issue(&mut err, sess, feature, issue); + + // Decorate this as a future-incompatibility lint as in rustc_middle::lint::struct_lint_level + let lint = UNSTABLE_SYNTAX_PRE_EXPANSION; + let future_incompatible = lint.future_incompatible.as_ref().unwrap(); + err.code(DiagnosticId::Lint { + name: lint.name_lower(), + has_future_breakage: false, + is_force_warn: false, + }); + err.warn(lint.desc); + err.note(format!("for more information, see {}", future_incompatible.reference)); + + // A later feature_err call can steal and cancel this warning. + err.stash(span, StashKey::EarlySyntaxWarning); +} + /// Adds the diagnostics for a feature to an existing error. pub fn add_feature_diagnostics<'a>(err: &mut Diagnostic, sess: &'a ParseSess, feature: Symbol) { add_feature_diagnostics_for_issue(err, sess, feature, GateIssue::Language); diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 156f53ac48626..4fac74278de5b 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1118,6 +1118,7 @@ symbols! { ptr_offset_from_unsigned, pub_macro_rules, pub_restricted, + public, pure, pushpop_unsafe, qreg, diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index c1ceeb0deb837..346df21383650 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -151,6 +151,8 @@ use core::async_iter::AsyncIterator; use core::borrow; use core::cmp::Ordering; use core::convert::{From, TryFrom}; +#[cfg(not(bootstrap))] +use core::error::Error; use core::fmt; use core::future::Future; use core::hash::{Hash, Hasher}; @@ -174,6 +176,9 @@ use crate::borrow::Cow; use crate::raw_vec::RawVec; #[cfg(not(no_global_oom_handling))] use crate::str::from_boxed_utf8_unchecked; +#[cfg(not(bootstrap))] +#[cfg(not(no_global_oom_handling))] +use crate::string::String; #[cfg(not(no_global_oom_handling))] use crate::vec::Vec; @@ -2085,3 +2090,304 @@ impl AsyncIterator for Box { (**self).size_hint() } } + +#[cfg(not(bootstrap))] +impl dyn Error { + #[inline] + #[stable(feature = "error_downcast", since = "1.3.0")] + #[rustc_allow_incoherent_impl] + /// Attempts to downcast the box to a concrete type. + pub fn downcast(self: Box) -> Result, Box> { + if self.is::() { + unsafe { + let raw: *mut dyn Error = Box::into_raw(self); + Ok(Box::from_raw(raw as *mut T)) + } + } else { + Err(self) + } + } +} + +#[cfg(not(bootstrap))] +impl dyn Error + Send { + #[inline] + #[stable(feature = "error_downcast", since = "1.3.0")] + #[rustc_allow_incoherent_impl] + /// Attempts to downcast the box to a concrete type. + pub fn downcast(self: Box) -> Result, Box> { + let err: Box = self; + ::downcast(err).map_err(|s| unsafe { + // Reapply the `Send` marker. + mem::transmute::, Box>(s) + }) + } +} + +#[cfg(not(bootstrap))] +impl dyn Error + Send + Sync { + #[inline] + #[stable(feature = "error_downcast", since = "1.3.0")] + #[rustc_allow_incoherent_impl] + /// Attempts to downcast the box to a concrete type. + pub fn downcast(self: Box) -> Result, Box> { + let err: Box = self; + ::downcast(err).map_err(|s| unsafe { + // Reapply the `Send + Sync` marker. + mem::transmute::, Box>(s) + }) + } +} + +#[cfg(not(bootstrap))] +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, E: Error + 'a> From for Box { + /// Converts a type of [`Error`] into a box of dyn [`Error`]. + /// + /// # Examples + /// + /// ``` + /// use std::error::Error; + /// use std::fmt; + /// use std::mem; + /// + /// #[derive(Debug)] + /// struct AnError; + /// + /// impl fmt::Display for AnError { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// write!(f, "An error") + /// } + /// } + /// + /// impl Error for AnError {} + /// + /// let an_error = AnError; + /// assert!(0 == mem::size_of_val(&an_error)); + /// let a_boxed_error = Box::::from(an_error); + /// assert!(mem::size_of::>() == mem::size_of_val(&a_boxed_error)) + /// ``` + fn from(err: E) -> Box { + Box::new(err) + } +} + +#[cfg(not(bootstrap))] +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, E: Error + Send + Sync + 'a> From for Box { + /// Converts a type of [`Error`] + [`Send`] + [`Sync`] into a box of + /// dyn [`Error`] + [`Send`] + [`Sync`]. + /// + /// # Examples + /// + /// ``` + /// use std::error::Error; + /// use std::fmt; + /// use std::mem; + /// + /// #[derive(Debug)] + /// struct AnError; + /// + /// impl fmt::Display for AnError { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// write!(f, "An error") + /// } + /// } + /// + /// impl Error for AnError {} + /// + /// unsafe impl Send for AnError {} + /// + /// unsafe impl Sync for AnError {} + /// + /// let an_error = AnError; + /// assert!(0 == mem::size_of_val(&an_error)); + /// let a_boxed_error = Box::::from(an_error); + /// assert!( + /// mem::size_of::>() == mem::size_of_val(&a_boxed_error)) + /// ``` + fn from(err: E) -> Box { + Box::new(err) + } +} + +#[cfg(not(bootstrap))] +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "rust1", since = "1.0.0")] +impl From for Box { + /// Converts a [`String`] into a box of dyn [`Error`] + [`Send`] + [`Sync`]. + /// + /// # Examples + /// + /// ``` + /// use std::error::Error; + /// use std::mem; + /// + /// let a_string_error = "a string error".to_string(); + /// let a_boxed_error = Box::::from(a_string_error); + /// assert!( + /// mem::size_of::>() == mem::size_of_val(&a_boxed_error)) + /// ``` + #[inline] + fn from(err: String) -> Box { + struct StringError(String); + + impl Error for StringError { + #[allow(deprecated)] + fn description(&self) -> &str { + &self.0 + } + } + + impl fmt::Display for StringError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.0, f) + } + } + + // Purposefully skip printing "StringError(..)" + impl fmt::Debug for StringError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.0, f) + } + } + + Box::new(StringError(err)) + } +} + +#[cfg(not(bootstrap))] +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "string_box_error", since = "1.6.0")] +impl From for Box { + /// Converts a [`String`] into a box of dyn [`Error`]. + /// + /// # Examples + /// + /// ``` + /// use std::error::Error; + /// use std::mem; + /// + /// let a_string_error = "a string error".to_string(); + /// let a_boxed_error = Box::::from(a_string_error); + /// assert!(mem::size_of::>() == mem::size_of_val(&a_boxed_error)) + /// ``` + fn from(str_err: String) -> Box { + let err1: Box = From::from(str_err); + let err2: Box = err1; + err2 + } +} + +#[cfg(not(bootstrap))] +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a> From<&str> for Box { + /// Converts a [`str`] into a box of dyn [`Error`] + [`Send`] + [`Sync`]. + /// + /// [`str`]: prim@str + /// + /// # Examples + /// + /// ``` + /// use std::error::Error; + /// use std::mem; + /// + /// let a_str_error = "a str error"; + /// let a_boxed_error = Box::::from(a_str_error); + /// assert!( + /// mem::size_of::>() == mem::size_of_val(&a_boxed_error)) + /// ``` + #[inline] + fn from(err: &str) -> Box { + From::from(String::from(err)) + } +} + +#[cfg(not(bootstrap))] +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "string_box_error", since = "1.6.0")] +impl From<&str> for Box { + /// Converts a [`str`] into a box of dyn [`Error`]. + /// + /// [`str`]: prim@str + /// + /// # Examples + /// + /// ``` + /// use std::error::Error; + /// use std::mem; + /// + /// let a_str_error = "a str error"; + /// let a_boxed_error = Box::::from(a_str_error); + /// assert!(mem::size_of::>() == mem::size_of_val(&a_boxed_error)) + /// ``` + fn from(err: &str) -> Box { + From::from(String::from(err)) + } +} + +#[cfg(not(bootstrap))] +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "cow_box_error", since = "1.22.0")] +impl<'a, 'b> From> for Box { + /// Converts a [`Cow`] into a box of dyn [`Error`] + [`Send`] + [`Sync`]. + /// + /// # Examples + /// + /// ``` + /// use std::error::Error; + /// use std::mem; + /// use std::borrow::Cow; + /// + /// let a_cow_str_error = Cow::from("a str error"); + /// let a_boxed_error = Box::::from(a_cow_str_error); + /// assert!( + /// mem::size_of::>() == mem::size_of_val(&a_boxed_error)) + /// ``` + fn from(err: Cow<'b, str>) -> Box { + From::from(String::from(err)) + } +} + +#[cfg(not(bootstrap))] +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "cow_box_error", since = "1.22.0")] +impl<'a> From> for Box { + /// Converts a [`Cow`] into a box of dyn [`Error`]. + /// + /// # Examples + /// + /// ``` + /// use std::error::Error; + /// use std::mem; + /// use std::borrow::Cow; + /// + /// let a_cow_str_error = Cow::from("a str error"); + /// let a_boxed_error = Box::::from(a_cow_str_error); + /// assert!(mem::size_of::>() == mem::size_of_val(&a_boxed_error)) + /// ``` + fn from(err: Cow<'a, str>) -> Box { + From::from(String::from(err)) + } +} + +#[cfg(not(bootstrap))] +#[stable(feature = "box_error", since = "1.8.0")] +impl core::error::Error for Box { + #[allow(deprecated, deprecated_in_future)] + fn description(&self) -> &str { + core::error::Error::description(&**self) + } + + #[allow(deprecated)] + fn cause(&self) -> Option<&dyn core::error::Error> { + core::error::Error::cause(&**self) + } + + fn source(&self) -> Option<&(dyn core::error::Error + 'static)> { + core::error::Error::source(&**self) + } +} diff --git a/library/alloc/src/boxed/thin.rs b/library/alloc/src/boxed/thin.rs index 649ccfcaa9ed8..0a20c74b00fdc 100644 --- a/library/alloc/src/boxed/thin.rs +++ b/library/alloc/src/boxed/thin.rs @@ -2,6 +2,8 @@ // https://github.com/matthieu-m/rfc2580/blob/b58d1d3cba0d4b5e859d3617ea2d0943aaa31329/examples/thin.rs // by matthieu-m use crate::alloc::{self, Layout, LayoutError}; +#[cfg(not(bootstrap))] +use core::error::Error; use core::fmt::{self, Debug, Display, Formatter}; use core::marker::PhantomData; #[cfg(not(no_global_oom_handling))] @@ -271,3 +273,11 @@ impl WithHeader { Layout::new::().extend(value_layout) } } + +#[cfg(not(bootstrap))] +#[unstable(feature = "thin_box", issue = "92791")] +impl Error for ThinBox { + fn source(&self) -> Option<&(dyn Error + 'static)> { + self.deref().source() + } +} diff --git a/library/alloc/src/collections/btree/map/entry.rs b/library/alloc/src/collections/btree/map/entry.rs index b6eecf9b0e952..cd7cdc1920719 100644 --- a/library/alloc/src/collections/btree/map/entry.rs +++ b/library/alloc/src/collections/btree/map/entry.rs @@ -133,6 +133,17 @@ impl<'a, K: Debug + Ord, V: Debug, A: Allocator + Clone> fmt::Display } } +#[cfg(not(bootstrap))] +#[unstable(feature = "map_try_insert", issue = "82766")] +impl<'a, K: core::fmt::Debug + Ord, V: core::fmt::Debug> core::error::Error + for crate::collections::btree_map::OccupiedError<'a, K, V> +{ + #[allow(deprecated)] + fn description(&self) -> &str { + "key already exists" + } +} + impl<'a, K: Ord, V, A: Allocator + Clone> Entry<'a, K, V, A> { /// Ensures a value is in the entry by inserting the default if empty, and returns /// a mutable reference to the value in the entry. diff --git a/library/alloc/src/collections/mod.rs b/library/alloc/src/collections/mod.rs index 628a5b155673c..21d0def0866ed 100644 --- a/library/alloc/src/collections/mod.rs +++ b/library/alloc/src/collections/mod.rs @@ -152,3 +152,7 @@ trait SpecExtend { /// Extends `self` with the contents of the given iterator. fn spec_extend(&mut self, iter: I); } + +#[cfg(not(bootstrap))] +#[stable(feature = "try_reserve", since = "1.57.0")] +impl core::error::Error for TryReserveError {} diff --git a/library/alloc/src/ffi/c_str.rs b/library/alloc/src/ffi/c_str.rs index ae61b1f1e8ed5..b3c3941be5afb 100644 --- a/library/alloc/src/ffi/c_str.rs +++ b/library/alloc/src/ffi/c_str.rs @@ -1121,3 +1121,29 @@ impl CStr { CString::from(self) } } + +#[cfg(not(bootstrap))] +#[stable(feature = "rust1", since = "1.0.0")] +impl core::error::Error for NulError { + #[allow(deprecated)] + fn description(&self) -> &str { + "nul byte found in data" + } +} + +#[cfg(not(bootstrap))] +#[stable(feature = "cstring_from_vec_with_nul", since = "1.58.0")] +impl core::error::Error for FromVecWithNulError {} + +#[cfg(not(bootstrap))] +#[stable(feature = "cstring_into", since = "1.7.0")] +impl core::error::Error for IntoStringError { + #[allow(deprecated)] + fn description(&self) -> &str { + "C string contained non-utf8 bytes" + } + + fn source(&self) -> Option<&(dyn core::error::Error + 'static)> { + Some(self.__source()) + } +} diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 8b6f4054851dd..93f40d902ef24 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -111,6 +111,8 @@ #![feature(const_pin)] #![feature(cstr_from_bytes_until_nul)] #![feature(dispatch_from_dyn)] +#![cfg_attr(not(bootstrap), feature(error_generic_member_access))] +#![cfg_attr(not(bootstrap), feature(error_in_core))] #![feature(exact_size_is_empty)] #![feature(extend_one)] #![feature(fmt_internals)] @@ -127,6 +129,7 @@ #![feature(nonnull_slice_from_raw_parts)] #![feature(pattern)] #![feature(pointer_byte_offsets)] +#![cfg_attr(not(bootstrap), feature(provide_any))] #![feature(ptr_internals)] #![feature(ptr_metadata)] #![feature(ptr_sub_ptr)] @@ -178,6 +181,7 @@ #![feature(unboxed_closures)] #![feature(unsized_fn_params)] #![feature(c_unwind)] +#![feature(with_negative_coherence)] // // Rustdoc features: #![feature(doc_cfg)] diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index a5118e5333b82..2c72dcdf1570d 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -44,6 +44,8 @@ #[cfg(not(no_global_oom_handling))] use core::char::{decode_utf16, REPLACEMENT_CHARACTER}; +#[cfg(not(bootstrap))] +use core::error::Error; use core::fmt; use core::hash; use core::iter::FusedIterator; @@ -1938,6 +1940,24 @@ impl fmt::Display for FromUtf16Error { } } +#[cfg(not(bootstrap))] +#[stable(feature = "rust1", since = "1.0.0")] +impl Error for FromUtf8Error { + #[allow(deprecated)] + fn description(&self) -> &str { + "invalid utf-8" + } +} + +#[cfg(not(bootstrap))] +#[stable(feature = "rust1", since = "1.0.0")] +impl Error for FromUtf16Error { + #[allow(deprecated)] + fn description(&self) -> &str { + "invalid utf-16" + } +} + #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] impl Clone for String { diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 4c03cc3ed158a..4377edeee8710 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -2763,3 +2763,25 @@ fn data_offset_align(align: usize) -> usize { let layout = Layout::new::>(); layout.size() + layout.padding_needed_for(align) } + +#[cfg(not(bootstrap))] +#[stable(feature = "arc_error", since = "1.52.0")] +impl core::error::Error for Arc { + #[allow(deprecated, deprecated_in_future)] + fn description(&self) -> &str { + core::error::Error::description(&**self) + } + + #[allow(deprecated)] + fn cause(&self) -> Option<&dyn core::error::Error> { + core::error::Error::cause(&**self) + } + + fn source(&self) -> Option<&(dyn core::error::Error + 'static)> { + core::error::Error::source(&**self) + } + + fn provide<'a>(&'a self, req: &mut core::any::Demand<'a>) { + core::error::Error::provide(&**self, req); + } +} diff --git a/library/core/src/alloc/layout.rs b/library/core/src/alloc/layout.rs index 3473ac09e956f..f03502429ab21 100644 --- a/library/core/src/alloc/layout.rs +++ b/library/core/src/alloc/layout.rs @@ -5,6 +5,8 @@ // Your performance intuition is useless. Run perf. use crate::cmp; +#[cfg(not(bootstrap))] +use crate::error::Error; use crate::fmt; use crate::mem::{self, ValidAlign}; use crate::ptr::NonNull; @@ -461,6 +463,10 @@ pub type LayoutErr = LayoutError; #[derive(Clone, PartialEq, Eq, Debug)] pub struct LayoutError; +#[cfg(not(bootstrap))] +#[stable(feature = "alloc_layout", since = "1.28.0")] +impl Error for LayoutError {} + // (we need this for downstream impl of trait Error) #[stable(feature = "alloc_layout", since = "1.28.0")] impl fmt::Display for LayoutError { diff --git a/library/core/src/alloc/mod.rs b/library/core/src/alloc/mod.rs index 6cc6e359e65b6..94efa76664f6a 100644 --- a/library/core/src/alloc/mod.rs +++ b/library/core/src/alloc/mod.rs @@ -21,6 +21,8 @@ pub use self::layout::LayoutErr; #[stable(feature = "alloc_layout_error", since = "1.50.0")] pub use self::layout::LayoutError; +#[cfg(not(bootstrap))] +use crate::error::Error; use crate::fmt; use crate::ptr::{self, NonNull}; @@ -32,6 +34,14 @@ use crate::ptr::{self, NonNull}; #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub struct AllocError; +#[cfg(not(bootstrap))] +#[unstable( + feature = "allocator_api", + reason = "the precise API and guarantees it provides may be tweaked.", + issue = "32838" +)] +impl Error for AllocError {} + // (we need this for downstream impl of trait Error) #[unstable(feature = "allocator_api", issue = "32838")] impl fmt::Display for AllocError { diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index c9823a136bc42..7cbd47175123f 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -7,6 +7,8 @@ use crate::borrow::{Borrow, BorrowMut}; use crate::cmp::Ordering; use crate::convert::{Infallible, TryFrom}; +#[cfg(not(bootstrap))] +use crate::error::Error; use crate::fmt; use crate::hash::{self, Hash}; use crate::iter::TrustedLen; @@ -119,6 +121,15 @@ impl fmt::Display for TryFromSliceError { } } +#[cfg(not(bootstrap))] +#[stable(feature = "try_from", since = "1.34.0")] +impl Error for TryFromSliceError { + #[allow(deprecated)] + fn description(&self) -> &str { + self.__description() + } +} + impl TryFromSliceError { #[unstable( feature = "array_error_internals", diff --git a/library/core/src/char/decode.rs b/library/core/src/char/decode.rs index 71297acd17145..dc8ea66cc6d0e 100644 --- a/library/core/src/char/decode.rs +++ b/library/core/src/char/decode.rs @@ -1,5 +1,7 @@ //! UTF-8 and UTF-16 decoding iterators +#[cfg(not(bootstrap))] +use crate::error::Error; use crate::fmt; use super::from_u32_unchecked; @@ -121,3 +123,12 @@ impl fmt::Display for DecodeUtf16Error { write!(f, "unpaired surrogate found: {:x}", self.code) } } + +#[cfg(not(bootstrap))] +#[stable(feature = "decode_utf16", since = "1.9.0")] +impl Error for DecodeUtf16Error { + #[allow(deprecated)] + fn description(&self) -> &str { + "unpaired surrogate found" + } +} diff --git a/library/core/src/char/mod.rs b/library/core/src/char/mod.rs index 0df23e7bbe695..d3a9feeaec79b 100644 --- a/library/core/src/char/mod.rs +++ b/library/core/src/char/mod.rs @@ -36,6 +36,8 @@ pub use self::methods::encode_utf16_raw; #[unstable(feature = "char_internals", reason = "exposed only for libstd", issue = "none")] pub use self::methods::encode_utf8_raw; +#[cfg(not(bootstrap))] +use crate::error::Error; use crate::fmt::{self, Write}; use crate::iter::FusedIterator; @@ -582,3 +584,7 @@ impl fmt::Display for TryFromCharError { "unicode code point out of range".fmt(fmt) } } + +#[cfg(not(bootstrap))] +#[stable(feature = "u8_from_char", since = "1.59.0")] +impl Error for TryFromCharError {} diff --git a/library/core/src/convert/mod.rs b/library/core/src/convert/mod.rs index b30c8a4aeabdd..5bddfd1a413e7 100644 --- a/library/core/src/convert/mod.rs +++ b/library/core/src/convert/mod.rs @@ -34,6 +34,8 @@ #![stable(feature = "rust1", since = "1.0.0")] +#[cfg(not(bootstrap))] +use crate::error::Error; use crate::fmt; use crate::hash::{Hash, Hasher}; @@ -715,6 +717,14 @@ impl fmt::Display for Infallible { } } +#[cfg(not(bootstrap))] +#[stable(feature = "str_parse_error2", since = "1.8.0")] +impl Error for Infallible { + fn description(&self) -> &str { + match *self {} + } +} + #[stable(feature = "convert_infallible", since = "1.34.0")] impl PartialEq for Infallible { fn eq(&self, _: &Infallible) -> bool { diff --git a/library/core/src/error.md b/library/core/src/error.md new file mode 100644 index 0000000000000..891abebbfd39b --- /dev/null +++ b/library/core/src/error.md @@ -0,0 +1,137 @@ +Interfaces for working with Errors. + +# Error Handling In Rust + +The Rust language provides two complementary systems for constructing / +representing, reporting, propagating, reacting to, and discarding errors. +These responsibilities are collectively known as "error handling." The +components of the first system, the panic runtime and interfaces, are most +commonly used to represent bugs that have been detected in your program. The +components of the second system, `Result`, the error traits, and user +defined types, are used to represent anticipated runtime failure modes of +your program. + +## The Panic Interfaces + +The following are the primary interfaces of the panic system and the +responsibilities they cover: + +* [`panic!`] and [`panic_any`] (Constructing, Propagated automatically) +* [`PanicInfo`] (Reporting) +* [`set_hook`], [`take_hook`], and [`#[panic_handler]`][panic-handler] (Reporting) +* [`catch_unwind`] and [`resume_unwind`] (Discarding, Propagating) + +The following are the primary interfaces of the error system and the +responsibilities they cover: + +* [`Result`] (Propagating, Reacting) +* The [`Error`] trait (Reporting) +* User defined types (Constructing / Representing) +* [`match`] and [`downcast`] (Reacting) +* The question mark operator ([`?`]) (Propagating) +* The partially stable [`Try`] traits (Propagating, Constructing) +* [`Termination`] (Reporting) + +## Converting Errors into Panics + +The panic and error systems are not entirely distinct. Often times errors +that are anticipated runtime failures in an API might instead represent bugs +to a caller. For these situations the standard library provides APIs for +constructing panics with an `Error` as it's source. + +* [`Result::unwrap`] +* [`Result::expect`] + +These functions are equivalent, they either return the inner value if the +`Result` is `Ok` or panic if the `Result` is `Err` printing the inner error +as the source. The only difference between them is that with `expect` you +provide a panic error message to be printed alongside the source, whereas +`unwrap` has a default message indicating only that you unwraped an `Err`. + +Of the two, `expect` is generally preferred since its `msg` field allows you +to convey your intent and assumptions which makes tracking down the source +of a panic easier. `unwrap` on the other hand can still be a good fit in +situations where you can trivially show that a piece of code will never +panic, such as `"127.0.0.1".parse::().unwrap()` or early +prototyping. + +# Common Message Styles + +There are two common styles for how people word `expect` messages. Using +the message to present information to users encountering a panic +("expect as error message") or using the message to present information +to developers debugging the panic ("expect as precondition"). + +In the former case the expect message is used to describe the error that +has occurred which is considered a bug. Consider the following example: + +```should_panic +// Read environment variable, panic if it is not present +let path = std::env::var("IMPORTANT_PATH").unwrap(); +``` + +In the "expect as error message" style we would use expect to describe +that the environment variable was not set when it should have been: + +```should_panic +let path = std::env::var("IMPORTANT_PATH") + .expect("env variable `IMPORTANT_PATH` is not set"); +``` + +In the "expect as precondition" style, we would instead describe the +reason we _expect_ the `Result` should be `Ok`. With this style we would +prefer to write: + +```should_panic +let path = std::env::var("IMPORTANT_PATH") + .expect("env variable `IMPORTANT_PATH` should be set by `wrapper_script.sh`"); +``` + +The "expect as error message" style does not work as well with the +default output of the std panic hooks, and often ends up repeating +information that is already communicated by the source error being +unwrapped: + +```text +thread 'main' panicked at 'env variable `IMPORTANT_PATH` is not set: NotPresent', src/main.rs:4:6 +``` + +In this example we end up mentioning that an env variable is not set, +followed by our source message that says the env is not present, the +only additional information we're communicating is the name of the +environment variable being checked. + +The "expect as precondition" style instead focuses on source code +readability, making it easier to understand what must have gone wrong in +situations where panics are being used to represent bugs exclusively. +Also, by framing our expect in terms of what "SHOULD" have happened to +prevent the source error, we end up introducing new information that is +independent from our source error. + +```text +thread 'main' panicked at 'env variable `IMPORTANT_PATH` should be set by `wrapper_script.sh`: NotPresent', src/main.rs:4:6 +``` + +In this example we are communicating not only the name of the +environment variable that should have been set, but also an explanation +for why it should have been set, and we let the source error display as +a clear contradiction to our expectation. + +**Hint**: If you're having trouble remembering how to phrase +expect-as-precondition style error messages remember to focus on the word +"should" as in "env variable should be set by blah" or "the given binary +should be available and executable by the current user". + +[`panic_any`]: ../../std/panic/fn.panic_any.html +[`PanicInfo`]: crate::panic::PanicInfo +[`catch_unwind`]: ../../std/panic/fn.catch_unwind.html +[`resume_unwind`]: ../../std/panic/fn.resume_unwind.html +[`downcast`]: crate::error::Error +[`Termination`]: ../../std/process/trait.Termination.html +[`Try`]: crate::ops::Try +[panic hook]: ../../std/panic/fn.set_hook.html +[`set_hook`]: ../../std/panic/fn.set_hook.html +[`take_hook`]: ../../std/panic/fn.take_hook.html +[panic-handler]: +[`match`]: ../../std/keyword.match.html +[`?`]: ../../std/result/index.html#the-question-mark-operator- diff --git a/library/core/src/error.rs b/library/core/src/error.rs new file mode 100644 index 0000000000000..d11debb34adca --- /dev/null +++ b/library/core/src/error.rs @@ -0,0 +1,508 @@ +#![doc = include_str!("error.md")] +#![unstable(feature = "error_in_core", issue = "none")] + +// A note about crates and the facade: +// +// Originally, the `Error` trait was defined in libcore, and the impls +// were scattered about. However, coherence objected to this +// arrangement, because to create the blanket impls for `Box` required +// knowing that `&str: !Error`, and we have no means to deal with that +// sort of conflict just now. Therefore, for the time being, we have +// moved the `Error` trait into libstd. As we evolve a sol'n to the +// coherence challenge (e.g., specialization, neg impls, etc) we can +// reconsider what crate these items belong in. + +#[cfg(test)] +mod tests; + +use crate::any::{Demand, Provider, TypeId}; +use crate::fmt::{Debug, Display}; + +/// `Error` is a trait representing the basic expectations for error values, +/// i.e., values of type `E` in [`Result`]. +/// +/// Errors must describe themselves through the [`Display`] and [`Debug`] +/// traits. Error messages are typically concise lowercase sentences without +/// trailing punctuation: +/// +/// ``` +/// let err = "NaN".parse::().unwrap_err(); +/// assert_eq!(err.to_string(), "invalid digit found in string"); +/// ``` +/// +/// Errors may provide cause chain information. [`Error::source()`] is generally +/// used when errors cross "abstraction boundaries". If one module must report +/// an error that is caused by an error from a lower-level module, it can allow +/// accessing that error via [`Error::source()`]. This makes it possible for the +/// high-level module to provide its own errors while also revealing some of the +/// implementation for debugging via `source` chains. +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "Error")] +#[rustc_has_incoherent_inherent_impls] +pub trait Error: Debug + Display { + /// The lower-level source of this error, if any. + /// + /// # Examples + /// + /// ``` + /// use std::error::Error; + /// use std::fmt; + /// + /// #[derive(Debug)] + /// struct SuperError { + /// source: SuperErrorSideKick, + /// } + /// + /// impl fmt::Display for SuperError { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// write!(f, "SuperError is here!") + /// } + /// } + /// + /// impl Error for SuperError { + /// fn source(&self) -> Option<&(dyn Error + 'static)> { + /// Some(&self.source) + /// } + /// } + /// + /// #[derive(Debug)] + /// struct SuperErrorSideKick; + /// + /// impl fmt::Display for SuperErrorSideKick { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// write!(f, "SuperErrorSideKick is here!") + /// } + /// } + /// + /// impl Error for SuperErrorSideKick {} + /// + /// fn get_super_error() -> Result<(), SuperError> { + /// Err(SuperError { source: SuperErrorSideKick }) + /// } + /// + /// fn main() { + /// match get_super_error() { + /// Err(e) => { + /// println!("Error: {e}"); + /// println!("Caused by: {}", e.source().unwrap()); + /// } + /// _ => println!("No error"), + /// } + /// } + /// ``` + #[stable(feature = "error_source", since = "1.30.0")] + fn source(&self) -> Option<&(dyn Error + 'static)> { + None + } + + /// Gets the `TypeId` of `self`. + #[doc(hidden)] + #[unstable( + feature = "error_type_id", + reason = "this is memory-unsafe to override in user code", + issue = "60784" + )] + fn type_id(&self, _: private::Internal) -> TypeId + where + Self: 'static, + { + TypeId::of::() + } + + /// ``` + /// if let Err(e) = "xc".parse::() { + /// // Print `e` itself, no need for description(). + /// eprintln!("Error: {e}"); + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[deprecated(since = "1.42.0", note = "use the Display impl or to_string()")] + fn description(&self) -> &str { + "description() is deprecated; use Display" + } + + #[stable(feature = "rust1", since = "1.0.0")] + #[deprecated( + since = "1.33.0", + note = "replaced by Error::source, which can support downcasting" + )] + #[allow(missing_docs)] + fn cause(&self) -> Option<&dyn Error> { + self.source() + } + + /// Provides type based access to context intended for error reports. + /// + /// Used in conjunction with [`Demand::provide_value`] and [`Demand::provide_ref`] to extract + /// references to member variables from `dyn Error` trait objects. + /// + /// # Example + /// + /// ```rust + /// #![feature(provide_any)] + /// #![feature(error_generic_member_access)] + /// use core::fmt; + /// use core::any::Demand; + /// + /// #[derive(Debug)] + /// struct MyBacktrace { + /// // ... + /// } + /// + /// impl MyBacktrace { + /// fn new() -> MyBacktrace { + /// // ... + /// # MyBacktrace {} + /// } + /// } + /// + /// #[derive(Debug)] + /// struct SourceError { + /// // ... + /// } + /// + /// impl fmt::Display for SourceError { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// write!(f, "Example Source Error") + /// } + /// } + /// + /// impl std::error::Error for SourceError {} + /// + /// #[derive(Debug)] + /// struct Error { + /// source: SourceError, + /// backtrace: MyBacktrace, + /// } + /// + /// impl fmt::Display for Error { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// write!(f, "Example Error") + /// } + /// } + /// + /// impl std::error::Error for Error { + /// fn provide<'a>(&'a self, req: &mut Demand<'a>) { + /// req + /// .provide_ref::(&self.backtrace) + /// .provide_ref::(&self.source); + /// } + /// } + /// + /// fn main() { + /// let backtrace = MyBacktrace::new(); + /// let source = SourceError {}; + /// let error = Error { source, backtrace }; + /// let dyn_error = &error as &dyn std::error::Error; + /// let backtrace_ref = dyn_error.request_ref::().unwrap(); + /// + /// assert!(core::ptr::eq(&error.backtrace, backtrace_ref)); + /// } + /// ``` + #[unstable(feature = "error_generic_member_access", issue = "99301")] + #[allow(unused_variables)] + fn provide<'a>(&'a self, req: &mut Demand<'a>) {} +} + +#[unstable(feature = "error_generic_member_access", issue = "99301")] +impl Provider for E +where + E: Error + ?Sized, +{ + fn provide<'a>(&'a self, req: &mut Demand<'a>) { + self.provide(req) + } +} + +mod private { + // This is a hack to prevent `type_id` from being overridden by `Error` + // implementations, since that can enable unsound downcasting. + #[unstable(feature = "error_type_id", issue = "60784")] + #[derive(Debug)] + pub struct Internal; +} + +#[unstable(feature = "never_type", issue = "35121")] +impl Error for ! {} + +impl<'a> dyn Error + 'a { + /// Request a reference of type `T` as context about this error. + #[unstable(feature = "error_generic_member_access", issue = "99301")] + pub fn request_ref(&'a self) -> Option<&'a T> { + core::any::request_ref(self) + } + + /// Request a value of type `T` as context about this error. + #[unstable(feature = "error_generic_member_access", issue = "99301")] + pub fn request_value(&'a self) -> Option { + core::any::request_value(self) + } +} + +// Copied from `any.rs`. +impl dyn Error + 'static { + /// Returns `true` if the inner type is the same as `T`. + #[stable(feature = "error_downcast", since = "1.3.0")] + #[inline] + pub fn is(&self) -> bool { + // Get `TypeId` of the type this function is instantiated with. + let t = TypeId::of::(); + + // Get `TypeId` of the type in the trait object (`self`). + let concrete = self.type_id(private::Internal); + + // Compare both `TypeId`s on equality. + t == concrete + } + + /// Returns some reference to the inner value if it is of type `T`, or + /// `None` if it isn't. + #[stable(feature = "error_downcast", since = "1.3.0")] + #[inline] + pub fn downcast_ref(&self) -> Option<&T> { + if self.is::() { + // SAFETY: `is` ensures this type cast is correct + unsafe { Some(&*(self as *const dyn Error as *const T)) } + } else { + None + } + } + + /// Returns some mutable reference to the inner value if it is of type `T`, or + /// `None` if it isn't. + #[stable(feature = "error_downcast", since = "1.3.0")] + #[inline] + pub fn downcast_mut(&mut self) -> Option<&mut T> { + if self.is::() { + // SAFETY: `is` ensures this type cast is correct + unsafe { Some(&mut *(self as *mut dyn Error as *mut T)) } + } else { + None + } + } +} + +impl dyn Error + 'static + Send { + /// Forwards to the method defined on the type `dyn Error`. + #[stable(feature = "error_downcast", since = "1.3.0")] + #[inline] + pub fn is(&self) -> bool { + ::is::(self) + } + + /// Forwards to the method defined on the type `dyn Error`. + #[stable(feature = "error_downcast", since = "1.3.0")] + #[inline] + pub fn downcast_ref(&self) -> Option<&T> { + ::downcast_ref::(self) + } + + /// Forwards to the method defined on the type `dyn Error`. + #[stable(feature = "error_downcast", since = "1.3.0")] + #[inline] + pub fn downcast_mut(&mut self) -> Option<&mut T> { + ::downcast_mut::(self) + } + + /// Request a reference of type `T` as context about this error. + #[unstable(feature = "error_generic_member_access", issue = "99301")] + pub fn request_ref(&self) -> Option<&T> { + ::request_ref(self) + } + + /// Request a value of type `T` as context about this error. + #[unstable(feature = "error_generic_member_access", issue = "99301")] + pub fn request_value(&self) -> Option { + ::request_value(self) + } +} + +impl dyn Error + 'static + Send + Sync { + /// Forwards to the method defined on the type `dyn Error`. + #[stable(feature = "error_downcast", since = "1.3.0")] + #[inline] + pub fn is(&self) -> bool { + ::is::(self) + } + + /// Forwards to the method defined on the type `dyn Error`. + #[stable(feature = "error_downcast", since = "1.3.0")] + #[inline] + pub fn downcast_ref(&self) -> Option<&T> { + ::downcast_ref::(self) + } + + /// Forwards to the method defined on the type `dyn Error`. + #[stable(feature = "error_downcast", since = "1.3.0")] + #[inline] + pub fn downcast_mut(&mut self) -> Option<&mut T> { + ::downcast_mut::(self) + } + + /// Request a reference of type `T` as context about this error. + #[unstable(feature = "error_generic_member_access", issue = "99301")] + pub fn request_ref(&self) -> Option<&T> { + ::request_ref(self) + } + + /// Request a value of type `T` as context about this error. + #[unstable(feature = "error_generic_member_access", issue = "99301")] + pub fn request_value(&self) -> Option { + ::request_value(self) + } +} + +impl dyn Error { + /// Returns an iterator starting with the current error and continuing with + /// recursively calling [`Error::source`]. + /// + /// If you want to omit the current error and only use its sources, + /// use `skip(1)`. + /// + /// # Examples + /// + /// ``` + /// #![feature(error_iter)] + /// use std::error::Error; + /// use std::fmt; + /// + /// #[derive(Debug)] + /// struct A; + /// + /// #[derive(Debug)] + /// struct B(Option>); + /// + /// impl fmt::Display for A { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// write!(f, "A") + /// } + /// } + /// + /// impl fmt::Display for B { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// write!(f, "B") + /// } + /// } + /// + /// impl Error for A {} + /// + /// impl Error for B { + /// fn source(&self) -> Option<&(dyn Error + 'static)> { + /// self.0.as_ref().map(|e| e.as_ref()) + /// } + /// } + /// + /// let b = B(Some(Box::new(A))); + /// + /// // let err : Box = b.into(); // or + /// let err = &b as &(dyn Error); + /// + /// let mut iter = err.chain(); + /// + /// assert_eq!("B".to_string(), iter.next().unwrap().to_string()); + /// assert_eq!("A".to_string(), iter.next().unwrap().to_string()); + /// assert!(iter.next().is_none()); + /// assert!(iter.next().is_none()); + /// ``` + #[unstable(feature = "error_iter", issue = "58520")] + #[inline] + pub fn chain(&self) -> Chain<'_> { + Chain { current: Some(self) } + } +} + +/// An iterator over an [`Error`] and its sources. +/// +/// If you want to omit the initial error and only process +/// its sources, use `skip(1)`. +#[unstable(feature = "error_iter", issue = "58520")] +#[derive(Clone, Debug)] +pub struct Chain<'a> { + current: Option<&'a (dyn Error + 'static)>, +} + +#[unstable(feature = "error_iter", issue = "58520")] +impl<'a> Iterator for Chain<'a> { + type Item = &'a (dyn Error + 'static); + + fn next(&mut self) -> Option { + let current = self.current; + self.current = self.current.and_then(Error::source); + current + } +} + +#[stable(feature = "error_by_ref", since = "1.51.0")] +impl<'a, T: Error + ?Sized> Error for &'a T { + #[allow(deprecated, deprecated_in_future)] + fn description(&self) -> &str { + Error::description(&**self) + } + + #[allow(deprecated)] + fn cause(&self) -> Option<&dyn Error> { + Error::cause(&**self) + } + + fn source(&self) -> Option<&(dyn Error + 'static)> { + Error::source(&**self) + } + + fn provide<'b>(&'b self, req: &mut Demand<'b>) { + Error::provide(&**self, req); + } +} + +#[stable(feature = "fmt_error", since = "1.11.0")] +impl Error for crate::fmt::Error { + #[allow(deprecated)] + fn description(&self) -> &str { + "an error occurred when formatting an argument" + } +} + +#[stable(feature = "try_borrow", since = "1.13.0")] +impl Error for crate::cell::BorrowError { + #[allow(deprecated)] + fn description(&self) -> &str { + "already mutably borrowed" + } +} + +#[stable(feature = "try_borrow", since = "1.13.0")] +impl Error for crate::cell::BorrowMutError { + #[allow(deprecated)] + fn description(&self) -> &str { + "already borrowed" + } +} + +#[stable(feature = "try_from", since = "1.34.0")] +impl Error for crate::char::CharTryFromError { + #[allow(deprecated)] + fn description(&self) -> &str { + "converted integer out of range for `char`" + } +} + +#[stable(feature = "char_from_str", since = "1.20.0")] +impl Error for crate::char::ParseCharError { + #[allow(deprecated)] + fn description(&self) -> &str { + self.__description() + } +} + +#[unstable(feature = "duration_checked_float", issue = "83400")] +impl Error for crate::time::FromFloatSecsError {} + +#[stable(feature = "frombyteswithnulerror_impls", since = "1.17.0")] +impl Error for crate::ffi::FromBytesWithNulError { + #[allow(deprecated)] + fn description(&self) -> &str { + self.__description() + } +} + +#[unstable(feature = "cstr_from_bytes_until_nul", issue = "95027")] +impl Error for crate::ffi::FromBytesUntilNulError {} diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 24742bb49b9a5..8d04a213f503d 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -302,6 +302,8 @@ pub mod clone; pub mod cmp; pub mod convert; pub mod default; +#[cfg(not(bootstrap))] +pub mod error; pub mod marker; pub mod ops; diff --git a/library/core/src/num/error.rs b/library/core/src/num/error.rs index 1a223016dae0f..1f6b40e5df554 100644 --- a/library/core/src/num/error.rs +++ b/library/core/src/num/error.rs @@ -1,6 +1,8 @@ //! Error types for conversion to integral types. use crate::convert::Infallible; +#[cfg(not(bootstrap))] +use crate::error::Error; use crate::fmt; /// The error type returned when a checked integral type conversion fails. @@ -144,3 +146,21 @@ impl fmt::Display for ParseIntError { self.__description().fmt(f) } } + +#[cfg(not(bootstrap))] +#[stable(feature = "rust1", since = "1.0.0")] +impl Error for ParseIntError { + #[allow(deprecated)] + fn description(&self) -> &str { + self.__description() + } +} + +#[cfg(not(bootstrap))] +#[stable(feature = "try_from", since = "1.34.0")] +impl Error for TryFromIntError { + #[allow(deprecated)] + fn description(&self) -> &str { + self.__description() + } +} diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index f481399fdcf92..8f5e39dc29522 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -3,6 +3,8 @@ #![stable(feature = "rust1", since = "1.0.0")] use crate::ascii; +#[cfg(not(bootstrap))] +use crate::error::Error; use crate::intrinsics; use crate::mem; use crate::ops::{Add, Mul, Sub}; @@ -57,6 +59,16 @@ pub use wrapping::Wrapping; #[cfg(not(no_fp_fmt_parse))] pub use dec2flt::ParseFloatError; +#[cfg(not(bootstrap))] +#[cfg(not(no_fp_fmt_parse))] +#[stable(feature = "rust1", since = "1.0.0")] +impl Error for ParseFloatError { + #[allow(deprecated)] + fn description(&self) -> &str { + self.__description() + } +} + #[stable(feature = "rust1", since = "1.0.0")] pub use error::ParseIntError; diff --git a/library/core/src/str/error.rs b/library/core/src/str/error.rs index 4e569fcc866b6..343889b6999fa 100644 --- a/library/core/src/str/error.rs +++ b/library/core/src/str/error.rs @@ -1,5 +1,7 @@ //! Defines utf8 error type. +#[cfg(not(bootstrap))] +use crate::error::Error; use crate::fmt; /// Errors which can occur when attempting to interpret a sequence of [`u8`] @@ -122,6 +124,15 @@ impl fmt::Display for Utf8Error { } } +#[cfg(not(bootstrap))] +#[stable(feature = "rust1", since = "1.0.0")] +impl Error for Utf8Error { + #[allow(deprecated)] + fn description(&self) -> &str { + "invalid utf-8: corrupt contents" + } +} + /// An error returned when parsing a `bool` using [`from_str`] fails /// /// [`from_str`]: super::FromStr::from_str @@ -136,3 +147,12 @@ impl fmt::Display for ParseBoolError { "provided string was not `true` or `false`".fmt(f) } } + +#[cfg(not(bootstrap))] +#[stable(feature = "rust1", since = "1.0.0")] +impl Error for ParseBoolError { + #[allow(deprecated)] + fn description(&self) -> &str { + "failed to parse bool" + } +} diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index ee66895a7ed2a..4682e9a85b0be 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -2638,3 +2638,7 @@ impl_fn_for_zst! { unsafe { from_utf8_unchecked(bytes) } }; } + +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg(not(bootstrap))] +impl !crate::error::Error for &str {} diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index db811343fa322..9845d1faf9aa1 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -9,6 +9,8 @@ use crate::borrow::Borrow; use crate::cell::Cell; use crate::collections::TryReserveError; use crate::collections::TryReserveErrorKind; +#[cfg(not(bootstrap))] +use crate::error::Error; use crate::fmt::{self, Debug}; #[allow(deprecated)] use crate::hash::{BuildHasher, Hash, Hasher, SipHasher13}; @@ -2158,6 +2160,15 @@ impl<'a, K: Debug, V: Debug> fmt::Display for OccupiedError<'a, K, V> { } } +#[cfg(not(bootstrap))] +#[unstable(feature = "map_try_insert", issue = "82766")] +impl<'a, K: fmt::Debug, V: fmt::Debug> Error for OccupiedError<'a, K, V> { + #[allow(deprecated)] + fn description(&self) -> &str { + "key already exists" + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl<'a, K, V, S> IntoIterator for &'a HashMap { type Item = (&'a K, &'a V); diff --git a/library/std/src/error.rs b/library/std/src/error.rs index df7a49d258282..914f6d6d2e3e9 100644 --- a/library/std/src/error.rs +++ b/library/std/src/error.rs @@ -1,141 +1,4 @@ -//! Interfaces for working with Errors. -//! -//! # Error Handling In Rust -//! -//! The Rust language provides two complementary systems for constructing / -//! representing, reporting, propagating, reacting to, and discarding errors. -//! These responsibilities are collectively known as "error handling." The -//! components of the first system, the panic runtime and interfaces, are most -//! commonly used to represent bugs that have been detected in your program. The -//! components of the second system, `Result`, the error traits, and user -//! defined types, are used to represent anticipated runtime failure modes of -//! your program. -//! -//! ## The Panic Interfaces -//! -//! The following are the primary interfaces of the panic system and the -//! responsibilities they cover: -//! -//! * [`panic!`] and [`panic_any`] (Constructing, Propagated automatically) -//! * [`PanicInfo`] (Reporting) -//! * [`set_hook`], [`take_hook`], and [`#[panic_handler]`][panic-handler] (Reporting) -//! * [`catch_unwind`] and [`resume_unwind`] (Discarding, Propagating) -//! -//! The following are the primary interfaces of the error system and the -//! responsibilities they cover: -//! -//! * [`Result`] (Propagating, Reacting) -//! * The [`Error`] trait (Reporting) -//! * User defined types (Constructing / Representing) -//! * [`match`] and [`downcast`] (Reacting) -//! * The question mark operator ([`?`]) (Propagating) -//! * The partially stable [`Try`] traits (Propagating, Constructing) -//! * [`Termination`] (Reporting) -//! -//! ## Converting Errors into Panics -//! -//! The panic and error systems are not entirely distinct. Often times errors -//! that are anticipated runtime failures in an API might instead represent bugs -//! to a caller. For these situations the standard library provides APIs for -//! constructing panics with an `Error` as it's source. -//! -//! * [`Result::unwrap`] -//! * [`Result::expect`] -//! -//! These functions are equivalent, they either return the inner value if the -//! `Result` is `Ok` or panic if the `Result` is `Err` printing the inner error -//! as the source. The only difference between them is that with `expect` you -//! provide a panic error message to be printed alongside the source, whereas -//! `unwrap` has a default message indicating only that you unwraped an `Err`. -//! -//! Of the two, `expect` is generally preferred since its `msg` field allows you -//! to convey your intent and assumptions which makes tracking down the source -//! of a panic easier. `unwrap` on the other hand can still be a good fit in -//! situations where you can trivially show that a piece of code will never -//! panic, such as `"127.0.0.1".parse::().unwrap()` or early -//! prototyping. -//! -//! # Common Message Styles -//! -//! There are two common styles for how people word `expect` messages. Using -//! the message to present information to users encountering a panic -//! ("expect as error message") or using the message to present information -//! to developers debugging the panic ("expect as precondition"). -//! -//! In the former case the expect message is used to describe the error that -//! has occurred which is considered a bug. Consider the following example: -//! -//! ```should_panic -//! // Read environment variable, panic if it is not present -//! let path = std::env::var("IMPORTANT_PATH").unwrap(); -//! ``` -//! -//! In the "expect as error message" style we would use expect to describe -//! that the environment variable was not set when it should have been: -//! -//! ```should_panic -//! let path = std::env::var("IMPORTANT_PATH") -//! .expect("env variable `IMPORTANT_PATH` is not set"); -//! ``` -//! -//! In the "expect as precondition" style, we would instead describe the -//! reason we _expect_ the `Result` should be `Ok`. With this style we would -//! prefer to write: -//! -//! ```should_panic -//! let path = std::env::var("IMPORTANT_PATH") -//! .expect("env variable `IMPORTANT_PATH` should be set by `wrapper_script.sh`"); -//! ``` -//! -//! The "expect as error message" style does not work as well with the -//! default output of the std panic hooks, and often ends up repeating -//! information that is already communicated by the source error being -//! unwrapped: -//! -//! ```text -//! thread 'main' panicked at 'env variable `IMPORTANT_PATH` is not set: NotPresent', src/main.rs:4:6 -//! ``` -//! -//! In this example we end up mentioning that an env variable is not set, -//! followed by our source message that says the env is not present, the -//! only additional information we're communicating is the name of the -//! environment variable being checked. -//! -//! The "expect as precondition" style instead focuses on source code -//! readability, making it easier to understand what must have gone wrong in -//! situations where panics are being used to represent bugs exclusively. -//! Also, by framing our expect in terms of what "SHOULD" have happened to -//! prevent the source error, we end up introducing new information that is -//! independent from our source error. -//! -//! ```text -//! thread 'main' panicked at 'env variable `IMPORTANT_PATH` should be set by `wrapper_script.sh`: NotPresent', src/main.rs:4:6 -//! ``` -//! -//! In this example we are communicating not only the name of the -//! environment variable that should have been set, but also an explanation -//! for why it should have been set, and we let the source error display as -//! a clear contradiction to our expectation. -//! -//! **Hint**: If you're having trouble remembering how to phrase -//! expect-as-precondition style error messages remember to focus on the word -//! "should" as in "env variable should be set by blah" or "the given binary -//! should be available and executable by the current user". -//! -//! [`panic_any`]: crate::panic::panic_any -//! [`PanicInfo`]: crate::panic::PanicInfo -//! [`catch_unwind`]: crate::panic::catch_unwind -//! [`resume_unwind`]: crate::panic::resume_unwind -//! [`downcast`]: crate::error::Error -//! [`Termination`]: crate::process::Termination -//! [`Try`]: crate::ops::Try -//! [panic hook]: crate::panic::set_hook -//! [`set_hook`]: crate::panic::set_hook -//! [`take_hook`]: crate::panic::take_hook -//! [panic-handler]: -//! [`match`]: ../../std/keyword.match.html -//! [`?`]: ../../std/result/index.html#the-question-mark-operator- - +#![doc = include_str!("../../core/src/error.md")] #![stable(feature = "rust1", since = "1.0.0")] // A note about crates and the facade: @@ -152,24 +15,48 @@ #[cfg(test)] mod tests; +#[cfg(bootstrap)] use core::array; +#[cfg(bootstrap)] use core::convert::Infallible; +#[cfg(bootstrap)] use crate::alloc::{AllocError, LayoutError}; -use crate::any::{Demand, Provider, TypeId}; +#[cfg(bootstrap)] +use crate::any::Demand; +#[cfg(bootstrap)] +use crate::any::{Provider, TypeId}; use crate::backtrace::Backtrace; +#[cfg(bootstrap)] use crate::borrow::Cow; +#[cfg(bootstrap)] use crate::cell; +#[cfg(bootstrap)] use crate::char; -use crate::fmt::{self, Debug, Display, Write}; +#[cfg(bootstrap)] +use crate::fmt::Debug; +#[cfg(bootstrap)] +use crate::fmt::Display; +use crate::fmt::{self, Write}; +#[cfg(bootstrap)] use crate::io; +#[cfg(bootstrap)] use crate::mem::transmute; +#[cfg(bootstrap)] use crate::num; +#[cfg(bootstrap)] use crate::str; +#[cfg(bootstrap)] use crate::string; +#[cfg(bootstrap)] use crate::sync::Arc; +#[cfg(bootstrap)] use crate::time; +#[cfg(not(bootstrap))] +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::error::Error; + /// `Error` is a trait representing the basic expectations for error values, /// i.e., values of type `E` in [`Result`]. /// @@ -190,6 +77,7 @@ use crate::time; /// implementation for debugging via `source` chains. #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "Error")] +#[cfg(bootstrap)] pub trait Error: Debug + Display { /// The lower-level source of this error, if any. /// @@ -355,6 +243,7 @@ pub trait Error: Debug + Display { fn provide<'a>(&'a self, req: &mut Demand<'a>) {} } +#[cfg(bootstrap)] #[unstable(feature = "error_generic_member_access", issue = "99301")] impl<'b> Provider for dyn Error + 'b { fn provide<'a>(&'a self, req: &mut Demand<'a>) { @@ -370,6 +259,7 @@ mod private { pub struct Internal; } +#[cfg(bootstrap)] #[stable(feature = "rust1", since = "1.0.0")] impl<'a, E: Error + 'a> From for Box { /// Converts a type of [`Error`] into a box of dyn [`Error`]. @@ -402,6 +292,7 @@ impl<'a, E: Error + 'a> From for Box { } } +#[cfg(bootstrap)] #[stable(feature = "rust1", since = "1.0.0")] impl<'a, E: Error + Send + Sync + 'a> From for Box { /// Converts a type of [`Error`] + [`Send`] + [`Sync`] into a box of @@ -440,6 +331,7 @@ impl<'a, E: Error + Send + Sync + 'a> From for Box for Box { /// Converts a [`String`] into a box of dyn [`Error`] + [`Send`] + [`Sync`]. @@ -483,6 +375,7 @@ impl From for Box { } } +#[cfg(bootstrap)] #[stable(feature = "string_box_error", since = "1.6.0")] impl From for Box { /// Converts a [`String`] into a box of dyn [`Error`]. @@ -504,6 +397,7 @@ impl From for Box { } } +#[cfg(bootstrap)] #[stable(feature = "rust1", since = "1.0.0")] impl<'a> From<&str> for Box { /// Converts a [`str`] into a box of dyn [`Error`] + [`Send`] + [`Sync`]. @@ -527,6 +421,7 @@ impl<'a> From<&str> for Box { } } +#[cfg(bootstrap)] #[stable(feature = "string_box_error", since = "1.6.0")] impl From<&str> for Box { /// Converts a [`str`] into a box of dyn [`Error`]. @@ -548,6 +443,7 @@ impl From<&str> for Box { } } +#[cfg(bootstrap)] #[stable(feature = "cow_box_error", since = "1.22.0")] impl<'a, 'b> From> for Box { /// Converts a [`Cow`] into a box of dyn [`Error`] + [`Send`] + [`Sync`]. @@ -569,6 +465,7 @@ impl<'a, 'b> From> for Box { } } +#[cfg(bootstrap)] #[stable(feature = "cow_box_error", since = "1.22.0")] impl<'a> From> for Box { /// Converts a [`Cow`] into a box of dyn [`Error`]. @@ -589,9 +486,11 @@ impl<'a> From> for Box { } } +#[cfg(bootstrap)] #[unstable(feature = "never_type", issue = "35121")] impl Error for ! {} +#[cfg(bootstrap)] #[unstable( feature = "allocator_api", reason = "the precise API and guarantees it provides may be tweaked.", @@ -599,9 +498,11 @@ impl Error for ! {} )] impl Error for AllocError {} +#[cfg(bootstrap)] #[stable(feature = "alloc_layout", since = "1.28.0")] impl Error for LayoutError {} +#[cfg(bootstrap)] #[stable(feature = "rust1", since = "1.0.0")] impl Error for str::ParseBoolError { #[allow(deprecated)] @@ -610,6 +511,7 @@ impl Error for str::ParseBoolError { } } +#[cfg(bootstrap)] #[stable(feature = "rust1", since = "1.0.0")] impl Error for str::Utf8Error { #[allow(deprecated)] @@ -618,6 +520,7 @@ impl Error for str::Utf8Error { } } +#[cfg(bootstrap)] #[stable(feature = "rust1", since = "1.0.0")] impl Error for num::ParseIntError { #[allow(deprecated)] @@ -626,6 +529,7 @@ impl Error for num::ParseIntError { } } +#[cfg(bootstrap)] #[stable(feature = "try_from", since = "1.34.0")] impl Error for num::TryFromIntError { #[allow(deprecated)] @@ -634,6 +538,7 @@ impl Error for num::TryFromIntError { } } +#[cfg(bootstrap)] #[stable(feature = "try_from", since = "1.34.0")] impl Error for array::TryFromSliceError { #[allow(deprecated)] @@ -642,6 +547,7 @@ impl Error for array::TryFromSliceError { } } +#[cfg(bootstrap)] #[stable(feature = "rust1", since = "1.0.0")] impl Error for num::ParseFloatError { #[allow(deprecated)] @@ -650,6 +556,7 @@ impl Error for num::ParseFloatError { } } +#[cfg(bootstrap)] #[stable(feature = "rust1", since = "1.0.0")] impl Error for string::FromUtf8Error { #[allow(deprecated)] @@ -658,6 +565,7 @@ impl Error for string::FromUtf8Error { } } +#[cfg(bootstrap)] #[stable(feature = "rust1", since = "1.0.0")] impl Error for string::FromUtf16Error { #[allow(deprecated)] @@ -666,6 +574,7 @@ impl Error for string::FromUtf16Error { } } +#[cfg(bootstrap)] #[stable(feature = "str_parse_error2", since = "1.8.0")] impl Error for Infallible { fn description(&self) -> &str { @@ -673,6 +582,7 @@ impl Error for Infallible { } } +#[cfg(bootstrap)] #[stable(feature = "decode_utf16", since = "1.9.0")] impl Error for char::DecodeUtf16Error { #[allow(deprecated)] @@ -681,9 +591,11 @@ impl Error for char::DecodeUtf16Error { } } +#[cfg(bootstrap)] #[stable(feature = "u8_from_char", since = "1.59.0")] impl Error for char::TryFromCharError {} +#[cfg(bootstrap)] #[unstable(feature = "map_try_insert", issue = "82766")] impl<'a, K: Debug + Ord, V: Debug> Error for crate::collections::btree_map::OccupiedError<'a, K, V> @@ -694,6 +606,7 @@ impl<'a, K: Debug + Ord, V: Debug> Error } } +#[cfg(bootstrap)] #[unstable(feature = "map_try_insert", issue = "82766")] impl<'a, K: Debug, V: Debug> Error for crate::collections::hash_map::OccupiedError<'a, K, V> { #[allow(deprecated)] @@ -702,6 +615,7 @@ impl<'a, K: Debug, V: Debug> Error for crate::collections::hash_map::OccupiedErr } } +#[cfg(bootstrap)] #[stable(feature = "box_error", since = "1.8.0")] impl Error for Box { #[allow(deprecated, deprecated_in_future)] @@ -719,6 +633,7 @@ impl Error for Box { } } +#[cfg(bootstrap)] #[unstable(feature = "thin_box", issue = "92791")] impl crate::error::Error for crate::boxed::ThinBox { fn source(&self) -> Option<&(dyn crate::error::Error + 'static)> { @@ -727,6 +642,7 @@ impl crate::error::Error for crate::boxed::Thin } } +#[cfg(bootstrap)] #[stable(feature = "error_by_ref", since = "1.51.0")] impl<'a, T: Error + ?Sized> Error for &'a T { #[allow(deprecated, deprecated_in_future)] @@ -748,6 +664,7 @@ impl<'a, T: Error + ?Sized> Error for &'a T { } } +#[cfg(bootstrap)] #[stable(feature = "arc_error", since = "1.52.0")] impl Error for Arc { #[allow(deprecated, deprecated_in_future)] @@ -769,6 +686,7 @@ impl Error for Arc { } } +#[cfg(bootstrap)] #[stable(feature = "fmt_error", since = "1.11.0")] impl Error for fmt::Error { #[allow(deprecated)] @@ -777,6 +695,7 @@ impl Error for fmt::Error { } } +#[cfg(bootstrap)] #[stable(feature = "try_borrow", since = "1.13.0")] impl Error for cell::BorrowError { #[allow(deprecated)] @@ -785,6 +704,7 @@ impl Error for cell::BorrowError { } } +#[cfg(bootstrap)] #[stable(feature = "try_borrow", since = "1.13.0")] impl Error for cell::BorrowMutError { #[allow(deprecated)] @@ -793,6 +713,7 @@ impl Error for cell::BorrowMutError { } } +#[cfg(bootstrap)] #[stable(feature = "try_from", since = "1.34.0")] impl Error for char::CharTryFromError { #[allow(deprecated)] @@ -801,6 +722,7 @@ impl Error for char::CharTryFromError { } } +#[cfg(bootstrap)] #[stable(feature = "char_from_str", since = "1.20.0")] impl Error for char::ParseCharError { #[allow(deprecated)] @@ -809,12 +731,15 @@ impl Error for char::ParseCharError { } } +#[cfg(bootstrap)] #[stable(feature = "try_reserve", since = "1.57.0")] impl Error for alloc::collections::TryReserveError {} +#[cfg(bootstrap)] #[unstable(feature = "duration_checked_float", issue = "83400")] impl Error for time::FromFloatSecsError {} +#[cfg(bootstrap)] #[stable(feature = "rust1", since = "1.0.0")] impl Error for alloc::ffi::NulError { #[allow(deprecated)] @@ -823,6 +748,7 @@ impl Error for alloc::ffi::NulError { } } +#[cfg(bootstrap)] #[stable(feature = "rust1", since = "1.0.0")] impl From for io::Error { /// Converts a [`alloc::ffi::NulError`] into a [`io::Error`]. @@ -831,6 +757,7 @@ impl From for io::Error { } } +#[cfg(bootstrap)] #[stable(feature = "frombyteswithnulerror_impls", since = "1.17.0")] impl Error for core::ffi::FromBytesWithNulError { #[allow(deprecated)] @@ -839,12 +766,15 @@ impl Error for core::ffi::FromBytesWithNulError { } } +#[cfg(bootstrap)] #[unstable(feature = "cstr_from_bytes_until_nul", issue = "95027")] impl Error for core::ffi::FromBytesUntilNulError {} +#[cfg(bootstrap)] #[stable(feature = "cstring_from_vec_with_nul", since = "1.58.0")] impl Error for alloc::ffi::FromVecWithNulError {} +#[cfg(bootstrap)] #[stable(feature = "cstring_into", since = "1.7.0")] impl Error for alloc::ffi::IntoStringError { #[allow(deprecated)] @@ -857,6 +787,7 @@ impl Error for alloc::ffi::IntoStringError { } } +#[cfg(bootstrap)] impl<'a> dyn Error + 'a { /// Request a reference of type `T` as context about this error. #[unstable(feature = "error_generic_member_access", issue = "99301")] @@ -872,6 +803,7 @@ impl<'a> dyn Error + 'a { } // Copied from `any.rs`. +#[cfg(bootstrap)] impl dyn Error + 'static { /// Returns `true` if the inner type is the same as `T`. #[stable(feature = "error_downcast", since = "1.3.0")] @@ -912,6 +844,7 @@ impl dyn Error + 'static { } } +#[cfg(bootstrap)] impl dyn Error + 'static + Send { /// Forwards to the method defined on the type `dyn Error`. #[stable(feature = "error_downcast", since = "1.3.0")] @@ -947,6 +880,7 @@ impl dyn Error + 'static + Send { } } +#[cfg(bootstrap)] impl dyn Error + 'static + Send + Sync { /// Forwards to the method defined on the type `dyn Error`. #[stable(feature = "error_downcast", since = "1.3.0")] @@ -982,6 +916,7 @@ impl dyn Error + 'static + Send + Sync { } } +#[cfg(bootstrap)] impl dyn Error { #[inline] #[stable(feature = "error_downcast", since = "1.3.0")] @@ -1061,10 +996,12 @@ impl dyn Error { /// its sources, use `skip(1)`. #[unstable(feature = "error_iter", issue = "58520")] #[derive(Clone, Debug)] +#[cfg(bootstrap)] pub struct Chain<'a> { current: Option<&'a (dyn Error + 'static)>, } +#[cfg(bootstrap)] #[unstable(feature = "error_iter", issue = "58520")] impl<'a> Iterator for Chain<'a> { type Item = &'a (dyn Error + 'static); @@ -1076,6 +1013,7 @@ impl<'a> Iterator for Chain<'a> { } } +#[cfg(bootstrap)] impl dyn Error + Send { #[inline] #[stable(feature = "error_downcast", since = "1.3.0")] @@ -1089,6 +1027,7 @@ impl dyn Error + Send { } } +#[cfg(bootstrap)] impl dyn Error + Send + Sync { #[inline] #[stable(feature = "error_downcast", since = "1.3.0")] @@ -1246,7 +1185,7 @@ impl dyn Error + Send + Sync { /// # Err(SuperError { source: SuperErrorSideKick }) /// # } /// -/// fn main() -> Result<(), Report> { +/// fn main() -> Result<(), Report> { /// get_super_error()?; /// Ok(()) /// } @@ -1293,7 +1232,7 @@ impl dyn Error + Send + Sync { /// # Err(SuperError { source: SuperErrorSideKick }) /// # } /// -/// fn main() -> Result<(), Report> { +/// fn main() -> Result<(), Report> { /// get_super_error() /// .map_err(Report::from) /// .map_err(|r| r.pretty(true).show_backtrace(true))?; @@ -1605,72 +1544,6 @@ where } } -impl Report> { - fn backtrace(&self) -> Option<&Backtrace> { - // have to grab the backtrace on the first error directly since that error may not be - // 'static - let backtrace = self.error.request_ref(); - let backtrace = backtrace.or_else(|| { - self.error - .source() - .map(|source| source.chain().find_map(|source| source.request_ref())) - .flatten() - }); - backtrace - } - - /// Format the report as a single line. - #[unstable(feature = "error_reporter", issue = "90172")] - fn fmt_singleline(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.error)?; - - let sources = self.error.source().into_iter().flat_map(::chain); - - for cause in sources { - write!(f, ": {cause}")?; - } - - Ok(()) - } - - /// Format the report as multiple lines, with each error cause on its own line. - #[unstable(feature = "error_reporter", issue = "90172")] - fn fmt_multiline(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let error = &self.error; - - write!(f, "{error}")?; - - if let Some(cause) = error.source() { - write!(f, "\n\nCaused by:")?; - - let multiple = cause.source().is_some(); - - for (ind, error) in cause.chain().enumerate() { - writeln!(f)?; - let mut indented = Indented { inner: f }; - if multiple { - write!(indented, "{ind: >4}: {error}")?; - } else { - write!(indented, " {error}")?; - } - } - } - - if self.show_backtrace { - let backtrace = self.backtrace(); - - if let Some(backtrace) = backtrace { - let backtrace = backtrace.to_string(); - - f.write_str("\n\nStack backtrace:\n")?; - f.write_str(backtrace.trim_end())?; - } - } - - Ok(()) - } -} - #[unstable(feature = "error_reporter", issue = "90172")] impl From for Report where @@ -1681,17 +1554,6 @@ where } } -#[unstable(feature = "error_reporter", issue = "90172")] -impl<'a, E> From for Report> -where - E: Error + 'a, -{ - fn from(error: E) -> Self { - let error = box error; - Report { error, show_backtrace: false, pretty: false } - } -} - #[unstable(feature = "error_reporter", issue = "90172")] impl fmt::Display for Report where @@ -1702,13 +1564,6 @@ where } } -#[unstable(feature = "error_reporter", issue = "90172")] -impl fmt::Display for Report> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if self.pretty { self.fmt_multiline(f) } else { self.fmt_singleline(f) } - } -} - // This type intentionally outputs the same format for `Display` and `Debug`for // situations where you unwrap a `Report` or return it from main. #[unstable(feature = "error_reporter", issue = "90172")] diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index ff7fdcae16f53..885e44f5e18bd 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -76,6 +76,15 @@ impl fmt::Debug for Error { } } +#[cfg(not(bootstrap))] +#[stable(feature = "rust1", since = "1.0.0")] +impl From for Error { + /// Converts a [`alloc::ffi::NulError`] into a [`Error`]. + fn from(_: alloc::ffi::NulError) -> Error { + const_io_error!(ErrorKind::InvalidInput, "data provided contains a nul byte") + } +} + // Only derive debug in tests, to make sure it // doesn't accidentally get printed. #[cfg_attr(test, derive(Debug))] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index a8d6645794ae5..b6bf70ea377ae 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -269,6 +269,9 @@ #![feature(cstr_internals)] #![feature(duration_checked_float)] #![feature(duration_constants)] +#![cfg_attr(not(bootstrap), feature(error_generic_member_access))] +#![cfg_attr(not(bootstrap), feature(error_in_core))] +#![cfg_attr(not(bootstrap), feature(error_iter))] #![feature(exact_size_is_empty)] #![feature(exclusive_wrapper)] #![feature(extend_one)] diff --git a/src/test/ui/feature-gates/soft-syntax-gates-with-errors.rs b/src/test/ui/feature-gates/soft-syntax-gates-with-errors.rs new file mode 100644 index 0000000000000..49f1cba7151ef --- /dev/null +++ b/src/test/ui/feature-gates/soft-syntax-gates-with-errors.rs @@ -0,0 +1,30 @@ +// check-fail +// This file is used to test the behavior of the early-pass syntax warnings. +// If macro syntax is stabilized, replace with a different unstable syntax. + +macro a() {} +//~^ ERROR: `macro` is experimental + +#[cfg(FALSE)] +macro b() {} + +macro_rules! identity { + ($($x:tt)*) => ($($x)*); +} + +identity! { + macro c() {} + //~^ ERROR: `macro` is experimental +} + +#[cfg(FALSE)] +identity! { + macro d() {} // No error +} + +identity! { + #[cfg(FALSE)] + macro e() {} +} + +fn main() {} diff --git a/src/test/ui/feature-gates/soft-syntax-gates-with-errors.stderr b/src/test/ui/feature-gates/soft-syntax-gates-with-errors.stderr new file mode 100644 index 0000000000000..49550d811ba52 --- /dev/null +++ b/src/test/ui/feature-gates/soft-syntax-gates-with-errors.stderr @@ -0,0 +1,21 @@ +error[E0658]: `macro` is experimental + --> $DIR/soft-syntax-gates-with-errors.rs:5:1 + | +LL | macro a() {} + | ^^^^^^^^^^^^ + | + = note: see issue #39412 for more information + = help: add `#![feature(decl_macro)]` to the crate attributes to enable + +error[E0658]: `macro` is experimental + --> $DIR/soft-syntax-gates-with-errors.rs:16:5 + | +LL | macro c() {} + | ^^^^^^^^^^^^ + | + = note: see issue #39412 for more information + = help: add `#![feature(decl_macro)]` to the crate attributes to enable + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/feature-gates/soft-syntax-gates-without-errors.rs b/src/test/ui/feature-gates/soft-syntax-gates-without-errors.rs new file mode 100644 index 0000000000000..ca4ad2320f657 --- /dev/null +++ b/src/test/ui/feature-gates/soft-syntax-gates-without-errors.rs @@ -0,0 +1,26 @@ +// check-pass +// This file is used to test the behavior of the early-pass syntax warnings. +// If macro syntax is stabilized, replace with a different unstable syntax. + +#[cfg(FALSE)] +macro b() {} +//~^ WARN: `macro` is experimental +//~| WARN: unstable syntax + +macro_rules! identity { + ($($x:tt)*) => ($($x)*); +} + +#[cfg(FALSE)] +identity! { + macro d() {} // No error +} + +identity! { + #[cfg(FALSE)] + macro e() {} + //~^ WARN: `macro` is experimental + //~| WARN: unstable syntax +} + +fn main() {} diff --git a/src/test/ui/feature-gates/soft-syntax-gates-without-errors.stderr b/src/test/ui/feature-gates/soft-syntax-gates-without-errors.stderr new file mode 100644 index 0000000000000..3d9c22e548710 --- /dev/null +++ b/src/test/ui/feature-gates/soft-syntax-gates-without-errors.stderr @@ -0,0 +1,24 @@ +warning: `macro` is experimental + --> $DIR/soft-syntax-gates-without-errors.rs:6:1 + | +LL | macro b() {} + | ^^^^^^^^^^^^ + | + = note: see issue #39412 for more information + = help: add `#![feature(decl_macro)]` to the crate attributes to enable + = warning: unstable syntax can change at any point in the future, causing a hard error! + = note: for more information, see issue #65860 + +warning: `macro` is experimental + --> $DIR/soft-syntax-gates-without-errors.rs:21:5 + | +LL | macro e() {} + | ^^^^^^^^^^^^ + | + = note: see issue #39412 for more information + = help: add `#![feature(decl_macro)]` to the crate attributes to enable + = warning: unstable syntax can change at any point in the future, causing a hard error! + = note: for more information, see issue #65860 + +warning: 2 warnings emitted + diff --git a/src/test/ui/implied-bounds/issue-100690.rs b/src/test/ui/implied-bounds/issue-100690.rs new file mode 100644 index 0000000000000..5599cd410baad --- /dev/null +++ b/src/test/ui/implied-bounds/issue-100690.rs @@ -0,0 +1,45 @@ +// This code (probably) _should_ compile, but it currently does not because we +// are not smart enough about implied bounds. + +use std::io; + +fn real_dispatch(f: F) -> Result<(), io::Error> +//~^ NOTE required by a bound in this +where + F: FnOnce(&mut UIView) -> Result<(), io::Error> + Send + 'static, + //~^ NOTE required by this bound in `real_dispatch` + //~| NOTE required by a bound in `real_dispatch` +{ + todo!() +} + +#[derive(Debug)] +struct UIView<'a, T: 'a> { + _phantom: std::marker::PhantomData<&'a mut T>, +} + +trait Handle<'a, T: 'a, V, R> { + fn dispatch(&self, f: F) -> Result<(), io::Error> + where + F: FnOnce(&mut V) -> R + Send + 'static; +} + +#[derive(Debug, Clone)] +struct TUIHandle { + _phantom: std::marker::PhantomData, +} + +impl<'a, T: 'a> Handle<'a, T, UIView<'a, T>, Result<(), io::Error>> for TUIHandle { + fn dispatch(&self, f: F) -> Result<(), io::Error> + where + F: FnOnce(&mut UIView<'a, T>) -> Result<(), io::Error> + Send + 'static, + { + real_dispatch(f) + //~^ ERROR expected a `FnOnce<(&mut UIView<'_, T>,)>` closure, found `F` + //~| NOTE expected an `FnOnce<(&mut UIView<'_, T>,)>` closure, found `F` + //~| NOTE expected a closure with arguments + //~| NOTE required by a bound introduced by this call + } +} + +fn main() {} diff --git a/src/test/ui/implied-bounds/issue-100690.stderr b/src/test/ui/implied-bounds/issue-100690.stderr new file mode 100644 index 0000000000000..3f6af70d8ed93 --- /dev/null +++ b/src/test/ui/implied-bounds/issue-100690.stderr @@ -0,0 +1,22 @@ +error[E0277]: expected a `FnOnce<(&mut UIView<'_, T>,)>` closure, found `F` + --> $DIR/issue-100690.rs:37:23 + | +LL | real_dispatch(f) + | ------------- ^ expected an `FnOnce<(&mut UIView<'_, T>,)>` closure, found `F` + | | + | required by a bound introduced by this call + | + = note: expected a closure with arguments `(&mut UIView<'a, T>,)` + found a closure with arguments `(&mut UIView<'_, T>,)` +note: required by a bound in `real_dispatch` + --> $DIR/issue-100690.rs:9:8 + | +LL | fn real_dispatch(f: F) -> Result<(), io::Error> + | ------------- required by a bound in this +... +LL | F: FnOnce(&mut UIView) -> Result<(), io::Error> + Send + 'static, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `real_dispatch` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/issues/issue-100605.rs b/src/test/ui/issues/issue-100605.rs new file mode 100644 index 0000000000000..917a45c15bbc8 --- /dev/null +++ b/src/test/ui/issues/issue-100605.rs @@ -0,0 +1,9 @@ +fn takes_option(_arg: Option<&String>) {} + +fn main() { + takes_option(&None); //~ ERROR 4:18: 4:23: mismatched types [E0308] + + let x = String::from("x"); + let res = Some(x); + takes_option(&res); //~ ERROR 8:18: 8:22: mismatched types [E0308] +} diff --git a/src/test/ui/issues/issue-100605.stderr b/src/test/ui/issues/issue-100605.stderr new file mode 100644 index 0000000000000..886e3cd6bb794 --- /dev/null +++ b/src/test/ui/issues/issue-100605.stderr @@ -0,0 +1,46 @@ +error[E0308]: mismatched types + --> $DIR/issue-100605.rs:4:18 + | +LL | takes_option(&None); + | ------------ ^^^^^ expected enum `Option`, found `&Option<_>` + | | + | arguments to this function are incorrect + | + = note: expected enum `Option<&String>` + found reference `&Option<_>` +note: function defined here + --> $DIR/issue-100605.rs:1:4 + | +LL | fn takes_option(_arg: Option<&String>) {} + | ^^^^^^^^^^^^ --------------------- +help: you can convert from `&Option` to `Option<&T>` using `.as_ref()` + | +LL | takes_option(None.as_ref()); + | ~~~~~~~~~~~~~ +help: consider removing the borrow + | +LL - takes_option(&None); +LL + takes_option(None); + | + +error[E0308]: mismatched types + --> $DIR/issue-100605.rs:8:18 + | +LL | takes_option(&res); + | ------------ ^^^^ + | | | + | | expected enum `Option`, found `&Option` + | | help: you can convert from `&Option` to `Option<&T>` using `.as_ref()`: `res.as_ref()` + | arguments to this function are incorrect + | + = note: expected enum `Option<&String>` + found reference `&Option` +note: function defined here + --> $DIR/issue-100605.rs:1:4 + | +LL | fn takes_option(_arg: Option<&String>) {} + | ^^^^^^^^^^^^ --------------------- + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/macros/stringify.rs b/src/test/ui/macros/stringify.rs index 8e71ed7c1120f..082c1abb8f2f4 100644 --- a/src/test/ui/macros/stringify.rs +++ b/src/test/ui/macros/stringify.rs @@ -3,11 +3,18 @@ // compile-flags: --test #![feature(async_closure)] +#![feature(box_patterns)] +#![feature(box_syntax)] #![feature(const_trait_impl)] +#![feature(decl_macro)] #![feature(generators)] #![feature(half_open_range_patterns)] +#![feature(label_break_value)] #![feature(more_qualified_paths)] #![feature(raw_ref_op)] +#![feature(trait_alias)] +#![feature(try_blocks)] +#![feature(type_ascription)] #![deny(unused_macros)] macro_rules! stringify_block { diff --git a/src/test/ui/or-patterns/or-patterns-syntactic-pass.rs b/src/test/ui/or-patterns/or-patterns-syntactic-pass.rs index 6f9a631b092a7..dda5c0bb59d23 100644 --- a/src/test/ui/or-patterns/or-patterns-syntactic-pass.rs +++ b/src/test/ui/or-patterns/or-patterns-syntactic-pass.rs @@ -7,7 +7,7 @@ fn main() {} // Test the `pat` macro fragment parser: macro_rules! accept_pat { - ($p:pat) => {} + ($p:pat) => {}; } accept_pat!((p | q)); @@ -21,28 +21,28 @@ accept_pat!([p | q]); #[cfg(FALSE)] fn or_patterns() { // Top level of `let`: - let (| A | B); + let (A | B); let (A | B); let (A | B): u8; let (A | B) = 0; let (A | B): u8 = 0; // Top level of `for`: - for | A | B in 0 {} + for A | B in 0 {} for A | B in 0 {} // Top level of `while`: - while let | A | B = 0 {} + while let A | B = 0 {} while let A | B = 0 {} // Top level of `if`: - if let | A | B = 0 {} + if let A | B = 0 {} if let A | B = 0 {} // Top level of `match` arms: match 0 { - | A | B => {}, - A | B => {}, + A | B => {} + A | B => {} } // Functions: @@ -68,6 +68,8 @@ fn or_patterns() { // These bind as `(prefix p) | q` as opposed to `prefix (p | q)`: let (box 0 | 1); // Unstable; we *can* change the precedence if we want. + //~^ WARN box pattern syntax is experimental + //~| WARN unstable syntax let (&0 | 1); let (&mut 0 | 1); let (x @ 0 | 1); diff --git a/src/test/ui/or-patterns/or-patterns-syntactic-pass.stderr b/src/test/ui/or-patterns/or-patterns-syntactic-pass.stderr new file mode 100644 index 0000000000000..c43fe192a73b8 --- /dev/null +++ b/src/test/ui/or-patterns/or-patterns-syntactic-pass.stderr @@ -0,0 +1,13 @@ +warning: box pattern syntax is experimental + --> $DIR/or-patterns-syntactic-pass.rs:70:10 + | +LL | let (box 0 | 1); // Unstable; we *can* change the precedence if we want. + | ^^^^^ + | + = note: see issue #29641 for more information + = help: add `#![feature(box_patterns)]` to the crate attributes to enable + = warning: unstable syntax can change at any point in the future, causing a hard error! + = note: for more information, see issue #65860 + +warning: 1 warning emitted + diff --git a/src/test/ui/parser/constraints-before-generic-args-syntactic-pass.rs b/src/test/ui/parser/constraints-before-generic-args-syntactic-pass.rs index afbd13e6fd9a8..d8346653c25aa 100644 --- a/src/test/ui/parser/constraints-before-generic-args-syntactic-pass.rs +++ b/src/test/ui/parser/constraints-before-generic-args-syntactic-pass.rs @@ -3,7 +3,11 @@ #[cfg(FALSE)] fn syntax() { foo::(); + //~^ WARN associated type bounds are unstable + //~| WARN unstable syntax foo::(); + //~^ WARN associated type bounds are unstable + //~| WARN unstable syntax } fn main() {} diff --git a/src/test/ui/parser/constraints-before-generic-args-syntactic-pass.stderr b/src/test/ui/parser/constraints-before-generic-args-syntactic-pass.stderr new file mode 100644 index 0000000000000..7e843c7f4d006 --- /dev/null +++ b/src/test/ui/parser/constraints-before-generic-args-syntactic-pass.stderr @@ -0,0 +1,24 @@ +warning: associated type bounds are unstable + --> $DIR/constraints-before-generic-args-syntactic-pass.rs:5:19 + | +LL | foo::(); + | ^^^^^^ + | + = note: see issue #52662 for more information + = help: add `#![feature(associated_type_bounds)]` to the crate attributes to enable + = warning: unstable syntax can change at any point in the future, causing a hard error! + = note: for more information, see issue #65860 + +warning: associated type bounds are unstable + --> $DIR/constraints-before-generic-args-syntactic-pass.rs:8:23 + | +LL | foo::(); + | ^^^^^^ + | + = note: see issue #52662 for more information + = help: add `#![feature(associated_type_bounds)]` to the crate attributes to enable + = warning: unstable syntax can change at any point in the future, causing a hard error! + = note: for more information, see issue #65860 + +warning: 2 warnings emitted + diff --git a/src/test/ui/parser/public-instead-of-pub-1.fixed b/src/test/ui/parser/public-instead-of-pub-1.fixed new file mode 100644 index 0000000000000..a4fa68ba5cc2f --- /dev/null +++ b/src/test/ui/parser/public-instead-of-pub-1.fixed @@ -0,0 +1,11 @@ +// Checks what happens when `public` is used instead of the correct, `pub` +// run-rustfix + +pub enum Test { + //~^ ERROR expected one of `!` or `::`, found keyword `enum` + //~^^ HELP write `pub` instead of `public` to make the item public + A, + B, +} + +fn main() { } diff --git a/src/test/ui/parser/public-instead-of-pub-1.rs b/src/test/ui/parser/public-instead-of-pub-1.rs new file mode 100644 index 0000000000000..43565c9b1d25f --- /dev/null +++ b/src/test/ui/parser/public-instead-of-pub-1.rs @@ -0,0 +1,11 @@ +// Checks what happens when `public` is used instead of the correct, `pub` +// run-rustfix + +public enum Test { + //~^ ERROR expected one of `!` or `::`, found keyword `enum` + //~^^ HELP write `pub` instead of `public` to make the item public + A, + B, +} + +fn main() { } diff --git a/src/test/ui/parser/public-instead-of-pub-1.stderr b/src/test/ui/parser/public-instead-of-pub-1.stderr new file mode 100644 index 0000000000000..795a5bcf5dfa6 --- /dev/null +++ b/src/test/ui/parser/public-instead-of-pub-1.stderr @@ -0,0 +1,13 @@ +error: expected one of `!` or `::`, found keyword `enum` + --> $DIR/public-instead-of-pub-1.rs:4:8 + | +LL | public enum Test { + | ^^^^ expected one of `!` or `::` + | +help: write `pub` instead of `public` to make the item public + | +LL | pub enum Test { + | ~~~ + +error: aborting due to previous error + diff --git a/src/test/ui/parser/public-instead-of-pub-2.rs b/src/test/ui/parser/public-instead-of-pub-2.rs new file mode 100644 index 0000000000000..8a43c361e0510 --- /dev/null +++ b/src/test/ui/parser/public-instead-of-pub-2.rs @@ -0,0 +1,7 @@ +// Checks what happens when `public` is used instead of the correct, `pub` +// Won't give help message for this case + +public let x = 1; +//~^ ERROR expected one of `!` or `::`, found keyword `let` + +fn main() { } diff --git a/src/test/ui/parser/public-instead-of-pub-2.stderr b/src/test/ui/parser/public-instead-of-pub-2.stderr new file mode 100644 index 0000000000000..efe225656fd7f --- /dev/null +++ b/src/test/ui/parser/public-instead-of-pub-2.stderr @@ -0,0 +1,8 @@ +error: expected one of `!` or `::`, found keyword `let` + --> $DIR/public-instead-of-pub-2.rs:4:8 + | +LL | public let x = 1; + | ^^^ expected one of `!` or `::` + +error: aborting due to previous error + diff --git a/src/test/ui/pattern/rest-pat-syntactic.rs b/src/test/ui/pattern/rest-pat-syntactic.rs index 9656a0b5de9ce..4da5a2db76743 100644 --- a/src/test/ui/pattern/rest-pat-syntactic.rs +++ b/src/test/ui/pattern/rest-pat-syntactic.rs @@ -19,6 +19,8 @@ fn rest_patterns() { // Box patterns: let box ..; + //~^ WARN box pattern syntax is experimental + //~| WARN unstable syntax // In or-patterns: match x { @@ -57,7 +59,7 @@ fn rest_patterns() { .. | [ ( - box .., + box .., //~ WARN box pattern syntax is experimental &(..), &mut .., x @ .. @@ -67,4 +69,5 @@ fn rest_patterns() { ref mut x @ .. => {} } + //~| WARN unstable syntax } diff --git a/src/test/ui/pattern/rest-pat-syntactic.stderr b/src/test/ui/pattern/rest-pat-syntactic.stderr new file mode 100644 index 0000000000000..37019b7d5ba7a --- /dev/null +++ b/src/test/ui/pattern/rest-pat-syntactic.stderr @@ -0,0 +1,24 @@ +warning: box pattern syntax is experimental + --> $DIR/rest-pat-syntactic.rs:21:9 + | +LL | let box ..; + | ^^^^^^ + | + = note: see issue #29641 for more information + = help: add `#![feature(box_patterns)]` to the crate attributes to enable + = warning: unstable syntax can change at any point in the future, causing a hard error! + = note: for more information, see issue #65860 + +warning: box pattern syntax is experimental + --> $DIR/rest-pat-syntactic.rs:62:17 + | +LL | box .., + | ^^^^^^ + | + = note: see issue #29641 for more information + = help: add `#![feature(box_patterns)]` to the crate attributes to enable + = warning: unstable syntax can change at any point in the future, causing a hard error! + = note: for more information, see issue #65860 + +warning: 2 warnings emitted + diff --git a/src/test/ui/suggestions/many-type-ascription.rs b/src/test/ui/suggestions/many-type-ascription.rs new file mode 100644 index 0000000000000..31ac556b9447c --- /dev/null +++ b/src/test/ui/suggestions/many-type-ascription.rs @@ -0,0 +1,4 @@ +fn main() { + let _ = 0: i32; //~ ERROR: type ascription is experimental + let _ = 0: i32; // (error only emitted once) +} diff --git a/src/test/ui/suggestions/many-type-ascription.stderr b/src/test/ui/suggestions/many-type-ascription.stderr new file mode 100644 index 0000000000000..3706bbae9df9f --- /dev/null +++ b/src/test/ui/suggestions/many-type-ascription.stderr @@ -0,0 +1,12 @@ +error[E0658]: type ascription is experimental + --> $DIR/many-type-ascription.rs:2:13 + | +LL | let _ = 0: i32; + | ^^^^^^ + | + = note: see issue #23416 for more information + = help: add `#![feature(type_ascription)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/suggestions/type-ascription-and-other-error.rs b/src/test/ui/suggestions/type-ascription-and-other-error.rs new file mode 100644 index 0000000000000..99ab2f3c858b9 --- /dev/null +++ b/src/test/ui/suggestions/type-ascription-and-other-error.rs @@ -0,0 +1,6 @@ +fn main() { + not rust; //~ ERROR + let _ = 0: i32; // (error hidden by existing error) + #[cfg(FALSE)] + let _ = 0: i32; // (warning hidden by existing error) +} diff --git a/src/test/ui/suggestions/type-ascription-and-other-error.stderr b/src/test/ui/suggestions/type-ascription-and-other-error.stderr new file mode 100644 index 0000000000000..eadf634bb14fd --- /dev/null +++ b/src/test/ui/suggestions/type-ascription-and-other-error.stderr @@ -0,0 +1,8 @@ +error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `rust` + --> $DIR/type-ascription-and-other-error.rs:2:9 + | +LL | not rust; + | ^^^^ expected one of 8 possible tokens + +error: aborting due to previous error + diff --git a/triagebot.toml b/triagebot.toml index b7532e016b68e..4d3f748195b80 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -129,6 +129,7 @@ trigger_files = [ # Internal tooling "src/etc/htmldocck.py", + "src/etc/check_missing_items.py", "src/tools/jsondocck", "src/tools/rustdoc-gui", "src/tools/rustdoc-js", @@ -138,6 +139,15 @@ exclude_labels = [ "T-*", ] +[autolabel."A-rustdoc-json"] +trigger_files = [ + "src/etc/check_missing_items.py", + "src/librustdoc/json/", + "src/rustdoc-json-types", + "src/test/rustdoc-json", + "src/tools/jsondocck", +] + [autolabel."T-compiler"] trigger_files = [ # Source code