Skip to content

Commit 9e1fcaf

Browse files
Rollup merge of #96760 - davidtwco:diagnostic-translation-vec, r=oli-obk
diagnostics: port more diagnostics to derive + add support for `Vec` fields - Port "unconstrained opaque type" diagnostic to using the derive. - Allow `Vec` fields in diagnostic derive - enables support for diagnostics that have multiple primary spans, or have subdiagnostics repeated at multiple locations. `Vec<..>` fields in the diagnostic derive become loops in the generated code. - Add `create_{err,warning}` - there wasn't a way to create a diagnostic from a struct and not emit it straight away. - Port "explicit generic args w/ impl trait" diagnostic to using the derive. r? ``@oli-obk`` cc ``@pvdrz``
2 parents 3cba764 + af47257 commit 9e1fcaf

File tree

10 files changed

+165
-70
lines changed

10 files changed

+165
-70
lines changed

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

+11
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,14 @@ typeck-add-return-type-missing-here = a return type might be missing here
9090
typeck-expected-default-return-type = expected `()` because of default return type
9191
9292
typeck-expected-return-type = expected `{$expected}` because of return type
93+
94+
typeck-unconstrained-opaque-type = unconstrained opaque type
95+
.note = `{$name}` must be used in combination with a concrete type within the same module
96+
97+
typeck-explicit-generic-args-with-impl-trait =
98+
cannot provide explicit generic arguments when `impl Trait` is used in argument position
99+
.label = explicit generic argument not allowed
100+
.note = see issue #83701 <https://github.com/rust-lang/rust/issues/83701> for more information
101+
102+
typeck-explicit-generic-args-with-impl-trait-feature =
103+
add `#![feature(explicit_generic_args_with_impl_trait)]` to the crate attributes to enable

compiler/rustc_macros/src/diagnostics/diagnostic.rs

