Skip to content

Commit

Permalink
Auto merge of #96853 - davidtwco:diagnostic-translation-unit-and-more…
Browse files Browse the repository at this point in the history
…-porting, r=oli-obk

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

- Extend diagnostic derive so that spanless subdiagnostics (e.g. some uses of `help`/`note`) can be applied via attributes to fields of type `()` (currently spanless subdiagnostics are applied via attributes on the diagnostic struct itself). A consequence of this is that `Option<()>` fields can be used to represent optional spanless subdiagnostics, which are sometimes useful (e.g. for a `help` that should only show on nightly builds).
- Simplify the "explicit generic args with impl trait" diagnostic struct (from #96760) using support for `Option<()>` spanless subdiagnostics.
- Change `DiagnosticBuilder::set_arg`, used to provide context for Fluent messages, so that it takes anything that implements `IntoDiagnosticArg`, rather than `DiagnosticArgValue` - this improves the ergonomics of manual implementations of `SessionDiagnostic` which are translatable.
- Port "the type parameter `T` must be explicitly specified", "manual implementations of `X` are experimental", "could not resolve substs on overridden impl" diagnostics to diagnostic structs.
- When testing macros from `rustc_macros` in `ui-fulldeps` tests, sometimes paths from the compiler source tree can be shown in error messages - these need to be normalized in `compiletest`.

r? `@oli-obk`
cc `@pvdrz`
  • Loading branch information
bors committed May 12, 2022
2 parents 4f8e2e3 + 4758247 commit 18bd2dd
Show file tree
Hide file tree
Showing 14 changed files with 254 additions and 143 deletions.
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

0 comments on commit 18bd2dd

Please sign in to comment.