Skip to content

diagnostics: port more diagnostics to derive + support for () fields #96853

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 29 additions & 2 deletions compiler/rustc_error_messages/locales/en-US/typeck.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,33 @@ typeck-explicit-generic-args-with-impl-trait =
cannot provide explicit generic arguments when `impl Trait` is used in argument position
.label = explicit generic argument not allowed
.note = see issue #83701 <https://github.com/rust-lang/rust/issues/83701> for more information
.help = add `#![feature(explicit_generic_args_with_impl_trait)]` to the crate attributes to enable

typeck-explicit-generic-args-with-impl-trait-feature =
add `#![feature(explicit_generic_args_with_impl_trait)]` to the crate attributes to enable
typeck-missing-type-params =
the type {$parameterCount ->
[one] parameter
*[other] parameters
} {$parameters} must be explicitly specified
.label = type {$parameterCount ->
[one] parameter
*[other] parameters
} {$parameters} must be specified for this
.suggestion = set the type {$parameterCount ->
[one] parameter
*[other] parameters
} to the desired {$parameterCount ->
[one] type
*[other] types
}
.no-suggestion-label = missing {$parameterCount ->
[one] reference
*[other] references
} to {$parameters}
.note = because of the default `Self` reference, type parameters must be specified on object types

typeck-manual-implementation =
manual implementations of `{$trait_name}` are experimental
.label = manual implementations of `{$trait_name}` are experimental
.help = add `#![feature(unboxed_closures)]` to the crate attributes to enable

