diff --git a/Cargo.lock b/Cargo.lock index 7be90aaf9e6fb..a500549fb53fb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4086,6 +4086,7 @@ dependencies = [ "rustc_hir_pretty", "rustc_index", "rustc_infer", + "rustc_macros", "rustc_middle", "rustc_session", "rustc_span", diff --git a/compiler/rustc_error_codes/src/error_codes/E0224.md b/compiler/rustc_error_codes/src/error_codes/E0224.md index fd89c1d52560f..628488575b2f8 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0224.md +++ b/compiler/rustc_error_codes/src/error_codes/E0224.md @@ -1,4 +1,4 @@ -A trait object was declaired with no traits. +A trait object was declared with no traits. Erroneous code example: @@ -8,7 +8,7 @@ type Foo = dyn 'static +; Rust does not currently support this. -To solve ensure the the trait object has at least one trait: +To solve, ensure that the trait object has at least one trait: ``` type Foo = dyn 'static + Copy; diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs index 7fb3b0e7ea6ac..5c28839c9b7e4 100644 --- a/compiler/rustc_macros/src/lib.rs +++ b/compiler/rustc_macros/src/lib.rs @@ -1,3 +1,4 @@ +#![feature(proc_macro_diagnostic)] #![allow(rustc::default_hash_types)] #![recursion_limit = "128"] @@ -9,6 +10,7 @@ mod hash_stable; mod lift; mod query; mod serialize; +mod session_diagnostic; mod symbols; mod type_foldable; @@ -36,3 +38,14 @@ decl_derive!([MetadataDecodable] => serialize::meta_decodable_derive); decl_derive!([MetadataEncodable] => serialize::meta_encodable_derive); decl_derive!([TypeFoldable, attributes(type_foldable)] => type_foldable::type_foldable_derive); decl_derive!([Lift, attributes(lift)] => lift::lift_derive); +decl_derive!( + [SessionDiagnostic, attributes( + message, + lint, + error, + label, + suggestion, + suggestion_short, + suggestion_hidden, + suggestion_verbose)] => session_diagnostic::session_diagnostic_derive +); diff --git a/compiler/rustc_macros/src/session_diagnostic.rs b/compiler/rustc_macros/src/session_diagnostic.rs new file mode 100644 index 0000000000000..396de77d5eee0 --- /dev/null +++ b/compiler/rustc_macros/src/session_diagnostic.rs @@ -0,0 +1,665 @@ +#![deny(unused_must_use)] +use quote::format_ident; +use quote::quote; + +use proc_macro::Diagnostic; +use syn::spanned::Spanned; + +use std::collections::{HashMap, HashSet}; + +/// Implements #[derive(SessionDiagnostic)], which allows for errors to be specified as a struct, independent +/// from the actual diagnostics emitting code. +/// ```ignore (pseudo-rust) +/// # extern crate rustc_errors; +/// # use rustc_errors::Applicability; +/// # extern crate rustc_span; +/// # use rustc_span::{symbol::Ident, Span}; +/// # extern crate rust_middle; +/// # use rustc_middle::ty::Ty; +/// #[derive(SessionDiagnostic)] +/// #[code = "E0505"] +/// #[error = "cannot move out of {name} because it is borrowed"] +/// pub struct MoveOutOfBorrowError<'tcx> { +/// pub name: Ident, +/// pub ty: Ty<'tcx>, +/// #[label = "cannot move out of borrow"] +/// pub span: Span, +/// #[label = "`{ty}` first borrowed here"] +/// pub other_span: Span, +/// #[suggestion(message = "consider cloning here", code = "{name}.clone()")] +/// pub opt_sugg: Option<(Span, Applicability)> +/// } +/// ``` +/// Then, later, to emit the error: +/// +/// ```ignore (pseudo-rust) +/// sess.emit_err(MoveOutOfBorrowError { +/// expected, +/// actual, +/// span, +/// other_span, +/// opt_sugg: Some(suggestion, Applicability::MachineApplicable), +/// }); +/// ``` +pub fn session_diagnostic_derive(s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { + // Names for the diagnostic we build and the session we build it from. + let diag = format_ident!("diag"); + let sess = format_ident!("sess"); + + SessionDiagnosticDerive::new(diag, sess, s).into_tokens() +} + +// Checks whether the type name of `ty` matches `name`. +// +// Given some struct at a::b::c::Foo, this will return true for c::Foo, b::c::Foo, or +// a::b::c::Foo. This reasonably allows qualified names to be used in the macro. +fn type_matches_path(ty: &syn::Type, name: &[&str]) -> bool { + if let syn::Type::Path(ty) = ty { + ty.path + .segments + .iter() + .map(|s| s.ident.to_string()) + .rev() + .zip(name.iter().rev()) + .all(|(x, y)| &x.as_str() == y) + } else { + false + } +} + +/// The central struct for constructing the as_error method from an annotated struct. +struct SessionDiagnosticDerive<'a> { + structure: synstructure::Structure<'a>, + builder: SessionDiagnosticDeriveBuilder<'a>, +} + +impl std::convert::From for SessionDiagnosticDeriveError { + fn from(e: syn::Error) -> Self { + SessionDiagnosticDeriveError::SynError(e) + } +} + +/// Equivalent to rustc:errors::diagnostic::DiagnosticId, except stores the quoted expression to +/// initialise the code with. +enum DiagnosticId { + Error(proc_macro2::TokenStream), + Lint(proc_macro2::TokenStream), +} + +#[derive(Debug)] +enum SessionDiagnosticDeriveError { + SynError(syn::Error), + ErrorHandled, +} + +impl SessionDiagnosticDeriveError { + fn to_compile_error(self) -> proc_macro2::TokenStream { + match self { + SessionDiagnosticDeriveError::SynError(e) => e.to_compile_error(), + SessionDiagnosticDeriveError::ErrorHandled => { + // Return ! to avoid having to create a blank DiagnosticBuilder to return when an + // error has already been emitted to the compiler. + quote! { + unreachable!() + } + } + } + } +} + +fn span_err(span: impl proc_macro::MultiSpan, msg: &str) -> proc_macro::Diagnostic { + Diagnostic::spanned(span, proc_macro::Level::Error, msg) +} + +/// For methods that return a Result<_, SessionDiagnosticDeriveError>: emit a diagnostic on +/// span $span with msg $msg (and, optionally, perform additional decoration using the FnOnce +/// passed in `diag`). Then, return Err(ErrorHandled). +macro_rules! throw_span_err { + ($span:expr, $msg:expr) => {{ throw_span_err!($span, $msg, |diag| diag) }}; + ($span:expr, $msg:expr, $f:expr) => {{ + return Err(_throw_span_err($span, $msg, $f)); + }}; +} + +/// When possible, prefer using throw_span_err! over using this function directly. This only exists +/// as a function to constrain `f` to an impl FnOnce. +fn _throw_span_err( + span: impl proc_macro::MultiSpan, + msg: &str, + f: impl FnOnce(proc_macro::Diagnostic) -> proc_macro::Diagnostic, +) -> SessionDiagnosticDeriveError { + let diag = span_err(span, msg); + f(diag).emit(); + SessionDiagnosticDeriveError::ErrorHandled +} + +impl<'a> SessionDiagnosticDerive<'a> { + fn new(diag: syn::Ident, sess: syn::Ident, structure: synstructure::Structure<'a>) -> Self { + // Build the mapping of field names to fields. This allows attributes to peek values from + // other fields. + let mut fields_map = HashMap::new(); + + // Convenience bindings. + let ast = structure.ast(); + + if let syn::Data::Struct(syn::DataStruct { fields, .. }) = &ast.data { + for field in fields.iter() { + if let Some(ident) = &field.ident { + fields_map.insert(ident.to_string(), field); + } + } + } + + Self { + builder: SessionDiagnosticDeriveBuilder { diag, sess, fields: fields_map, kind: None }, + structure, + } + } + fn into_tokens(self) -> proc_macro2::TokenStream { + let SessionDiagnosticDerive { structure, mut builder } = self; + + let ast = structure.ast(); + let attrs = &ast.attrs; + + let implementation = { + if let syn::Data::Struct(..) = ast.data { + let preamble = { + let preamble = attrs.iter().map(|attr| { + builder + .generate_structure_code(attr) + .unwrap_or_else(|v| v.to_compile_error()) + }); + quote! { + #(#preamble)*; + } + }; + + let body = structure.each(|field_binding| { + let field = field_binding.ast(); + let result = field.attrs.iter().map(|attr| { + builder + .generate_field_code( + attr, + FieldInfo { + vis: &field.vis, + binding: field_binding, + ty: &field.ty, + span: &field.span(), + }, + ) + .unwrap_or_else(|v| v.to_compile_error()) + }); + return quote! { + #(#result);* + }; + }); + // Finally, putting it altogether. + match builder.kind { + None => { + span_err(ast.span().unwrap(), "`code` not specified") + .help("use the [code = \"...\"] attribute to set this diagnostic's error code ") + .emit(); + SessionDiagnosticDeriveError::ErrorHandled.to_compile_error() + } + Some((kind, _)) => match kind { + DiagnosticId::Lint(_lint) => todo!(), + DiagnosticId::Error(code) => { + let (diag, sess) = (&builder.diag, &builder.sess); + quote! { + let mut #diag = #sess.struct_err_with_code("", rustc_errors::DiagnosticId::Error(#code)); + #preamble + match self { + #body + } + #diag + } + } + }, + } + } else { + span_err( + ast.span().unwrap(), + "`#[derive(SessionDiagnostic)]` can only be used on structs", + ) + .emit(); + SessionDiagnosticDeriveError::ErrorHandled.to_compile_error() + } + }; + + let sess = &builder.sess; + structure.gen_impl(quote! { + gen impl<'__session_diagnostic_sess> rustc_session::SessionDiagnostic<'__session_diagnostic_sess> + for @Self + { + fn into_diagnostic( + self, + #sess: &'__session_diagnostic_sess rustc_session::Session + ) -> rustc_errors::DiagnosticBuilder<'__session_diagnostic_sess> { + #implementation + } + } + }) + } +} + +/// Field information passed to the builder. Deliberately omits attrs to discourage the generate_* +/// methods from walking the attributes themselves. +struct FieldInfo<'a> { + vis: &'a syn::Visibility, + binding: &'a synstructure::BindingInfo<'a>, + ty: &'a syn::Type, + span: &'a proc_macro2::Span, +} + +/// Tracks persistent information required for building up the individual calls to diagnostic +/// methods for the final generated method. This is a separate struct to SessionDerive only to be +/// able to destructure and split self.builder and the self.structure up to avoid a double mut +/// borrow later on. +struct SessionDiagnosticDeriveBuilder<'a> { + /// Name of the session parameter that's passed in to the as_error method. + sess: syn::Ident, + + /// Store a map of field name to its corresponding field. This is built on construction of the + /// derive builder. + fields: HashMap, + + /// The identifier to use for the generated DiagnosticBuilder instance. + diag: syn::Ident, + + /// Whether this is a lint or an error. This dictates how the diag will be initialised. Span + /// stores at what Span the kind was first set at (for error reporting purposes, if the kind + /// was multiply specified). + kind: Option<(DiagnosticId, proc_macro2::Span)>, +} + +impl<'a> SessionDiagnosticDeriveBuilder<'a> { + fn generate_structure_code( + &mut self, + attr: &syn::Attribute, + ) -> Result { + Ok(match attr.parse_meta()? { + syn::Meta::NameValue(syn::MetaNameValue { lit: syn::Lit::Str(s), .. }) => { + let formatted_str = self.build_format(&s.value(), attr.span()); + let name = attr.path.segments.last().unwrap().ident.to_string(); + let name = name.as_str(); + match name { + "message" => { + let diag = &self.diag; + quote! { + #diag.set_primary_message(#formatted_str); + } + } + attr @ "error" | attr @ "lint" => { + self.set_kind_once( + if attr == "error" { + DiagnosticId::Error(formatted_str) + } else if attr == "lint" { + DiagnosticId::Lint(formatted_str) + } else { + unreachable!() + }, + s.span(), + )?; + // This attribute is only allowed to be applied once, and the attribute + // will be set in the initialisation code. + quote! {} + } + other => throw_span_err!( + attr.span().unwrap(), + &format!( + "`#[{} = ...]` is not a valid SessionDiagnostic struct attribute", + other + ) + ), + } + } + _ => todo!("unhandled meta kind"), + }) + } + + #[must_use] + fn set_kind_once( + &mut self, + kind: DiagnosticId, + span: proc_macro2::Span, + ) -> Result<(), SessionDiagnosticDeriveError> { + if self.kind.is_none() { + self.kind = Some((kind, span)); + Ok(()) + } else { + let kind_str = |kind: &DiagnosticId| match kind { + DiagnosticId::Lint(..) => "lint", + DiagnosticId::Error(..) => "error", + }; + + let existing_kind = kind_str(&self.kind.as_ref().unwrap().0); + let this_kind = kind_str(&kind); + + let msg = if this_kind == existing_kind { + format!("`{}` specified multiple times", existing_kind) + } else { + format!("`{}` specified when `{}` was already specified", this_kind, existing_kind) + }; + throw_span_err!(span.unwrap(), &msg); + } + } + + fn generate_field_code( + &mut self, + attr: &syn::Attribute, + info: FieldInfo<'_>, + ) -> Result { + let field_binding = &info.binding.binding; + + let option_ty = option_inner_ty(&info.ty); + + let generated_code = self.generate_non_option_field_code( + attr, + FieldInfo { + vis: info.vis, + binding: info.binding, + ty: option_ty.unwrap_or(&info.ty), + span: info.span, + }, + )?; + Ok(if option_ty.is_none() { + quote! { #generated_code } + } else { + quote! { + if let Some(#field_binding) = #field_binding { + #generated_code + } + } + }) + } + + fn generate_non_option_field_code( + &mut self, + attr: &syn::Attribute, + info: FieldInfo<'_>, + ) -> Result { + let diag = &self.diag; + let field_binding = &info.binding.binding; + let name = attr.path.segments.last().unwrap().ident.to_string(); + let name = name.as_str(); + // At this point, we need to dispatch based on the attribute key + the + // type. + let meta = attr.parse_meta()?; + Ok(match meta { + syn::Meta::NameValue(syn::MetaNameValue { lit: syn::Lit::Str(s), .. }) => { + let formatted_str = self.build_format(&s.value(), attr.span()); + match name { + "message" => { + if type_matches_path(&info.ty, &["rustc_span", "Span"]) { + quote! { + #diag.set_span(*#field_binding); + #diag.set_primary_message(#formatted_str); + } + } else { + throw_span_err!( + attr.span().unwrap(), + "the `#[message = \"...\"]` attribute can only be applied to fields of type Span" + ); + } + } + "label" => { + if type_matches_path(&info.ty, &["rustc_span", "Span"]) { + quote! { + #diag.span_label(*#field_binding, #formatted_str); + } + } else { + throw_span_err!( + attr.span().unwrap(), + "The `#[label = ...]` attribute can only be applied to fields of type Span" + ); + } + } + other => throw_span_err!( + attr.span().unwrap(), + &format!( + "`#[{} = ...]` is not a valid SessionDiagnostic field attribute", + other + ) + ), + } + } + syn::Meta::List(list) => { + match list.path.segments.iter().last().unwrap().ident.to_string().as_str() { + suggestion_kind @ "suggestion" + | suggestion_kind @ "suggestion_short" + | suggestion_kind @ "suggestion_hidden" + | suggestion_kind @ "suggestion_verbose" => { + // For suggest, we need to ensure we are running on a (Span, + // Applicability) pair. + let (span, applicability) = (|| match &info.ty { + ty @ syn::Type::Path(..) + if type_matches_path(ty, &["rustc_span", "Span"]) => + { + let binding = &info.binding.binding; + Ok(( + quote!(*#binding), + quote!(rustc_errors::Applicability::Unspecified), + )) + } + syn::Type::Tuple(tup) => { + let mut span_idx = None; + let mut applicability_idx = None; + for (idx, elem) in tup.elems.iter().enumerate() { + if type_matches_path(elem, &["rustc_span", "Span"]) { + if span_idx.is_none() { + span_idx = Some(syn::Index::from(idx)); + } else { + throw_span_err!( + info.span.clone().unwrap(), + "type of field annotated with `#[suggestion(...)]` contains more than one Span" + ); + } + } else if type_matches_path( + elem, + &["rustc_errors", "Applicability"], + ) { + if applicability_idx.is_none() { + applicability_idx = Some(syn::Index::from(idx)); + } else { + throw_span_err!( + info.span.clone().unwrap(), + "type of field annotated with `#[suggestion(...)]` contains more than one Applicability" + ); + } + } + } + if let Some(span_idx) = span_idx { + let binding = &info.binding.binding; + let span = quote!(#binding.#span_idx); + let applicability = applicability_idx + .map( + |applicability_idx| quote!(#binding.#applicability_idx), + ) + .unwrap_or(quote!( + rustc_errors::Applicability::Unspecified + )); + return Ok((span, applicability)); + } + throw_span_err!( + info.span.clone().unwrap(), + "wrong types for suggestion", + |diag| { + diag.help("#[suggestion(...)] on a tuple field must be applied to fields of type (Span, Applicability)") + } + ); + } + _ => throw_span_err!( + info.span.clone().unwrap(), + "wrong field type for suggestion", + |diag| { + diag.help("#[suggestion(...)] should be applied to fields of type Span or (Span, Applicability)") + } + ), + })()?; + // Now read the key-value pairs. + let mut msg = None; + let mut code = None; + + for arg in list.nested.iter() { + if let syn::NestedMeta::Meta(syn::Meta::NameValue(arg_name_value)) = arg + { + if let syn::MetaNameValue { lit: syn::Lit::Str(s), .. } = + arg_name_value + { + let name = arg_name_value + .path + .segments + .last() + .unwrap() + .ident + .to_string(); + let name = name.as_str(); + let formatted_str = self.build_format(&s.value(), arg.span()); + match name { + "message" => { + msg = Some(formatted_str); + } + "code" => { + code = Some(formatted_str); + } + other => throw_span_err!( + arg.span().unwrap(), + &format!( + "`{}` is not a valid key for `#[suggestion(...)]`", + other + ) + ), + } + } + } + } + let msg = if let Some(msg) = msg { + quote!(#msg.as_str()) + } else { + throw_span_err!( + list.span().unwrap(), + "missing suggestion message", + |diag| { + diag.help("provide a suggestion message using #[suggestion(message = \"...\")]") + } + ); + }; + let code = code.unwrap_or_else(|| quote! { String::new() }); + // Now build it out: + let suggestion_method = format_ident!("span_{}", suggestion_kind); + quote! { + #diag.#suggestion_method(#span, #msg, #code, #applicability); + } + } + other => throw_span_err!( + list.span().unwrap(), + &format!("invalid annotation list `#[{}(...)]`", other) + ), + } + } + _ => panic!("unhandled meta kind"), + }) + } + + /// In the strings in the attributes supplied to this macro, we want callers to be able to + /// reference fields in the format string. Take this, for example: + /// ```ignore (not-usage-example) + /// struct Point { + /// #[error = "Expected a point greater than ({x}, {y})"] + /// x: i32, + /// y: i32, + /// } + /// ``` + /// We want to automatically pick up that {x} refers `self.x` and {y} refers to `self.y`, then + /// generate this call to format!: + /// ```ignore (not-usage-example) + /// format!("Expected a point greater than ({x}, {y})", x = self.x, y = self.y) + /// ``` + /// This function builds the entire call to format!. + fn build_format(&self, input: &String, span: proc_macro2::Span) -> proc_macro2::TokenStream { + let mut referenced_fields: HashSet = HashSet::new(); + + // At this point, we can start parsing the format string. + let mut it = input.chars().peekable(); + // Once the start of a format string has been found, process the format string and spit out + // the referenced fields. Leaves `it` sitting on the closing brace of the format string, so the + // next call to `it.next()` retrieves the next character. + while let Some(c) = it.next() { + if c == '{' && *it.peek().unwrap_or(&'\0') != '{' { + #[must_use] + let mut eat_argument = || -> Option { + let mut result = String::new(); + // Format specifiers look like + // format := '{' [ argument ] [ ':' format_spec ] '}' . + // Therefore, we only need to eat until ':' or '}' to find the argument. + while let Some(c) = it.next() { + result.push(c); + let next = *it.peek().unwrap_or(&'\0'); + if next == '}' { + break; + } else if next == ':' { + // Eat the ':' character. + assert_eq!(it.next().unwrap(), ':'); + break; + } + } + // Eat until (and including) the matching '}' + while it.next()? != '}' { + continue; + } + Some(result) + }; + + if let Some(referenced_field) = eat_argument() { + referenced_fields.insert(referenced_field); + } + } + } + // At this point, `referenced_fields` contains a set of the unique fields that were + // referenced in the format string. Generate the corresponding "x = self.x" format + // string parameters: + let args = referenced_fields.into_iter().map(|field: String| { + let field_ident = format_ident!("{}", field); + let value = if self.fields.contains_key(&field) { + quote! { + &self.#field_ident + } + } else { + // This field doesn't exist. Emit a diagnostic. + Diagnostic::spanned( + span.unwrap(), + proc_macro::Level::Error, + format!("`{}` doesn't refer to a field on this type", field), + ) + .emit(); + quote! { + "{#field}" + } + }; + quote! { + #field_ident = #value + } + }); + quote! { + format!(#input #(,#args)*) + } + } +} + +/// If `ty` is an Option, returns Some(inner type). Else, returns None. +fn option_inner_ty(ty: &syn::Type) -> Option<&syn::Type> { + if type_matches_path(ty, &["std", "option", "Option"]) { + if let syn::Type::Path(ty_path) = ty { + let path = &ty_path.path; + let ty = path.segments.iter().last().unwrap(); + if let syn::PathArguments::AngleBracketed(bracketed) = &ty.arguments { + if bracketed.args.len() == 1 { + if let syn::GenericArgument::Type(ty) = &bracketed.args[0] { + return Some(ty); + } + } + } + } + } + None +} diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index db2059251c0c8..05780d2e0c40b 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -237,6 +237,14 @@ enum DiagnosticBuilderMethod { // Add more variants as needed to support one-time diagnostics. } +/// Trait implemented by error types. This should not be implemented manually. Instead, use +/// `#[derive(SessionDiagnostic)]` -- see [rustc_macros::SessionDiagnostic]. +pub trait SessionDiagnostic<'a> { + /// Write out as a diagnostic out of `sess`. + #[must_use] + fn into_diagnostic(self, sess: &'a Session) -> DiagnosticBuilder<'a>; +} + /// Diagnostic message ID, used by `Session.one_time_diagnostics` to avoid /// emitting the same message more than once. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] @@ -392,6 +400,9 @@ impl Session { pub fn err(&self, msg: &str) { self.diagnostic().err(msg) } + pub fn emit_err<'a>(&'a self, err: impl SessionDiagnostic<'a>) { + err.into_diagnostic(self).emit() + } pub fn err_count(&self) -> usize { self.diagnostic().err_count() } diff --git a/compiler/rustc_typeck/Cargo.toml b/compiler/rustc_typeck/Cargo.toml index 0a6bfaef4319a..e3ba0bea7e8e2 100644 --- a/compiler/rustc_typeck/Cargo.toml +++ b/compiler/rustc_typeck/Cargo.toml @@ -11,6 +11,7 @@ doctest = false [dependencies] rustc_arena = { path = "../rustc_arena" } tracing = "0.1" +rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } rustc_attr = { path = "../rustc_attr" } rustc_data_structures = { path = "../rustc_data_structures" } diff --git a/compiler/rustc_typeck/src/astconv/generics.rs b/compiler/rustc_typeck/src/astconv/generics.rs index 84dab6de95819..a434406c284ac 100644 --- a/compiler/rustc_typeck/src/astconv/generics.rs +++ b/compiler/rustc_typeck/src/astconv/generics.rs @@ -1,6 +1,7 @@ use crate::astconv::{ AstConv, ExplicitLateBound, GenericArgCountMismatch, GenericArgCountResult, GenericArgPosition, }; +use crate::errors::AssocTypeBindingNotAllowed; use rustc_ast::ast::ParamKindOrd; use rustc_errors::{pluralize, struct_span_err, DiagnosticId, ErrorReported}; use rustc_hir as hir; @@ -544,13 +545,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { /// Emits an error regarding forbidden type binding associations pub fn prohibit_assoc_ty_binding(tcx: TyCtxt<'_>, span: Span) { - let mut err = struct_span_err!( - tcx.sess, - span, - E0229, - "associated type bindings are not allowed here" - ); - err.span_label(span, "associated type not allowed here").emit(); + tcx.sess.emit_err(AssocTypeBindingNotAllowed { span }); } /// Prohibits explicit lifetime arguments if late-bound lifetime parameters diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs index 80dd26e9154b3..8a9fe687ae721 100644 --- a/compiler/rustc_typeck/src/astconv/mod.rs +++ b/compiler/rustc_typeck/src/astconv/mod.rs @@ -7,6 +7,10 @@ mod generics; use crate::bounds::Bounds; use crate::collect::PlaceholderHirTyCollector; +use crate::errors::{ + AmbiguousLifetimeBound, MultipleRelaxedDefaultBounds, TraitObjectDeclaredWithNoTraits, + TypeofReservedKeywordUsed, ValueOfAssociatedStructAlreadySpecified, +}; use crate::middle::resolve_lifetime as rl; use crate::require_c_abi_if_c_variadic; use rustc_ast::util::lev_distance::find_best_match_for_name; @@ -684,14 +688,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { if unbound.is_none() { unbound = Some(&ptr.trait_ref); } else { - struct_span_err!( - tcx.sess, - span, - E0203, - "type parameter has more than one relaxed default \ - bound, only one is supported" - ) - .emit(); + tcx.sess.emit_err(MultipleRelaxedDefaultBounds { span }); } } } @@ -927,18 +924,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { dup_bindings .entry(assoc_ty.def_id) .and_modify(|prev_span| { - struct_span_err!( - self.tcx().sess, - binding.span, - E0719, - "the value of the associated type `{}` (from trait `{}`) \ - is already specified", - binding.item_name, - tcx.def_path_str(assoc_ty.container.id()) - ) - .span_label(binding.span, "re-bound here") - .span_label(*prev_span, format!("`{}` bound here first", binding.item_name)) - .emit(); + self.tcx().sess.emit_err(ValueOfAssociatedStructAlreadySpecified { + span: binding.span, + prev_span: *prev_span, + item_name: binding.item_name, + def_path: tcx.def_path_str(assoc_ty.container.id()), + }); }) .or_insert(binding.span); } @@ -1051,13 +1042,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } if regular_traits.is_empty() && auto_traits.is_empty() { - struct_span_err!( - tcx.sess, - span, - E0224, - "at least one trait is required for an object type" - ) - .emit(); + tcx.sess.emit_err(TraitObjectDeclaredWithNoTraits { span }); return tcx.ty_error(); } @@ -2059,15 +2044,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { self.normalize_ty(ast_ty.span, array_ty) } hir::TyKind::Typeof(ref _e) => { - struct_span_err!( - tcx.sess, - ast_ty.span, - E0516, - "`typeof` is a reserved keyword but unimplemented" - ) - .span_label(ast_ty.span, "reserved keyword") - .emit(); - + tcx.sess.emit_err(TypeofReservedKeywordUsed { span: ast_ty.span }); tcx.ty_error() } hir::TyKind::Infer => { @@ -2283,13 +2260,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // error. let r = derived_region_bounds[0]; if derived_region_bounds[1..].iter().any(|r1| r != *r1) { - struct_span_err!( - tcx.sess, - span, - E0227, - "ambiguous lifetime bound, explicit lifetime bound required" - ) - .emit(); + tcx.sess.emit_err(AmbiguousLifetimeBound { span }); } Some(r) } diff --git a/compiler/rustc_typeck/src/check/compare_method.rs b/compiler/rustc_typeck/src/check/compare_method.rs index 7adcd7b472e0d..bbf5153d35d9b 100644 --- a/compiler/rustc_typeck/src/check/compare_method.rs +++ b/compiler/rustc_typeck/src/check/compare_method.rs @@ -1,3 +1,4 @@ +use crate::errors::LifetimesOrBoundsMismatchOnTrait; use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId, ErrorReported}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; @@ -366,24 +367,18 @@ fn check_region_bounds_on_impl_item<'tcx>( let item_kind = assoc_item_kind_str(impl_m); let def_span = tcx.sess.source_map().guess_head_span(span); let span = tcx.hir().get_generics(impl_m.def_id).map(|g| g.span).unwrap_or(def_span); - let mut err = struct_span_err!( - tcx.sess, + let generics_span = if let Some(sp) = tcx.hir().span_if_local(trait_m.def_id) { + let def_sp = tcx.sess.source_map().guess_head_span(sp); + Some(tcx.hir().get_generics(trait_m.def_id).map(|g| g.span).unwrap_or(def_sp)) + } else { + None + }; + tcx.sess.emit_err(LifetimesOrBoundsMismatchOnTrait { span, - E0195, - "lifetime parameters or bounds on {} `{}` do not match the trait declaration", item_kind, - impl_m.ident, - ); - err.span_label(span, &format!("lifetimes do not match {} in trait", item_kind)); - if let Some(sp) = tcx.hir().span_if_local(trait_m.def_id) { - let def_sp = tcx.sess.source_map().guess_head_span(sp); - let sp = tcx.hir().get_generics(trait_m.def_id).map(|g| g.span).unwrap_or(def_sp); - err.span_label( - sp, - &format!("lifetimes in impl do not match this {} in trait", item_kind), - ); - } - err.emit(); + ident: impl_m.ident, + generics_span, + }); return Err(ErrorReported); } diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index cc8a6953f1397..ce3cdd6ff6be5 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -14,8 +14,13 @@ use crate::check::Expectation::{self, ExpectCastableToType, ExpectHasType, NoExp use crate::check::FnCtxt; use crate::check::Needs; use crate::check::TupleArgumentsFlag::DontTupleArguments; +use crate::errors::{ + FieldMultiplySpecifiedInInitializer, FunctionalRecordUpdateOnNonStruct, + YieldExprOutsideOfGenerator, +}; use crate::type_error_struct; +use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive}; use rustc_ast as ast; use rustc_ast::util::lev_distance::find_best_match_for_name; use rustc_data_structures::fx::FxHashMap; @@ -439,14 +444,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }) }); if !is_named { - struct_span_err!( - self.tcx.sess, - oprnd.span, - E0745, - "cannot take address of a temporary" - ) - .span_label(oprnd.span, "temporary value") - .emit(); + self.tcx.sess.emit_err(AddressOfTemporaryTaken { span: oprnd.span }) } } @@ -665,13 +663,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &'tcx hir::Expr<'tcx>, ) -> Ty<'tcx> { if self.ret_coercion.is_none() { - struct_span_err!( - self.tcx.sess, - expr.span, - E0572, - "return statement outside of function body", - ) - .emit(); + self.tcx.sess.emit_err(ReturnStmtOutsideOfFnBody { span: expr.span }); } else if let Some(ref e) = expr_opt { if self.ret_coercion_span.borrow().is_none() { *self.ret_coercion_span.borrow_mut() = Some(e.span); @@ -740,6 +732,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr_span: &Span, ) { if !lhs.is_syntactic_place_expr() { + // FIXME: Make this use SessionDiagnostic once error codes can be dynamically set. let mut err = self.tcx.sess.struct_span_err_with_code( *expr_span, "invalid left-hand side of assignment", @@ -1120,14 +1113,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Prohibit struct expressions when non-exhaustive flag is set. let adt = adt_ty.ty_adt_def().expect("`check_struct_path` returned non-ADT type"); if !adt.did.is_local() && variant.is_field_list_non_exhaustive() { - struct_span_err!( - self.tcx.sess, - expr.span, - E0639, - "cannot create non-exhaustive {} using struct expression", - adt.variant_descr() - ) - .emit(); + self.tcx + .sess + .emit_err(StructExprNonExhaustive { span: expr.span, what: adt.variant_descr() }); } let error_happened = self.check_expr_struct_fields( @@ -1165,13 +1153,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .insert(expr.hir_id, fru_field_types); } _ => { - struct_span_err!( - self.tcx.sess, - base_expr.span, - E0436, - "functional record update syntax requires a struct" - ) - .emit(); + self.tcx + .sess + .emit_err(FunctionalRecordUpdateOnNonStruct { span: base_expr.span }); } } } @@ -1234,18 +1218,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { error_happened = true; if let Some(prev_span) = seen_fields.get(&ident) { - let mut err = struct_span_err!( - self.tcx.sess, - field.ident.span, - E0062, - "field `{}` specified more than once", - ident - ); - - err.span_label(field.ident.span, "used more than once"); - err.span_label(*prev_span, format!("first use of `{}`", ident)); - - err.emit(); + tcx.sess.emit_err(FieldMultiplySpecifiedInInitializer { + span: field.ident.span, + prev_span: *prev_span, + ident, + }); } else { self.report_unknown_field(adt_ty, variant, field, ast_fields, kind_name, span); } @@ -1876,13 +1853,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx.mk_unit() } _ => { - struct_span_err!( - self.tcx.sess, - expr.span, - E0627, - "yield expression outside of generator literal" - ) - .emit(); + self.tcx.sess.emit_err(YieldExprOutsideOfGenerator { span: expr.span }); self.tcx.mk_unit() } } diff --git a/compiler/rustc_typeck/src/check/intrinsic.rs b/compiler/rustc_typeck/src/check/intrinsic.rs index 47cea8649ef3d..b8230f524446a 100644 --- a/compiler/rustc_typeck/src/check/intrinsic.rs +++ b/compiler/rustc_typeck/src/check/intrinsic.rs @@ -1,6 +1,10 @@ //! Type-checking for the rust-intrinsic and platform-intrinsic //! intrinsics that the compiler exposes. +use crate::errors::{ + SimdShuffleMissingLength, UnrecognizedAtomicOperation, UnrecognizedIntrinsicFunction, + WrongNumberOfTypeArgumentsToInstrinsic, +}; use crate::require_same_types; use rustc_errors::struct_span_err; @@ -41,17 +45,11 @@ fn equate_intrinsic_type<'tcx>( _ => bug!(), }; - struct_span_err!( - tcx.sess, + tcx.sess.emit_err(WrongNumberOfTypeArgumentsToInstrinsic { span, - E0094, - "intrinsic has wrong number of type \ - parameters: found {}, expected {}", - i_n_tps, - n_tps - ) - .span_label(span, format!("expected {} type parameter", n_tps)) - .emit(); + found: i_n_tps, + expected: n_tps, + }); return; } @@ -146,15 +144,7 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { | "umin" => (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], param(0)), "fence" | "singlethreadfence" => (0, Vec::new(), tcx.mk_unit()), op => { - struct_span_err!( - tcx.sess, - it.span, - E0092, - "unrecognized atomic operation function: `{}`", - op - ) - .span_label(it.span, "unrecognized atomic operation") - .emit(); + tcx.sess.emit_err(UnrecognizedAtomicOperation { span: it.span, op }); return; } }; @@ -380,15 +370,7 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { sym::nontemporal_store => (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], tcx.mk_unit()), other => { - struct_span_err!( - tcx.sess, - it.span, - E0093, - "unrecognized intrinsic function: `{}`", - other, - ) - .span_label(it.span, "unrecognized intrinsic") - .emit(); + tcx.sess.emit_err(UnrecognizedIntrinsicFunction { span: it.span, name: other }); return; } }; @@ -468,14 +450,7 @@ pub fn check_platform_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) (2, params, param(1)) } Err(_) => { - struct_span_err!( - tcx.sess, - it.span, - E0439, - "invalid `simd_shuffle`, needs length: `{}`", - name - ) - .emit(); + tcx.sess.emit_err(SimdShuffleMissingLength { span: it.span, name }); return; } } diff --git a/compiler/rustc_typeck/src/check/method/probe.rs b/compiler/rustc_typeck/src/check/method/probe.rs index 7ac6681be1a4a..5817212374bd5 100644 --- a/compiler/rustc_typeck/src/check/method/probe.rs +++ b/compiler/rustc_typeck/src/check/method/probe.rs @@ -4,6 +4,7 @@ use super::NoMatchData; use super::{CandidateSource, ImplSource, TraitSource}; use crate::check::FnCtxt; +use crate::errors::MethodCallOnUnknownType; use crate::hir::def::DefKind; use crate::hir::def_id::DefId; @@ -11,7 +12,6 @@ use rustc_ast as ast; use rustc_ast::util::lev_distance::{find_best_match_for_name, lev_distance}; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sync::Lrc; -use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def::Namespace; use rustc_infer::infer::canonical::OriginalQueryValues; @@ -376,14 +376,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // so we do a future-compat lint here for the 2015 edition // (see https://github.com/rust-lang/rust/issues/46906) if self.tcx.sess.rust_2018() { - struct_span_err!( - self.tcx.sess, - span, - E0699, - "the type of this value must be known to call a method on a raw pointer on \ - it" - ) - .emit(); + self.tcx.sess.emit_err(MethodCallOnUnknownType { span }); } else { self.tcx.struct_span_lint_hir( lint::builtin::TYVAR_BEHIND_RAW_POINTER, diff --git a/compiler/rustc_typeck/src/coherence/builtin.rs b/compiler/rustc_typeck/src/coherence/builtin.rs index 0d3cac7f7f38a..cd6d17999f946 100644 --- a/compiler/rustc_typeck/src/coherence/builtin.rs +++ b/compiler/rustc_typeck/src/coherence/builtin.rs @@ -1,6 +1,7 @@ //! Check properties that are required by built-in traits and set //! up data structures required by type-checking/codegen. +use crate::errors::{CopyImplOnNonAdt, CopyImplOnTypeWithDtor, DropImplOnWrongItem}; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -58,14 +59,7 @@ fn visit_implementation_of_drop(tcx: TyCtxt<'_>, impl_did: LocalDefId) { _ => bug!("expected Drop impl item"), }; - struct_span_err!( - tcx.sess, - sp, - E0120, - "the `Drop` trait may only be implemented for structs, enums, and unions", - ) - .span_label(sp, "must be a struct, enum, or union") - .emit(); + tcx.sess.emit_err(DropImplOnWrongItem { span: sp }); } fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) { @@ -108,25 +102,10 @@ fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) { let span = if let ItemKind::Impl { self_ty, .. } = item.kind { self_ty.span } else { span }; - struct_span_err!( - tcx.sess, - span, - E0206, - "the trait `Copy` may not be implemented for this type" - ) - .span_label(span, "type is not a structure or enumeration") - .emit(); + tcx.sess.emit_err(CopyImplOnNonAdt { span }); } Err(CopyImplementationError::HasDestructor) => { - struct_span_err!( - tcx.sess, - span, - E0184, - "the trait `Copy` may not be implemented for this type; the \ - type has a destructor" - ) - .span_label(span, "Copy not allowed on types with destructors") - .emit(); + tcx.sess.emit_err(CopyImplOnTypeWithDtor { span }); } } } diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs index 7a3f7ec56a2ce..08f4e4a311098 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_typeck/src/collect.rs @@ -18,6 +18,7 @@ use crate::astconv::{AstConv, SizedByDefault}; use crate::bounds::Bounds; use crate::check::intrinsic::intrinsic_operation_unsafety; use crate::constrained_generic_params as cgp; +use crate::errors; use crate::middle::resolve_lifetime as rl; use rustc_ast as ast; use rustc_ast::MetaItemKind; @@ -834,16 +835,11 @@ fn convert_variant( let fid = tcx.hir().local_def_id(f.hir_id); let dup_span = seen_fields.get(&f.ident.normalize_to_macros_2_0()).cloned(); if let Some(prev_span) = dup_span { - struct_span_err!( - tcx.sess, - f.span, - E0124, - "field `{}` is already declared", - f.ident - ) - .span_label(f.span, "field already declared") - .span_label(prev_span, format!("`{}` first declared here", f.ident)) - .emit(); + tcx.sess.emit_err(errors::FieldAlreadyDeclared { + field_name: f.ident, + span: f.span, + prev_span, + }); } else { seen_fields.insert(f.ident.normalize_to_macros_2_0(), f.span); } diff --git a/compiler/rustc_typeck/src/collect/type_of.rs b/compiler/rustc_typeck/src/collect/type_of.rs index 70ed92c5614a1..0f64b96b29106 100644 --- a/compiler/rustc_typeck/src/collect/type_of.rs +++ b/compiler/rustc_typeck/src/collect/type_of.rs @@ -1,5 +1,6 @@ +use crate::errors::AssocTypeOnInherentImpl; use rustc_data_structures::fx::FxHashSet; -use rustc_errors::{struct_span_err, Applicability, ErrorReported, StashKey}; +use rustc_errors::{Applicability, ErrorReported, StashKey}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -627,11 +628,5 @@ fn infer_placeholder_type( } fn report_assoc_ty_on_inherent_impl(tcx: TyCtxt<'_>, span: Span) { - struct_span_err!( - tcx.sess, - span, - E0202, - "associated types are not yet supported in inherent impls (see #8995)" - ) - .emit(); + tcx.sess.emit_err(AssocTypeOnInherentImpl { span }); } diff --git a/compiler/rustc_typeck/src/errors.rs b/compiler/rustc_typeck/src/errors.rs new file mode 100644 index 0000000000000..a769e48d2ca80 --- /dev/null +++ b/compiler/rustc_typeck/src/errors.rs @@ -0,0 +1,199 @@ +//! Errors emitted by typeck. +use rustc_macros::SessionDiagnostic; +use rustc_span::{symbol::Ident, Span, Symbol}; + +#[derive(SessionDiagnostic)] +#[error = "E0062"] +pub struct FieldMultiplySpecifiedInInitializer { + #[message = "field `{ident}` specified more than once"] + #[label = "used more than once"] + pub span: Span, + #[label = "first use of `{ident}`"] + pub prev_span: Span, + pub ident: Ident, +} + +#[derive(SessionDiagnostic)] +#[error = "E0092"] +pub struct UnrecognizedAtomicOperation<'a> { + #[message = "unrecognized atomic operation function: `{op}`"] + #[label = "unrecognized atomic operation"] + pub span: Span, + pub op: &'a str, +} + +#[derive(SessionDiagnostic)] +#[error = "E0094"] +pub struct WrongNumberOfTypeArgumentsToInstrinsic { + #[message = "intrinsic has wrong number of type \ + parameters: found {found}, expected {expected}"] + #[label = "expected {expected} type parameter"] + pub span: Span, + pub found: usize, + pub expected: usize, +} + +#[derive(SessionDiagnostic)] +#[error = "E0093"] +pub struct UnrecognizedIntrinsicFunction { + #[message = "unrecognized intrinsic function: `{name}`"] + #[label = "unrecognized intrinsic"] + pub span: Span, + pub name: Symbol, +} + +#[derive(SessionDiagnostic)] +#[error = "E0195"] +pub struct LifetimesOrBoundsMismatchOnTrait { + #[message = "lifetime parameters or bounds on {item_kind} `{ident}` do not match the trait declaration"] + #[label = "lifetimes do not match {item_kind} in trait"] + pub span: Span, + #[label = "lifetimes in impl do not match this {item_kind} in trait"] + pub generics_span: Option, + pub item_kind: &'static str, + pub ident: Ident, +} + +#[derive(SessionDiagnostic)] +#[error = "E0120"] +pub struct DropImplOnWrongItem { + #[message = "the `Drop` trait may only be implemented for structs, enums, and unions"] + #[label = "must be a struct, enum, or union"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0124"] +pub struct FieldAlreadyDeclared { + pub field_name: Ident, + #[message = "field `{field_name}` is already declared"] + #[label = "field already declared"] + pub span: Span, + #[label = "`{field_name}` first declared here"] + pub prev_span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0184"] +pub struct CopyImplOnTypeWithDtor { + #[message = "the trait `Copy` may not be implemented for this type; the \ + type has a destructor"] + #[label = "Copy not allowed on types with destructors"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0202"] +pub struct AssocTypeOnInherentImpl { + #[message = "associated types are not yet supported in inherent impls (see #8995)"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0203"] +pub struct MultipleRelaxedDefaultBounds { + #[message = "type parameter has more than one relaxed default bound, only one is supported"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0206"] +pub struct CopyImplOnNonAdt { + #[message = "the trait `Copy` may not be implemented for this type"] + #[label = "type is not a structure or enumeration"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0224"] +pub struct TraitObjectDeclaredWithNoTraits { + #[message = "at least one trait is required for an object type"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0227"] +pub struct AmbiguousLifetimeBound { + #[message = "ambiguous lifetime bound, explicit lifetime bound required"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0229"] +pub struct AssocTypeBindingNotAllowed { + #[message = "associated type bindings are not allowed here"] + #[label = "associated type not allowed here"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0439"] +pub struct SimdShuffleMissingLength { + #[message = "invalid `simd_shuffle`, needs length: `{name}`"] + pub span: Span, + pub name: Symbol, +} + +#[derive(SessionDiagnostic)] +#[error = "E0436"] +pub struct FunctionalRecordUpdateOnNonStruct { + #[message = "functional record update syntax requires a struct"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0516"] +pub struct TypeofReservedKeywordUsed { + #[message = "`typeof` is a reserved keyword but unimplemented"] + #[label = "reserved keyword"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0572"] +pub struct ReturnStmtOutsideOfFnBody { + #[message = "return statement outside of function body"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0627"] +pub struct YieldExprOutsideOfGenerator { + #[message = "yield expression outside of generator literal"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0639"] +pub struct StructExprNonExhaustive { + #[message = "cannot create non-exhaustive {what} using struct expression"] + pub span: Span, + pub what: &'static str, +} + +#[derive(SessionDiagnostic)] +#[error = "E0699"] +pub struct MethodCallOnUnknownType { + #[message = "the type of this value must be known to call a method on a raw pointer on it"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0719"] +pub struct ValueOfAssociatedStructAlreadySpecified { + #[message = "the value of the associated type `{item_name}` (from trait `{def_path}`) is already specified"] + #[label = "re-bound here"] + pub span: Span, + #[label = "`{item_name}` bound here first"] + pub prev_span: Span, + pub item_name: Ident, + pub def_path: String, +} + +#[derive(SessionDiagnostic)] +#[error = "E0745"] +pub struct AddressOfTemporaryTaken { + #[message = "cannot take address of a temporary"] + #[label = "temporary value"] + pub span: Span, +} diff --git a/compiler/rustc_typeck/src/lib.rs b/compiler/rustc_typeck/src/lib.rs index 62f92fe7ffa48..428b4662b84e7 100644 --- a/compiler/rustc_typeck/src/lib.rs +++ b/compiler/rustc_typeck/src/lib.rs @@ -84,6 +84,7 @@ mod check_unused; mod coherence; mod collect; mod constrained_generic_params; +mod errors; mod impl_wf_check; mod mem_categorization; mod outlives; diff --git a/src/test/ui-fulldeps/session-derive-errors.rs b/src/test/ui-fulldeps/session-derive-errors.rs new file mode 100644 index 0000000000000..7967b32a4a491 --- /dev/null +++ b/src/test/ui-fulldeps/session-derive-errors.rs @@ -0,0 +1,260 @@ +// check-fail +// Tests error conditions for specifying diagnostics using #[derive(SessionDiagnostic)] + +#![feature(rustc_private)] +#![crate_type = "lib"] + +extern crate rustc_span; +use rustc_span::Span; +use rustc_span::symbol::Ident; + +extern crate rustc_macros; +use rustc_macros::SessionDiagnostic; + +extern crate rustc_middle; +use rustc_middle::ty::Ty; + +extern crate rustc_errors; +use rustc_errors::Applicability; + +extern crate rustc_session; + +#[derive(SessionDiagnostic)] +#[message = "Hello, world!"] +#[error = "E0123"] +struct Hello {} + +#[derive(SessionDiagnostic)] +#[error = "E0123"] +//~^ ERROR `#[derive(SessionDiagnostic)]` can only be used on structs +enum SessionDiagnosticOnEnum { + Foo, + Bar, +} + +#[derive(SessionDiagnostic)] +#[error = "E0123"] +#[label = "This is in the wrong place"] +//~^ ERROR `#[label = ...]` is not a valid SessionDiagnostic struct attribute +struct WrongPlace {} + +#[derive(SessionDiagnostic)] +#[error = "E0123"] +struct WrongPlaceField { + #[suggestion = "this is the wrong kind of attribute"] +//~^ ERROR `#[suggestion = ...]` is not a valid SessionDiagnostic field attribute + sp: Span, +} + +#[derive(SessionDiagnostic)] +#[message = "Hello, world!"] +#[error = "E0123"] +#[error = "E0456"] //~ ERROR `error` specified multiple times +struct ErrorSpecifiedTwice {} + +#[derive(SessionDiagnostic)] +#[message = "Hello, world!"] +#[error = "E0123"] +#[lint = "some_useful_lint"] //~ ERROR `lint` specified when `error` was already specified +struct LintSpecifiedAfterError {} + +#[derive(SessionDiagnostic)] +#[message = "Some lint message"] +#[error = "E0123"] +struct LintButHasErrorCode {} + +#[derive(SessionDiagnostic)] +struct ErrorCodeNotProvided {} //~ ERROR `code` not specified + +// FIXME: Uncomment when emitting lints is supported. +/* +#[derive(SessionDiagnostic)] +#[message = "Hello, world!"] +#[lint = "clashing_extern_declarations"] +#[lint = "improper_ctypes"] // FIXME: ERROR `lint` specified multiple times +struct LintSpecifiedTwice {} + +#[derive(SessionDiagnostic)] +#[lint = "Some lint message"] +#[message = "Some error message"] +#[error = "E0123"] // ERROR `error` specified when `lint` was already specified +struct ErrorSpecifiedAfterLint {} +*/ + +#[derive(SessionDiagnostic)] +#[error = "E0123"] +struct ErrorWithField { + name: String, + #[message = "This error has a field, and references {name}"] + span: Span +} + +#[derive(SessionDiagnostic)] +#[error = "E0123"] +struct ErrorWithMessageAppliedToField { + #[message = "this message is applied to a String field"] + //~^ ERROR the `#[message = "..."]` attribute can only be applied to fields of type Span + name: String, +} + +#[derive(SessionDiagnostic)] +#[error = "E0123"] +#[message = "This error has a field, and references {name}"] +//~^ ERROR `name` doesn't refer to a field on this type +struct ErrorWithNonexistentField { + span: Span +} + +#[derive(SessionDiagnostic)] +#[error = "E0123"] +#[message = "This is missing a closing brace: {name"] +//~^ ERROR invalid format string: expected `'}'` +struct ErrorMissingClosingBrace { + name: String, + span: Span +} + +#[derive(SessionDiagnostic)] +#[error = "E0123"] +#[message = "This is missing an opening brace: name}"] +//~^ ERROR invalid format string: unmatched `}` +struct ErrorMissingOpeningBrace { + name: String, + span: Span +} + +#[derive(SessionDiagnostic)] +#[error = "E0123"] +#[message = "Something something"] +struct LabelOnSpan { + #[label = "See here"] + sp: Span +} + +#[derive(SessionDiagnostic)] +#[error = "E0123"] +#[message = "Something something"] +struct LabelOnNonSpan { + #[label = "See here"] + //~^ ERROR The `#[label = ...]` attribute can only be applied to fields of type Span + id: u32, +} + +#[derive(SessionDiagnostic)] +#[error = "E0123"] +struct Suggest { + #[suggestion(message = "This is a suggestion", code = "This is the suggested code")] + #[suggestion_short(message = "This is a suggestion", code = "This is the suggested code")] + #[suggestion_hidden(message = "This is a suggestion", code = "This is the suggested code")] + #[suggestion_verbose(message = "This is a suggestion", code = "This is the suggested code")] + suggestion: (Span, Applicability), +} + +#[derive(SessionDiagnostic)] +#[error = "E0123"] +struct SuggestWithoutCode { + #[suggestion(message = "This is a suggestion")] + suggestion: (Span, Applicability), +} + +#[derive(SessionDiagnostic)] +#[error = "E0123"] +struct SuggestWithBadKey { + #[suggestion(nonsense = "This is nonsense")] + //~^ ERROR `nonsense` is not a valid key for `#[suggestion(...)]` + suggestion: (Span, Applicability), +} + +#[derive(SessionDiagnostic)] +#[error = "E0123"] +struct SuggestWithShorthandMsg { + #[suggestion(msg = "This is a suggestion")] + //~^ ERROR `msg` is not a valid key for `#[suggestion(...)]` + suggestion: (Span, Applicability), +} + +#[derive(SessionDiagnostic)] +#[error = "E0123"] +struct SuggestWithoutMsg { + #[suggestion(code = "This is suggested code")] + //~^ ERROR missing suggestion message + suggestion: (Span, Applicability), +} + +#[derive(SessionDiagnostic)] +#[error = "E0123"] +struct SuggestWithTypesSwapped { + #[suggestion(message = "This is a message", code = "This is suggested code")] + suggestion: (Applicability, Span), +} + +#[derive(SessionDiagnostic)] +#[error = "E0123"] +struct SuggestWithWrongTypeApplicabilityOnly { + #[suggestion(message = "This is a message", code = "This is suggested code")] + //~^ ERROR wrong field type for suggestion + suggestion: Applicability, +} + +#[derive(SessionDiagnostic)] +#[error = "E0123"] +struct SuggestWithSpanOnly{ + #[suggestion(message = "This is a message", code = "This is suggested code")] + suggestion: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0123"] +struct SuggestWithDuplicateSpanAndApplicability { + #[suggestion(message = "This is a message", code = "This is suggested code")] + //~^ ERROR type of field annotated with `#[suggestion(...)]` contains more than one Span + suggestion: (Span, Span, Applicability), +} + +#[derive(SessionDiagnostic)] +#[error = "E0123"] +struct SuggestWithDuplicateApplicabilityAndSpan { + #[suggestion(message = "This is a message", code = "This is suggested code")] + //~^ ERROR type of field annotated with `#[suggestion(...)]` contains more than one + suggestion: (Applicability, Applicability, Span), +} + +#[derive(SessionDiagnostic)] +#[error = "E0123"] +struct WrongKindOfAnnotation { + #[label("wrong kind of annotation for label")] + //~^ ERROR invalid annotation list `#[label(...)]` + z: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0123"] +#[message = "Something something else"] +struct OptionsInErrors { + #[label = "Label message"] + label: Option, + #[suggestion(message = "suggestion message")] + opt_sugg: Option<(Span, Applicability)>, +} + +#[derive(SessionDiagnostic)] +#[error = "E0456"] +struct MoveOutOfBorrowError<'tcx> { + name: Ident, + ty: Ty<'tcx>, + #[message = "cannot move {ty} out of borrow"] + #[label = "cannot move out of borrow"] + span: Span, + #[label = "`{ty}` first borrowed here"] + other_span: Span, + #[suggestion(message = "consider cloning here", code = "{name}.clone()")] + opt_sugg: Option<(Span, Applicability)>, +} + +#[derive(SessionDiagnostic)] +#[error = "E0123"] +struct ErrorWithLifetime<'a> { + #[message = "Some message that references {name}"] + span: Span, + name: &'a str, +} diff --git a/src/test/ui-fulldeps/session-derive-errors.stderr b/src/test/ui-fulldeps/session-derive-errors.stderr new file mode 100644 index 0000000000000..c1be151f1c1ce --- /dev/null +++ b/src/test/ui-fulldeps/session-derive-errors.stderr @@ -0,0 +1,135 @@ +error: `#[derive(SessionDiagnostic)]` can only be used on structs + --> $DIR/session-derive-errors.rs:28:1 + | +LL | / #[error = "E0123"] +LL | | +LL | | enum SessionDiagnosticOnEnum { +LL | | Foo, +LL | | Bar, +LL | | } + | |_^ + +error: `#[label = ...]` is not a valid SessionDiagnostic struct attribute + --> $DIR/session-derive-errors.rs:37:1 + | +LL | #[label = "This is in the wrong place"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `#[suggestion = ...]` is not a valid SessionDiagnostic field attribute + --> $DIR/session-derive-errors.rs:44:5 + | +LL | #[suggestion = "this is the wrong kind of attribute"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `error` specified multiple times + --> $DIR/session-derive-errors.rs:52:11 + | +LL | #[error = "E0456"] + | ^^^^^^^ + +error: `lint` specified when `error` was already specified + --> $DIR/session-derive-errors.rs:58:10 + | +LL | #[lint = "some_useful_lint"] + | ^^^^^^^^^^^^^^^^^^ + +error: `code` not specified + --> $DIR/session-derive-errors.rs:67:1 + | +LL | struct ErrorCodeNotProvided {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use the [code = "..."] attribute to set this diagnostic's error code + +error: the `#[message = "..."]` attribute can only be applied to fields of type Span + --> $DIR/session-derive-errors.rs:95:5 + | +LL | #[message = "this message is applied to a String field"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `name` doesn't refer to a field on this type + --> $DIR/session-derive-errors.rs:102:1 + | +LL | #[message = "This error has a field, and references {name}"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: invalid format string: expected `'}'` but string was terminated + --> $DIR/session-derive-errors.rs:110:1 + | +LL | #[error = "E0123"] + | - because of this opening brace +LL | #[message = "This is missing a closing brace: {name"] + | ^ expected `'}'` in format string + | + = note: if you intended to print `{`, you can escape it using `{{` + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: invalid format string: unmatched `}` found + --> $DIR/session-derive-errors.rs:119:1 + | +LL | #[message = "This is missing an opening brace: name}"] + | ^ unmatched `}` in format string + | + = note: if you intended to print `}`, you can escape it using `}}` + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: The `#[label = ...]` attribute can only be applied to fields of type Span + --> $DIR/session-derive-errors.rs:138:5 + | +LL | #[label = "See here"] + | ^^^^^^^^^^^^^^^^^^^^^ + +error: `nonsense` is not a valid key for `#[suggestion(...)]` + --> $DIR/session-derive-errors.rs:163:18 + | +LL | #[suggestion(nonsense = "This is nonsense")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `msg` is not a valid key for `#[suggestion(...)]` + --> $DIR/session-derive-errors.rs:171:18 + | +LL | #[suggestion(msg = "This is a suggestion")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: missing suggestion message + --> $DIR/session-derive-errors.rs:179:7 + | +LL | #[suggestion(code = "This is suggested code")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: provide a suggestion message using #[suggestion(message = "...")] + +error: wrong field type for suggestion + --> $DIR/session-derive-errors.rs:194:5 + | +LL | / #[suggestion(message = "This is a message", code = "This is suggested code")] +LL | | +LL | | suggestion: Applicability, + | |_____________________________^ + | + = help: #[suggestion(...)] should be applied to fields of type Span or (Span, Applicability) + +error: type of field annotated with `#[suggestion(...)]` contains more than one Span + --> $DIR/session-derive-errors.rs:209:5 + | +LL | / #[suggestion(message = "This is a message", code = "This is suggested code")] +LL | | +LL | | suggestion: (Span, Span, Applicability), + | |___________________________________________^ + +error: type of field annotated with `#[suggestion(...)]` contains more than one Applicability + --> $DIR/session-derive-errors.rs:217:5 + | +LL | / #[suggestion(message = "This is a message", code = "This is suggested code")] +LL | | +LL | | suggestion: (Applicability, Applicability, Span), + | |____________________________________________________^ + +error: invalid annotation list `#[label(...)]` + --> $DIR/session-derive-errors.rs:225:7 + | +LL | #[label("wrong kind of annotation for label")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 18 previous errors +