Auto merge of #75138 - jumbatm:session-diagnostic-derive, r=oli-obk

Add derive macro for specifying diagnostics using attributes.

Introduces `#[derive(SessionDiagnostic)]`, a derive macro for specifying structs that can be converted to Diagnostics using directions given by attributes on the struct and its fields. Currently, the following attributes have been implemented:
- `#[code = "..."]` -- this sets the Diagnostic's error code, and must be provided on the struct iself (ie, not on a field). Equivalent to calling `code`.
- `#[message = "..."]` -- this sets the Diagnostic's primary error message.
- `#[label = "..."]` -- this must be applied to fields of type `Span`, and is equivalent to `span_label`
- `#[suggestion(..)]` -- this allows a suggestion message to be supplied. This attribute must be applied to a field of type `Span` or `(Span, Applicability)`, and is equivalent to calling `span_suggestion`. Valid arguments are:
    - `message = "..."` -- this sets the suggestion message.
    - (Optional) `code = "..."` -- this suggests code for the suggestion. Defaults to empty.

`suggestion`also  comes with other variants: `#[suggestion_short(..)]`, `#[suggestion_hidden(..)]` and `#[suggestion_verbose(..)]` which all take the same keys.

Within the strings passed to each attribute, fields can be referenced without needing to be passed explicitly into the format string -- eg, `#[error = "{ident} already declared"] ` will set the error message to `format!("{} already declared", &self.ident)`. Any fields on the struct can be referenced in this way.

Additionally, for any of these attributes, Option fields can be used to only optionally apply the decoration -- for example:

```rust
#[derive(SessionDiagnostic)]
#[code = "E0123"]
struct SomeKindOfError {
    ...
    #[suggestion(message = "informative error message")]
    opt_sugg: Option<(Span, Applicability)>
    ...
}
```
will not emit a suggestion if `opt_sugg` is `None`.

We plan on iterating on this macro further; this PR is a start.

Closes #61132.

r? `@oli-obk`
This commit is contained in:
bors 2020-09-08 00:58:43 +00:00
commit 71569e4201
19 changed files with 1362 additions and 206 deletions

View File

@ -4087,6 +4087,7 @@ dependencies = [
"rustc_hir_pretty",
"rustc_index",
"rustc_infer",
"rustc_macros",
"rustc_middle",
"rustc_session",
"rustc_span",

View File

@ -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;

View File

@ -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
);

View File

@ -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<syn::Error> 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<String, &'a syn::Field>,
/// 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<proc_macro2::TokenStream, SessionDiagnosticDeriveError> {
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<proc_macro2::TokenStream, SessionDiagnosticDeriveError> {
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<proc_macro2::TokenStream, SessionDiagnosticDeriveError> {
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<String> = 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<String> {
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
}

View File

@ -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()
}

View File

@ -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" }

View File

@ -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

View File

@ -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)
}

View File

@ -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,
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 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);
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();
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,
item_kind,
ident: impl_m.ident,
generics_span,
});
return Err(ErrorReported);
}

View File

@ -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()
}
}

View File

@ -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;
}
}

View File

@ -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,

View File

@ -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 });
}
}
}

View File

@ -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);
}

View File

@ -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 });
}

View File

@ -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<Span>,
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,
}

View File

@ -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;

View File

@ -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<Span>,
#[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,
}

View File

@ -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