typeck-substs-on-overridden-impl = could not resolve substs on overridden impl
4 changes: 2 additions & 2 deletions compiler/rustc_errors/src/diagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -821,9 +821,9 @@ impl Diagnostic {
pub fn set_arg(
&mut self,
name: impl Into<Cow<'static, str>>,
arg: DiagnosticArgValue<'static>,
arg: impl IntoDiagnosticArg,
) -> &mut Self {
self.args.push((name.into(), arg));
self.args.push((name.into(), arg.into_diagnostic_arg()));
self
}

Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_errors/src/diagnostic_builder.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::diagnostic::DiagnosticArgValue;
use crate::diagnostic::IntoDiagnosticArg;
use crate::{Diagnostic, DiagnosticId, DiagnosticMessage, DiagnosticStyledString, ErrorGuaranteed};
use crate::{Handler, Level, MultiSpan, StashKey};
use rustc_lint_defs::Applicability;
Expand Down Expand Up @@ -528,7 +528,7 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
forward!(pub fn set_arg(
&mut self,
name: impl Into<Cow<'static, str>>,
arg: DiagnosticArgValue<'static>,
arg: impl IntoDiagnosticArg,
) -> &mut Self);

forward!(pub fn subdiagnostic(
Expand Down
55 changes: 42 additions & 13 deletions compiler/rustc_macros/src/diagnostics/diagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ use crate::diagnostics::error::{
SessionDiagnosticDeriveError,
};
use crate::diagnostics::utils::{
report_error_if_not_applied_to_span, type_matches_path, Applicability, FieldInfo, FieldInnerTy,
HasFieldMap, SetOnce,
report_error_if_not_applied_to_span, report_type_error, type_is_unit, type_matches_path,
Applicability, FieldInfo, FieldInnerTy, HasFieldMap, SetOnce,
};
use proc_macro2::TokenStream;
use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote};
use std::collections::HashMap;
use std::str::FromStr;
Expand Down Expand Up @@ -113,7 +113,7 @@ impl<'a> SessionDiagnosticDerive<'a> {
quote! {
#diag.set_arg(
stringify!(#ident),
#field_binding.into_diagnostic_arg()
#field_binding
);
}
} else {
Expand Down Expand Up @@ -388,7 +388,8 @@ impl SessionDiagnosticDeriveBuilder {
) -> Result<TokenStream, SessionDiagnosticDeriveError> {
let diag = &self.diag;

let name = attr.path.segments.last().unwrap().ident.to_string();
let ident = &attr.path.segments.last().unwrap().ident;
let name = ident.to_string();
let name = name.as_str();

let meta = attr.parse_meta()?;
Expand All @@ -405,9 +406,18 @@ impl SessionDiagnosticDeriveBuilder {
#diag.set_span(#binding);
})
}
"label" | "note" | "help" => {
"label" => {
report_error_if_not_applied_to_span(attr, &info)?;
Ok(self.add_subdiagnostic(binding, name, name))
Ok(self.add_spanned_subdiagnostic(binding, ident, name))
}
"note" | "help" => {
if type_matches_path(&info.ty, &["rustc_span", "Span"]) {
Ok(self.add_spanned_subdiagnostic(binding, ident, name))
} else if type_is_unit(&info.ty) {
Ok(self.add_subdiagnostic(ident, name))
} else {
report_type_error(attr, "`Span` or `()`")?;
}
}
"subdiagnostic" => Ok(quote! { #diag.subdiagnostic(#binding); }),
_ => throw_invalid_attr!(attr, &meta, |diag| {
Expand All @@ -416,9 +426,18 @@ impl SessionDiagnosticDeriveBuilder {
}),
},
Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(ref s), .. }) => match name {
"label" | "note" | "help" => {
"label" => {
report_error_if_not_applied_to_span(attr, &info)?;
Ok(self.add_subdiagnostic(binding, name, &s.value()))
Ok(self.add_spanned_subdiagnostic(binding, ident, &s.value()))
}
"note" | "help" => {
if type_matches_path(&info.ty, &["rustc_span", "Span"]) {
Ok(self.add_spanned_subdiagnostic(binding, ident, &s.value()))
} else if type_is_unit(&info.ty) {
Ok(self.add_subdiagnostic(ident, &s.value()))
} else {
report_type_error(attr, "`Span` or `()`")?;
}
}
_ => throw_invalid_attr!(attr, &meta, |diag| {
diag.help("only `label`, `note` and `help` are valid field attributes")
Expand Down Expand Up @@ -510,12 +529,12 @@ impl SessionDiagnosticDeriveBuilder {
}
}

/// Adds a subdiagnostic by generating a `diag.span_$kind` call with the current slug and
/// `fluent_attr_identifier`.
fn add_subdiagnostic(
/// Adds a spanned subdiagnostic by generating a `diag.span_$kind` call with the current slug
/// and `fluent_attr_identifier`.
fn add_spanned_subdiagnostic(
&self,
field_binding: TokenStream,
kind: &str,
kind: &Ident,
fluent_attr_identifier: &str,
) -> TokenStream {
let diag = &self.diag;
Expand All @@ -531,6 +550,16 @@ impl SessionDiagnosticDeriveBuilder {
}
}

/// Adds a subdiagnostic by generating a `diag.span_$kind` call with the current slug
/// and `fluent_attr_identifier`.
fn add_subdiagnostic(&self, kind: &Ident, fluent_attr_identifier: &str) -> TokenStream {
let diag = &self.diag;
let slug = self.slug.as_ref().map(|(slug, _)| slug.as_str()).unwrap_or("missing-slug");
quote! {
#diag.#kind(rustc_errors::DiagnosticMessage::fluent_attr(#slug, #fluent_attr_identifier));
}
}

fn span_and_applicability_of_ty(
&self,
info: FieldInfo<'_>,
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
let generated = quote! {
#diag.set_arg(
stringify!(#ident),
#binding.into_diagnostic_arg()
#binding
);
};

Expand Down
54 changes: 33 additions & 21 deletions compiler/rustc_macros/src/diagnostics/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use proc_macro2::TokenStream;
use quote::{format_ident, quote, ToTokens};
use std::collections::BTreeSet;
use std::str::FromStr;
use syn::{spanned::Spanned, Attribute, Meta, Type, Visibility};
use syn::{spanned::Spanned, Attribute, Meta, Type, TypeTuple, Visibility};
use synstructure::BindingInfo;

/// Checks whether the type name of `ty` matches `name`.
Expand All @@ -25,31 +25,43 @@ pub(crate) fn type_matches_path(ty: &Type, name: &[&str]) -> bool {
}
}

/// Reports an error if the field's type is not `Applicability`.
/// Checks whether the type `ty` is `()`.
pub(crate) fn type_is_unit(ty: &Type) -> bool {
if let Type::Tuple(TypeTuple { elems, .. }) = ty { elems.is_empty() } else { false }
}

/// Reports a type error for field with `attr`.
pub(crate) fn report_type_error(
attr: &Attribute,
ty_name: &str,
) -> Result<!, SessionDiagnosticDeriveError> {
let name = attr.path.segments.last().unwrap().ident.to_string();
let meta = attr.parse_meta()?;

throw_span_err!(
attr.span().unwrap(),
&format!(
"the `#[{}{}]` attribute can only be applied to fields of type {}",
name,
match meta {
Meta::Path(_) => "",
Meta::NameValue(_) => " = ...",
Meta::List(_) => "(...)",
},
ty_name
)
);
}

/// Reports an error if the field's type does not match `path`.
fn report_error_if_not_applied_to_ty(
attr: &Attribute,
info: &FieldInfo<'_>,
path: &[&str],
ty_name: &str,
) -> Result<(), SessionDiagnosticDeriveError> {
if !type_matches_path(&info.ty, path) {
let name = attr.path.segments.last().unwrap().ident.to_string();
let name = name.as_str();
let meta = attr.parse_meta()?;

throw_span_err!(
attr.span().unwrap(),
&format!(
"the `#[{}{}]` attribute can only be applied to fields of type `{}`",
name,
match meta {
Meta::Path(_) => "",
Meta::NameValue(_) => " = ...",
Meta::List(_) => "(...)",
},
ty_name
)
);
report_type_error(attr, ty_name)?;
}

Ok(())
Expand All @@ -64,7 +76,7 @@ pub(crate) fn report_error_if_not_applied_to_applicability(
attr,
info,
&["rustc_errors", "Applicability"],
"Applicability",
"`Applicability`",
)
}

Expand All @@ -73,7 +85,7 @@ pub(crate) fn report_error_if_not_applied_to_span(
attr: &Attribute,
info: &FieldInfo<'_>,
) -> Result<(), SessionDiagnosticDeriveError> {
report_error_if_not_applied_to_ty(attr, info, &["rustc_span", "Span"], "Span")
report_error_if_not_applied_to_ty(attr, info, &["rustc_span", "Span"], "`Span`")
}

/// Inner type of a field and type of wrapper.
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#![feature(allow_internal_unstable)]
#![feature(let_else)]
#![feature(never_type)]
#![feature(proc_macro_diagnostic)]
#![allow(rustc::default_hash_types)]
#![recursion_limit = "128"]
Expand Down
77 changes: 7 additions & 70 deletions compiler/rustc_typeck/src/astconv/errors.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::astconv::AstConv;
use crate::errors::{ManualImplementation, MissingTypeParams};
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{pluralize, struct_span_err, Applicability, ErrorGuaranteed};
use rustc_hir as hir;
Expand All @@ -24,65 +25,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
if missing_type_params.is_empty() {
return;
}
let display =
missing_type_params.iter().map(|n| format!("`{}`", n)).collect::<Vec<_>>().join(", ");
let mut err = struct_span_err!(
self.tcx().sess,

self.tcx().sess.emit_err(MissingTypeParams {
span,
E0393,
"the type parameter{} {} must be explicitly specified",
pluralize!(missing_type_params.len()),
display,
);
err.span_label(
self.tcx().def_span(def_id),
&format!(
"type parameter{} {} must be specified for this",
pluralize!(missing_type_params.len()),
display,
),
);
let mut suggested = false;
if let (Ok(snippet), true) = (
self.tcx().sess.source_map().span_to_snippet(span),
// Don't suggest setting the type params if there are some already: the order is
// tricky to get right and the user will already know what the syntax is.
def_span: self.tcx().def_span(def_id),
missing_type_params,
empty_generic_args,
) {
if snippet.ends_with('>') {
// The user wrote `Trait<'a, T>` or similar. To provide an accurate suggestion
// we would have to preserve the right order. For now, as clearly the user is
// aware of the syntax, we do nothing.
} else {
// The user wrote `Iterator`, so we don't have a type we can suggest, but at
// least we can clue them to the correct syntax `Iterator<Type>`.
err.span_suggestion(
span,
&format!(
"set the type parameter{plural} to the desired type{plural}",
plural = pluralize!(missing_type_params.len()),
),
format!("{}<{}>", snippet, missing_type_params.join(", ")),
Applicability::HasPlaceholders,
);
suggested = true;
}
}
if !suggested {
err.span_label(
span,
format!(
"missing reference{} to {}",
pluralize!(missing_type_params.len()),
display,
),
);
}
err.note(
"because of the default `Self` reference, type parameters must be \
specified on object types",
);
err.emit();
});
}

/// When the code is using the `Fn` traits directly, instead of the `Fn(A) -> B` syntax, emit
Expand Down Expand Up @@ -172,19 +121,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {

if is_impl {
let trait_name = self.tcx().def_path_str(trait_def_id);
struct_span_err!(
self.tcx().sess,
span,
E0183,
"manual implementations of `{}` are experimental",
trait_name,
)
.span_label(
span,
format!("manual implementations of `{}` are experimental", trait_name),
)
.help("add `#![feature(unboxed_closures)]` to the crate attributes to enable")
.emit();
self.tcx().sess.emit_err(ManualImplementation { span, trait_name });
}
}

Expand Down
Loading