Skip to content

Commit 18bd2dd

Browse files
committed
Auto merge of #96853 - davidtwco:diagnostic-translation-unit-and-more-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`
2 parents 4f8e2e3 + 4758247 commit 18bd2dd

File tree

14 files changed

+254
-143
lines changed

14 files changed

+254
-143
lines changed

compiler/rustc_error_messages/locales/en-US/typeck.ftl

+29-2
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,33 @@ typeck-explicit-generic-args-with-impl-trait =
9999
cannot provide explicit generic arguments when `impl Trait` is used in argument position
100100
.label = explicit generic argument not allowed
101101
.note = see issue #83701 <https://github.com/rust-lang/rust/issues/83701> for more information
102+
.help = add `#![feature(explicit_generic_args_with_impl_trait)]` to the crate attributes to enable
102103
103-
typeck-explicit-generic-args-with-impl-trait-feature =
104-
add `#![feature(explicit_generic_args_with_impl_trait)]` to the crate attributes to enable
104+
typeck-missing-type-params =
105+
the type {$parameterCount ->
106+
[one] parameter
107+
*[other] parameters
108+
} {$parameters} must be explicitly specified
109+
.label = type {$parameterCount ->
110+
[one] parameter
111+
*[other] parameters
112+
} {$parameters} must be specified for this
113+
.suggestion = set the type {$parameterCount ->
114+
[one] parameter
115+
*[other] parameters
116+
} to the desired {$parameterCount ->
117+
[one] type
118+
*[other] types
119+
}
120+
.no-suggestion-label = missing {$parameterCount ->
121+
[one] reference
122+
*[other] references
123+
} to {$parameters}
124+
.note = because of the default `Self` reference, type parameters must be specified on object types
125+
126+
typeck-manual-implementation =
127+
manual implementations of `{$trait_name}` are experimental
128+
.label = manual implementations of `{$trait_name}` are experimental
129+
.help = add `#![feature(unboxed_closures)]` to the crate attributes to enable
130+
131+
typeck-substs-on-overridden-impl = could not resolve substs on overridden impl