+25-20
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ use crate::diagnostics::error::{
55
SessionDiagnosticDeriveError,
66
};
77
use crate::diagnostics::utils::{
8-
option_inner_ty, report_error_if_not_applied_to_span, type_matches_path, Applicability,
9-
FieldInfo, HasFieldMap, SetOnce,
8+
report_error_if_not_applied_to_span, type_matches_path, Applicability, FieldInfo, FieldInnerTy,
9+
HasFieldMap, SetOnce,
1010
};
1111
use proc_macro2::TokenStream;
1212
use quote::{format_ident, quote};
@@ -353,35 +353,40 @@ impl SessionDiagnosticDeriveBuilder {
353353
info: FieldInfo<'_>,
354354
) -> Result<TokenStream, SessionDiagnosticDeriveError> {
355355
let field_binding = &info.binding.binding;
356-
let option_ty = option_inner_ty(&info.ty);
357-
let generated_code = self.generate_non_option_field_code(
356+
357+
let inner_ty = FieldInnerTy::from_type(&info.ty);
358+
let name = attr.path.segments.last().unwrap().ident.to_string();
359+
let (binding, needs_destructure) = match (name.as_str(), &inner_ty) {
360+
// `primary_span` can accept a `Vec<Span>` so don't destructure that.
361+
("primary_span", FieldInnerTy::Vec(_)) => (quote! { #field_binding.clone() }, false),
362+
_ => (quote! { *#field_binding }, true),
363+
};
364+
365+
let generated_code = self.generate_inner_field_code(
358366
attr,
359367
FieldInfo {
360368
vis: info.vis,
361369
binding: info.binding,
362-
ty: option_ty.unwrap_or(&info.ty),
370+
ty: inner_ty.inner_type().unwrap_or(&info.ty),
363371
span: info.span,
364372
},
373+
binding,
365374
)?;
366375

367-
if option_ty.is_none() {
368-
Ok(quote! { #generated_code })
376+
if needs_destructure {
377+
Ok(inner_ty.with(field_binding, generated_code))
369378
} else {
370-
Ok(quote! {
371-
if let Some(#field_binding) = #field_binding {
372-
#generated_code
373-
}
374-
})
379+
Ok(generated_code)
375380
}
376381
}
377382

378-
fn generate_non_option_field_code(
383+
fn generate_inner_field_code(
379384
&mut self,
380385
attr: &Attribute,
381386
info: FieldInfo<'_>,
387+
binding: TokenStream,
382388
) -> Result<TokenStream, SessionDiagnosticDeriveError> {
383389
let diag = &self.diag;
384-
let field_binding = &info.binding.binding;
385390

386391
let name = attr.path.segments.last().unwrap().ident.to_string();
387392
let name = name.as_str();
@@ -397,14 +402,14 @@ impl SessionDiagnosticDeriveBuilder {
397402
"primary_span" => {
398403
report_error_if_not_applied_to_span(attr, &info)?;
399404
Ok(quote! {
400-
#diag.set_span(*#field_binding);
405+
#diag.set_span(#binding);
401406
})
402407
}
403408
"label" | "note" | "help" => {
404409
report_error_if_not_applied_to_span(attr, &info)?;
405-
Ok(self.add_subdiagnostic(field_binding, name, name))
410+
Ok(self.add_subdiagnostic(binding, name, name))
406411
}
407-
"subdiagnostic" => Ok(quote! { #diag.subdiagnostic(*#field_binding); }),
412+
"subdiagnostic" => Ok(quote! { #diag.subdiagnostic(#binding); }),
408413
_ => throw_invalid_attr!(attr, &meta, |diag| {
409414
diag
410415
.help("only `skip_arg`, `primary_span`, `label`, `note`, `help` and `subdiagnostic` are valid field attributes")
@@ -413,7 +418,7 @@ impl SessionDiagnosticDeriveBuilder {
413418
Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(ref s), .. }) => match name {
414419
"label" | "note" | "help" => {
415420
report_error_if_not_applied_to_span(attr, &info)?;
416-
Ok(self.add_subdiagnostic(field_binding, name, &s.value()))
421+
Ok(self.add_subdiagnostic(binding, name, &s.value()))
417422
}
418423
_ => throw_invalid_attr!(attr, &meta, |diag| {
419424
diag.help("only `label`, `note` and `help` are valid field attributes")
@@ -509,7 +514,7 @@ impl SessionDiagnosticDeriveBuilder {
509514
/// `fluent_attr_identifier`.
510515
fn add_subdiagnostic(
511516
&self,
512-
field_binding: &proc_macro2::Ident,
517+
field_binding: TokenStream,
513518
kind: &str,
514519
fluent_attr_identifier: &str,
515520
) -> TokenStream {
@@ -520,7 +525,7 @@ impl SessionDiagnosticDeriveBuilder {
520525
let fn_name = format_ident!("span_{}", kind);
521526
quote! {
522527
#diag.#fn_name(
523-
*#field_binding,
528+
#field_binding,
524529
rustc_errors::DiagnosticMessage::fluent_attr(#slug, #fluent_attr_identifier)
525530
);
526531
}

compiler/rustc_macros/src/diagnostics/subdiagnostic.rs

+5-13
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ use crate::diagnostics::error::{
55
SessionDiagnosticDeriveError,
66
};
77
use crate::diagnostics::utils::{
8-
option_inner_ty, report_error_if_not_applied_to_applicability,
9-
report_error_if_not_applied_to_span, Applicability, FieldInfo, HasFieldMap, SetOnce,
8+
report_error_if_not_applied_to_applicability, report_error_if_not_applied_to_span,
9+
Applicability, FieldInfo, FieldInnerTy, HasFieldMap, SetOnce,
1010
};
1111
use proc_macro2::TokenStream;
1212
use quote::{format_ident, quote};
@@ -301,11 +301,11 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
301301
) -> Result<TokenStream, SessionDiagnosticDeriveError> {
302302
let ast = binding.ast();
303303

304-
let option_ty = option_inner_ty(&ast.ty);
304+
let inner_ty = FieldInnerTy::from_type(&ast.ty);
305305
let info = FieldInfo {
306306
vis: &ast.vis,
307307
binding: binding,
308-
ty: option_ty.unwrap_or(&ast.ty),
308+
ty: inner_ty.inner_type().unwrap_or(&ast.ty),
309309
span: &ast.span(),
310310
};
311311

@@ -353,15 +353,7 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
353353
);
354354
};
355355

356-
if option_ty.is_none() {
357-
Ok(quote! { #generated })
358-
} else {
359-
Ok(quote! {
360-
if let Some(#binding) = #binding {
361-
#generated
362-
}
363-
})
364-
}
356+
Ok(inner_ty.with(binding, generated))
365357
}
366358

367359
fn into_tokens(&mut self) -> Result<TokenStream, SessionDiagnosticDeriveError> {

compiler/rustc_macros/src/diagnostics/utils.rs

+55-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::diagnostics::error::{span_err, throw_span_err, SessionDiagnosticDeriveError};
22
use proc_macro::Span;
33
use proc_macro2::TokenStream;
4-
use quote::{format_ident, quote};
4+
use quote::{format_ident, quote, ToTokens};
55
use std::collections::BTreeSet;
66
use std::str::FromStr;
77
use syn::{spanned::Spanned, Attribute, Meta, Type, Visibility};
@@ -76,22 +76,71 @@ pub(crate) fn report_error_if_not_applied_to_span(
7676
report_error_if_not_applied_to_ty(attr, info, &["rustc_span", "Span"], "Span")
7777
}
7878

79-
/// If `ty` is an Option, returns `Some(inner type)`, otherwise returns `None`.
80-
pub(crate) fn option_inner_ty(ty: &Type) -> Option<&Type> {
81-
if type_matches_path(ty, &["std", "option", "Option"]) {
79+
/// Inner type of a field and type of wrapper.
80+
pub(crate) enum FieldInnerTy<'ty> {
81+
/// Field is wrapped in a `Option<$inner>`.
82+
Option(&'ty Type),
83+
/// Field is wrapped in a `Vec<$inner>`.
84+
Vec(&'ty Type),
85+
/// Field isn't wrapped in an outer type.
86+
None,
87+
}
88+
89+
impl<'ty> FieldInnerTy<'ty> {
90+
/// Returns inner type for a field, if there is one.
91+
///
92+
/// - If `ty` is an `Option`, returns `FieldInnerTy::Option { inner: (inner type) }`.
93+
/// - If `ty` is a `Vec`, returns `FieldInnerTy::Vec { inner: (inner type) }`.
94+
/// - Otherwise returns `None`.
95+
pub(crate) fn from_type(ty: &'ty Type) -> Self {
96+
let variant: &dyn Fn(&'ty Type) -> FieldInnerTy<'ty> =
97+
if type_matches_path(ty, &["std", "option", "Option"]) {
98+
&FieldInnerTy::Option
99+
} else if type_matches_path(ty, &["std", "vec", "Vec"]) {
100+
&FieldInnerTy::Vec
101+
} else {
102+
return FieldInnerTy::None;
103+
};
104+
82105
if let Type::Path(ty_path) = ty {
83106
let path = &ty_path.path;
84107
let ty = path.segments.iter().last().unwrap();
85108
if let syn::PathArguments::AngleBracketed(bracketed) = &ty.arguments {
86109
if bracketed.args.len() == 1 {
87110
if let syn::GenericArgument::Type(ty) = &bracketed.args[0] {
88-
return Some(ty);
111+
return variant(ty);
89112
}
90113
}
91114
}
92115
}
116+
117+
unreachable!();
118+
}
119+
120+
/// Returns `Option` containing inner type if there is one.
121+
pub(crate) fn inner_type(&self) -> Option<&'ty Type> {
122+
match self {
123+
FieldInnerTy::Option(inner) | FieldInnerTy::Vec(inner) => Some(inner),
124+
FieldInnerTy::None => None,
125+
}
126+
}
127+
128+
/// Surrounds `inner` with destructured wrapper type, exposing inner type as `binding`.
129+
pub(crate) fn with(&self, binding: impl ToTokens, inner: impl ToTokens) -> TokenStream {
130+
match self {
131+
FieldInnerTy::Option(..) => quote! {
132+
if let Some(#binding) = #binding {
133+
#inner
134+
}
135+
},
136+
FieldInnerTy::Vec(..) => quote! {
137+
for #binding in #binding {
138+
#inner
139+
}
140+
},
141+
FieldInnerTy::None => quote! { #inner },
142+
}
93143
}
94-
None
95144
}
96145

97146
/// Field information passed to the builder. Deliberately omits attrs to discourage the

compiler/rustc_session/src/parse.rs

+16-2
Original file line numberDiff line numberDiff line change
@@ -289,12 +289,26 @@ impl ParseSess {
289289
self.proc_macro_quoted_spans.lock().clone()
290290
}
291291

292+
pub fn create_err<'a>(
293+
&'a self,
294+
err: impl SessionDiagnostic<'a>,
295+
) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
296+
err.into_diagnostic(self)
297+
}
298+
292299
pub fn emit_err<'a>(&'a self, err: impl SessionDiagnostic<'a>) -> ErrorGuaranteed {
293-
err.into_diagnostic(self).emit()
300+
self.create_err(err).emit()
301+
}
302+
303+
pub fn create_warning<'a>(
304+
&'a self,
305+
warning: impl SessionDiagnostic<'a, ()>,
306+
) -> DiagnosticBuilder<'a, ()> {
307+
warning.into_diagnostic(self)
294308
}
295309

296310
pub fn emit_warning<'a>(&'a self, warning: impl SessionDiagnostic<'a, ()>) {
297-
warning.into_diagnostic(self).emit()
311+
self.create_warning(warning).emit()
298312
}
299313

300314
pub fn struct_err(

compiler/rustc_session/src/session.rs

+12
Original file line numberDiff line numberDiff line change
@@ -413,9 +413,21 @@ impl Session {
413413
pub fn err(&self, msg: impl Into<DiagnosticMessage>) -> ErrorGuaranteed {
414414
self.diagnostic().err(msg)
415415
}
416+
pub fn create_err<'a>(
417+
&'a self,
418+
err: impl SessionDiagnostic<'a>,
419+
) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
420+
self.parse_sess.create_err(err)
421+
}
416422
pub fn emit_err<'a>(&'a self, err: impl SessionDiagnostic<'a>) -> ErrorGuaranteed {
417423
self.parse_sess.emit_err(err)
418424
}
425+
pub fn create_warning<'a>(
426+
&'a self,
427+
err: impl SessionDiagnostic<'a, ()>,
428+
) -> DiagnosticBuilder<'a, ()> {
429+
self.parse_sess.create_warning(err)
430+
}
419431
pub fn emit_warning<'a>(&'a self, warning: impl SessionDiagnostic<'a, ()>) {
420432
self.parse_sess.emit_warning(warning)
421433
}

compiler/rustc_typeck/src/astconv/generics.rs

+6-22
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ use crate::astconv::{
33
AstConv, CreateSubstsForGenericArgsCtxt, ExplicitLateBound, GenericArgCountMismatch,
44
GenericArgCountResult, GenericArgPosition,
55
};
6-
use crate::errors::AssocTypeBindingNotAllowed;
6+
use crate::errors::{
7+
AssocTypeBindingNotAllowed, ExplicitGenericArgsWithImplTrait,
8+
ExplicitGenericArgsWithImplTraitFeature,
9+
};
710
use crate::structured_errors::{GenericArgsInfo, StructuredDiagnostic, WrongNumberOfGenericArgs};
811
use rustc_ast::ast::ParamKindOrd;
912
use rustc_errors::{struct_span_err, Applicability, Diagnostic, MultiSpan};
@@ -636,29 +639,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
636639
})
637640
.collect::<Vec<_>>();
638641

639-
let mut err = struct_span_err! {
640-
tcx.sess,
641-
spans.clone(),
642-
E0632,
643-
"cannot provide explicit generic arguments when `impl Trait` is \
644-
used in argument position"
645-
};
646-
647-
for span in spans {
648-
err.span_label(span, "explicit generic argument not allowed");
649-
}
650-
651-
err.note(
652-
"see issue #83701 <https://github.com/rust-lang/rust/issues/83701> \
653-
for more information",
654-
);
642+
let mut err = tcx.sess.create_err(ExplicitGenericArgsWithImplTrait { spans });
655643
if tcx.sess.is_nightly_build() {
656-
err.help(
657-
"add `#![feature(explicit_generic_args_with_impl_trait)]` \
658-
to the crate attributes to enable",
659-
);
644+
err.subdiagnostic(ExplicitGenericArgsWithImplTraitFeature);
660645
}
661-
662646
err.emit();
663647
}
664648

compiler/rustc_typeck/src/collect/type_of.rs

+5-7
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use rustc_span::{Span, DUMMY_SP};
1414

1515
use super::ItemCtxt;
1616
use super::{bad_placeholder, is_suggestable_infer_ty};
17+
use crate::errors::UnconstrainedOpaqueType;
1718

1819
/// Computes the relevant generic parameter for a potential generic const argument.
1920
///
@@ -682,13 +683,10 @@ fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> {
682683
match locator.found {
683684
Some(hidden) => hidden.ty,
684685
None => {
685-
let span = tcx.def_span(def_id);
686-
let name = tcx.item_name(tcx.local_parent(def_id).to_def_id());
687-
let label = format!(
688-
"`{}` must be used in combination with a concrete type within the same module",
689-
name
690-
);
691-
tcx.sess.struct_span_err(span, "unconstrained opaque type").note(&label).emit();
686+
tcx.sess.emit_err(UnconstrainedOpaqueType {
687+
span: tcx.def_span(def_id),
688+
name: tcx.item_name(tcx.local_parent(def_id).to_def_id()),
689+
});
692690
tcx.ty_error()
693691
}
694692
}

compiler/rustc_typeck/src/errors.rs

+22
Original file line numberDiff line numberDiff line change
@@ -228,3 +228,25 @@ pub enum ExpectedReturnTypeLabel<'tcx> {
228228
expected: Ty<'tcx>,
229229
},
230230
}
231+
232+
#[derive(SessionDiagnostic)]
233+
#[error(slug = "typeck-unconstrained-opaque-type")]
234+
#[note]
235+
pub struct UnconstrainedOpaqueType {
236+
#[primary_span]
237+
pub span: Span,
238+
pub name: Symbol,
239+
}
240+
241+
#[derive(SessionDiagnostic)]
242+
#[error(code = "E0632", slug = "typeck-explicit-generic-args-with-impl-trait")]
243+
#[note]
244+
pub struct ExplicitGenericArgsWithImplTrait {
245+
#[primary_span]
246+
#[label]
247+
pub spans: Vec<Span>,
248+
}
249+
250+
#[derive(SessionSubdiagnostic)]
251+
#[help(slug = "typeck-explicit-generic-args-with-impl-trait-feature")]
252+
pub struct ExplicitGenericArgsWithImplTraitFeature;

0 commit comments

Comments
 (0)