compiler/rustc_errors/src/diagnostic.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -821,9 +821,9 @@ impl Diagnostic {
821821
pub fn set_arg(
822822
&mut self,
823823
name: impl Into<Cow<'static, str>>,
824-
arg: DiagnosticArgValue<'static>,
824+
arg: impl IntoDiagnosticArg,
825825
) -> &mut Self {
826-
self.args.push((name.into(), arg));
826+
self.args.push((name.into(), arg.into_diagnostic_arg()));
827827
self
828828
}
829829

compiler/rustc_errors/src/diagnostic_builder.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::diagnostic::DiagnosticArgValue;
1+
use crate::diagnostic::IntoDiagnosticArg;
22
use crate::{Diagnostic, DiagnosticId, DiagnosticMessage, DiagnosticStyledString, ErrorGuaranteed};
33
use crate::{Handler, Level, MultiSpan, StashKey};
44
use rustc_lint_defs::Applicability;
@@ -528,7 +528,7 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
528528
forward!(pub fn set_arg(
529529
&mut self,
530530
name: impl Into<Cow<'static, str>>,
531-
arg: DiagnosticArgValue<'static>,
531+
arg: impl IntoDiagnosticArg,
532532
) -> &mut Self);
533533

534534
forward!(pub fn subdiagnostic(

compiler/rustc_macros/src/diagnostics/diagnostic.rs

+42-13
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ use crate::diagnostics::error::{
55
SessionDiagnosticDeriveError,
66
};
77
use crate::diagnostics::utils::{
8-
report_error_if_not_applied_to_span, type_matches_path, Applicability, FieldInfo, FieldInnerTy,
9-
HasFieldMap, SetOnce,
8+
report_error_if_not_applied_to_span, report_type_error, type_is_unit, type_matches_path,
9+
Applicability, FieldInfo, FieldInnerTy, HasFieldMap, SetOnce,
1010
};
11-
use proc_macro2::TokenStream;
11+
use proc_macro2::{Ident, TokenStream};
1212
use quote::{format_ident, quote};
1313
use std::collections::HashMap;
1414
use std::str::FromStr;
@@ -113,7 +113,7 @@ impl<'a> SessionDiagnosticDerive<'a> {
113113
quote! {
114114
#diag.set_arg(
115115
stringify!(#ident),
116-
#field_binding.into_diagnostic_arg()
116+
#field_binding
117117
);
118118
}
119119
} else {
@@ -388,7 +388,8 @@ impl SessionDiagnosticDeriveBuilder {
388388
) -> Result<TokenStream, SessionDiagnosticDeriveError> {
389389
let diag = &self.diag;
390390

391-
let name = attr.path.segments.last().unwrap().ident.to_string();
391+
let ident = &attr.path.segments.last().unwrap().ident;
392+
let name = ident.to_string();
392393
let name = name.as_str();
393394

394395
let meta = attr.parse_meta()?;
@@ -405,9 +406,18 @@ impl SessionDiagnosticDeriveBuilder {
405406
#diag.set_span(#binding);
406407
})
407408
}
408-
"label" | "note" | "help" => {
409+
"label" => {
409410
report_error_if_not_applied_to_span(attr, &info)?;
410-
Ok(self.add_subdiagnostic(binding, name, name))
411+
Ok(self.add_spanned_subdiagnostic(binding, ident, name))
412+
}
413+
"note" | "help" => {
414+
if type_matches_path(&info.ty, &["rustc_span", "Span"]) {
415+
Ok(self.add_spanned_subdiagnostic(binding, ident, name))
416+
} else if type_is_unit(&info.ty) {
417+
Ok(self.add_subdiagnostic(ident, name))
418+
} else {
419+
report_type_error(attr, "`Span` or `()`")?;
420+
}
411421
}
412422
"subdiagnostic" => Ok(quote! { #diag.subdiagnostic(#binding); }),
413423
_ => throw_invalid_attr!(attr, &meta, |diag| {
@@ -416,9 +426,18 @@ impl SessionDiagnosticDeriveBuilder {
416426
}),
417427
},
418428
Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(ref s), .. }) => match name {
419-
"label" | "note" | "help" => {
429+
"label" => {
420430
report_error_if_not_applied_to_span(attr, &info)?;
421-
Ok(self.add_subdiagnostic(binding, name, &s.value()))
431+
Ok(self.add_spanned_subdiagnostic(binding, ident, &s.value()))
432+
}
433+
"note" | "help" => {
434+
if type_matches_path(&info.ty, &["rustc_span", "Span"]) {
435+
Ok(self.add_spanned_subdiagnostic(binding, ident, &s.value()))
436+
} else if type_is_unit(&info.ty) {
437+
Ok(self.add_subdiagnostic(ident, &s.value()))
438+
} else {
439+
report_type_error(attr, "`Span` or `()`")?;
440+
}
422441
}
423442
_ => throw_invalid_attr!(attr, &meta, |diag| {
424443
diag.help("only `label`, `note` and `help` are valid field attributes")
@@ -510,12 +529,12 @@ impl SessionDiagnosticDeriveBuilder {
510529
}
511530
}
512531

513-
/// Adds a subdiagnostic by generating a `diag.span_$kind` call with the current slug and
514-
/// `fluent_attr_identifier`.
515-
fn add_subdiagnostic(
532+
/// Adds a spanned subdiagnostic by generating a `diag.span_$kind` call with the current slug
533+
/// and `fluent_attr_identifier`.
534+
fn add_spanned_subdiagnostic(
516535
&self,
517536
field_binding: TokenStream,
518-
kind: &str,
537+
kind: &Ident,
519538
fluent_attr_identifier: &str,
520539
) -> TokenStream {
521540
let diag = &self.diag;
@@ -531,6 +550,16 @@ impl SessionDiagnosticDeriveBuilder {
531550
}
532551
}
533552

553+
/// Adds a subdiagnostic by generating a `diag.span_$kind` call with the current slug
554+
/// and `fluent_attr_identifier`.
555+
fn add_subdiagnostic(&self, kind: &Ident, fluent_attr_identifier: &str) -> TokenStream {
556+
let diag = &self.diag;
557+
let slug = self.slug.as_ref().map(|(slug, _)| slug.as_str()).unwrap_or("missing-slug");
558+
quote! {
559+
#diag.#kind(rustc_errors::DiagnosticMessage::fluent_attr(#slug, #fluent_attr_identifier));
560+
}
561+
}
562+
534563
fn span_and_applicability_of_ty(
535564
&self,
536565
info: FieldInfo<'_>,

compiler/rustc_macros/src/diagnostics/subdiagnostic.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
349349
let generated = quote! {
350350
#diag.set_arg(
351351
stringify!(#ident),
352-
#binding.into_diagnostic_arg()
352+
#binding
353353
);
354354
};
355355

compiler/rustc_macros/src/diagnostics/utils.rs

+33-21
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use proc_macro2::TokenStream;
44
use quote::{format_ident, quote, ToTokens};
55
use std::collections::BTreeSet;
66
use std::str::FromStr;
7-
use syn::{spanned::Spanned, Attribute, Meta, Type, Visibility};
7+
use syn::{spanned::Spanned, Attribute, Meta, Type, TypeTuple, Visibility};
88
use synstructure::BindingInfo;
99

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

28-
/// Reports an error if the field's type is not `Applicability`.
28+
/// Checks whether the type `ty` is `()`.
29+
pub(crate) fn type_is_unit(ty: &Type) -> bool {
30+
if let Type::Tuple(TypeTuple { elems, .. }) = ty { elems.is_empty() } else { false }
31+
}
32+
33+
/// Reports a type error for field with `attr`.
34+
pub(crate) fn report_type_error(
35+
attr: &Attribute,
36+
ty_name: &str,
37+
) -> Result<!, SessionDiagnosticDeriveError> {
38+
let name = attr.path.segments.last().unwrap().ident.to_string();
39+
let meta = attr.parse_meta()?;
40+
41+
throw_span_err!(
42+
attr.span().unwrap(),
43+
&format!(
44+
"the `#[{}{}]` attribute can only be applied to fields of type {}",
45+
name,
46+
match meta {
47+
Meta::Path(_) => "",
48+
Meta::NameValue(_) => " = ...",
49+
Meta::List(_) => "(...)",
50+
},
51+
ty_name
52+
)
53+
);
54+
}
55+
56+
/// Reports an error if the field's type does not match `path`.
2957
fn report_error_if_not_applied_to_ty(
3058
attr: &Attribute,
3159
info: &FieldInfo<'_>,
3260
path: &[&str],
3361
ty_name: &str,
3462
) -> Result<(), SessionDiagnosticDeriveError> {
3563
if !type_matches_path(&info.ty, path) {
36-
let name = attr.path.segments.last().unwrap().ident.to_string();
37-
let name = name.as_str();
38-
let meta = attr.parse_meta()?;
39-
40-
throw_span_err!(
41-
attr.span().unwrap(),
42-
&format!(
43-
"the `#[{}{}]` attribute can only be applied to fields of type `{}`",
44-
name,
45-
match meta {
46-
Meta::Path(_) => "",
47-
Meta::NameValue(_) => " = ...",
48-
Meta::List(_) => "(...)",
49-
},
50-
ty_name
51-
)
52-
);
64+
report_type_error(attr, ty_name)?;
5365
}
5466

5567
Ok(())
@@ -64,7 +76,7 @@ pub(crate) fn report_error_if_not_applied_to_applicability(
6476
attr,
6577
info,
6678
&["rustc_errors", "Applicability"],
67-
"Applicability",
79+
"`Applicability`",
6880
)
6981
}
7082

@@ -73,7 +85,7 @@ pub(crate) fn report_error_if_not_applied_to_span(
7385
attr: &Attribute,
7486
info: &FieldInfo<'_>,
7587
) -> Result<(), SessionDiagnosticDeriveError> {
76-
report_error_if_not_applied_to_ty(attr, info, &["rustc_span", "Span"], "Span")
88+
report_error_if_not_applied_to_ty(attr, info, &["rustc_span", "Span"], "`Span`")
7789
}
7890

7991
/// Inner type of a field and type of wrapper.

compiler/rustc_macros/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#![feature(allow_internal_unstable)]
22
#![feature(let_else)]
3+
#![feature(never_type)]
34
#![feature(proc_macro_diagnostic)]
45
#![allow(rustc::default_hash_types)]
56
#![recursion_limit = "128"]

compiler/rustc_typeck/src/astconv/errors.rs

+7-70
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::astconv::AstConv;
2+
use crate::errors::{ManualImplementation, MissingTypeParams};
23
use rustc_data_structures::fx::FxHashMap;
34
use rustc_errors::{pluralize, struct_span_err, Applicability, ErrorGuaranteed};
45
use rustc_hir as hir;
@@ -24,65 +25,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
2425
if missing_type_params.is_empty() {
2526
return;
2627
}
27-
let display =
28-
missing_type_params.iter().map(|n| format!("`{}`", n)).collect::<Vec<_>>().join(", ");
29-
let mut err = struct_span_err!(
30-
self.tcx().sess,
28+
29+
self.tcx().sess.emit_err(MissingTypeParams {
3130
span,
32-
E0393,
33-
"the type parameter{} {} must be explicitly specified",
34-
pluralize!(missing_type_params.len()),
35-
display,
36-
);
37-
err.span_label(
38-
self.tcx().def_span(def_id),
39-
&format!(
40-
"type parameter{} {} must be specified for this",
41-
pluralize!(missing_type_params.len()),
42-
display,
43-
),
44-
);
45-
let mut suggested = false;
46-
if let (Ok(snippet), true) = (
47-
self.tcx().sess.source_map().span_to_snippet(span),
48-
// Don't suggest setting the type params if there are some already: the order is
49-
// tricky to get right and the user will already know what the syntax is.
31+
def_span: self.tcx().def_span(def_id),
32+
missing_type_params,
5033
empty_generic_args,
51-
) {
52-
if snippet.ends_with('>') {
53-
// The user wrote `Trait<'a, T>` or similar. To provide an accurate suggestion
54-
// we would have to preserve the right order. For now, as clearly the user is
55-
// aware of the syntax, we do nothing.
56-
} else {
57-
// The user wrote `Iterator`, so we don't have a type we can suggest, but at
58-
// least we can clue them to the correct syntax `Iterator<Type>`.
59-
err.span_suggestion(
60-
span,
61-
&format!(
62-
"set the type parameter{plural} to the desired type{plural}",
63-
plural = pluralize!(missing_type_params.len()),
64-
),
65-
format!("{}<{}>", snippet, missing_type_params.join(", ")),
66-
Applicability::HasPlaceholders,
67-
);
68-
suggested = true;
69-
}
70-
}
71-
if !suggested {
72-
err.span_label(
73-
span,
74-
format!(
75-
"missing reference{} to {}",
76-
pluralize!(missing_type_params.len()),
77-
display,
78-
),
79-
);
80-
}
81-
err.note(
82-
"because of the default `Self` reference, type parameters must be \
83-
specified on object types",
84-
);
85-
err.emit();
34+
});
8635
}
8736

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

173122
if is_impl {
174123
let trait_name = self.tcx().def_path_str(trait_def_id);
175-
struct_span_err!(
176-
self.tcx().sess,
177-
span,
178-
E0183,
179-
"manual implementations of `{}` are experimental",
180-
trait_name,
181-
)
182-
.span_label(
183-
span,
184-
format!("manual implementations of `{}` are experimental", trait_name),
185-
)
186-
.help("add `#![feature(unboxed_closures)]` to the crate attributes to enable")
187-
.emit();
124+
self.tcx().sess.emit_err(ManualImplementation { span, trait_name });
188125
}
189126
}
190127

0 commit comments

Comments
 (0)