From 7b7061dd898b5a9c06477941e2d0840e27b9c67b Mon Sep 17 00:00:00 2001 From: David Wood Date: Sat, 7 May 2022 06:02:11 +0100 Subject: [PATCH 1/7] macros: spanless subdiagnostics from `()` fields Type attributes could previously be used to support spanless subdiagnostics but these couldn't easily be made optional in the same way that spanned subdiagnostics could by using a field attribute on a field with an `Option` type. Spanless subdiagnostics can now be specified on fields with `()` type or `Option<()>` type. Signed-off-by: David Wood --- .../src/diagnostics/diagnostic.rs | 53 +++++++++++++----- .../rustc_macros/src/diagnostics/utils.rs | 54 +++++++++++-------- compiler/rustc_macros/src/lib.rs | 1 + .../session-diagnostic/diagnostic-derive.rs | 22 ++++++++ 4 files changed, 97 insertions(+), 33 deletions(-) diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs index 83fc7bcde8a..6788ce37eae 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs @@ -5,10 +5,10 @@ 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; @@ -388,7 +388,8 @@ fn generate_inner_field_code( ) -> Result { 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()?; @@ -405,9 +406,18 @@ fn generate_inner_field_code( #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| { @@ -416,9 +426,18 @@ fn generate_inner_field_code( }), }, 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") @@ -510,12 +529,12 @@ fn generate_inner_field_code( } } - /// 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; @@ -531,6 +550,16 @@ fn add_subdiagnostic( } } + /// 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<'_>, diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs index aba861fc6aa..af5a30880e0 100644 --- a/compiler/rustc_macros/src/diagnostics/utils.rs +++ b/compiler/rustc_macros/src/diagnostics/utils.rs @@ -4,7 +4,7 @@ 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`. @@ -25,7 +25,35 @@ 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 { + 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<'_>, @@ -33,23 +61,7 @@ fn report_error_if_not_applied_to_ty( 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(()) @@ -64,7 +76,7 @@ pub(crate) fn report_error_if_not_applied_to_applicability( attr, info, &["rustc_errors", "Applicability"], - "Applicability", + "`Applicability`", ) } @@ -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. diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs index b01e01414e8..0baebdb7130 100644 --- a/compiler/rustc_macros/src/lib.rs +++ b/compiler/rustc_macros/src/lib.rs @@ -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"] diff --git a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs index c63410fa35b..1bc7475eca7 100644 --- a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs +++ b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs @@ -482,3 +482,25 @@ struct VecField { #[label] spans: Vec, } + +#[derive(SessionDiagnostic)] +#[error(code = "E0123", slug = "foo")] +struct UnitField { + #[primary_span] + spans: Span, + #[help] + foo: (), + #[help = "a"] + bar: (), +} + +#[derive(SessionDiagnostic)] +#[error(code = "E0123", slug = "foo")] +struct OptUnitField { + #[primary_span] + spans: Span, + #[help] + foo: Option<()>, + #[help = "a"] + bar: Option<()>, +} From 5685abc96b4e2fa1be05ed4beefb72708c233739 Mon Sep 17 00:00:00 2001 From: David Wood Date: Sat, 7 May 2022 06:05:01 +0100 Subject: [PATCH 2/7] typeck: simplify error type using `()` field Using new support for spanless subdiagnostics from `()` fields in the diagnostic derive, simplify the "explicit generic args with impl trait" diagnostic's struct. Signed-off-by: David Wood --- .../rustc_error_messages/locales/en-US/typeck.ftl | 4 +--- compiler/rustc_typeck/src/astconv/generics.rs | 14 +++++--------- compiler/rustc_typeck/src/errors.rs | 6 ++---- 3 files changed, 8 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_error_messages/locales/en-US/typeck.ftl b/compiler/rustc_error_messages/locales/en-US/typeck.ftl index 9195d7a2b8f..be1a45fd872 100644 --- a/compiler/rustc_error_messages/locales/en-US/typeck.ftl +++ b/compiler/rustc_error_messages/locales/en-US/typeck.ftl @@ -99,6 +99,4 @@ 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 for more information - -typeck-explicit-generic-args-with-impl-trait-feature = - add `#![feature(explicit_generic_args_with_impl_trait)]` to the crate attributes to enable + .help = add `#![feature(explicit_generic_args_with_impl_trait)]` to the crate attributes to enable diff --git a/compiler/rustc_typeck/src/astconv/generics.rs b/compiler/rustc_typeck/src/astconv/generics.rs index 38c29d3874c..dc4bc8fb55a 100644 --- a/compiler/rustc_typeck/src/astconv/generics.rs +++ b/compiler/rustc_typeck/src/astconv/generics.rs @@ -3,10 +3,7 @@ AstConv, CreateSubstsForGenericArgsCtxt, ExplicitLateBound, GenericArgCountMismatch, GenericArgCountResult, GenericArgPosition, }; -use crate::errors::{ - AssocTypeBindingNotAllowed, ExplicitGenericArgsWithImplTrait, - ExplicitGenericArgsWithImplTraitFeature, -}; +use crate::errors::{AssocTypeBindingNotAllowed, ExplicitGenericArgsWithImplTrait}; use crate::structured_errors::{GenericArgsInfo, StructuredDiagnostic, WrongNumberOfGenericArgs}; use rustc_ast::ast::ParamKindOrd; use rustc_errors::{struct_span_err, Applicability, Diagnostic, MultiSpan}; @@ -639,11 +636,10 @@ pub(crate) fn check_impl_trait( }) .collect::>(); - let mut err = tcx.sess.create_err(ExplicitGenericArgsWithImplTrait { spans }); - if tcx.sess.is_nightly_build() { - err.subdiagnostic(ExplicitGenericArgsWithImplTraitFeature); - } - err.emit(); + tcx.sess.emit_err(ExplicitGenericArgsWithImplTrait { + spans, + is_nightly_build: tcx.sess.is_nightly_build().then_some(()), + }); } impl_trait diff --git a/compiler/rustc_typeck/src/errors.rs b/compiler/rustc_typeck/src/errors.rs index 81d4c9135ef..540fd63e137 100644 --- a/compiler/rustc_typeck/src/errors.rs +++ b/compiler/rustc_typeck/src/errors.rs @@ -247,8 +247,6 @@ pub struct ExplicitGenericArgsWithImplTrait { #[primary_span] #[label] pub spans: Vec, + #[help] + pub is_nightly_build: Option<()>, } - -#[derive(SessionSubdiagnostic)] -#[help(slug = "typeck-explicit-generic-args-with-impl-trait-feature")] -pub struct ExplicitGenericArgsWithImplTraitFeature; From b5545f1b0c28a7acba27d1985a934845a013af1b Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 9 May 2022 04:18:35 +0100 Subject: [PATCH 3/7] compiletest: normalize paths from repository root 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. Signed-off-by: David Wood --- src/tools/compiletest/src/runtest.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 6d94fe3ebb9..a59a0584d5e 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -3494,22 +3494,21 @@ fn normalize_output(&self, output: &str, custom_rules: &[(String, String)]) -> S normalize_path(parent_dir, "$DIR"); // Paths into the libstd/libcore - let src_dir = self - .config - .src_base - .parent() - .unwrap() - .parent() - .unwrap() - .parent() - .unwrap() - .join("library"); + let base_dir = self.config.src_base.parent().unwrap().parent().unwrap().parent().unwrap(); + let src_dir = base_dir.join("library"); normalize_path(&src_dir, "$SRC_DIR"); + // `ui-fulldeps` tests can show paths to the compiler source when testing macros from + // `rustc_macros` + // eg. /home/user/rust/compiler + let compiler_src_dir = base_dir.join("compiler"); + normalize_path(&compiler_src_dir, "$COMPILER_DIR"); + if let Some(virtual_rust_source_base_dir) = option_env!("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR").map(PathBuf::from) { normalize_path(&virtual_rust_source_base_dir.join("library"), "$SRC_DIR"); + normalize_path(&virtual_rust_source_base_dir.join("compiler"), "$COMPILER_DIR"); } // Paths into the build directory From de3e8ca2f3908dc48fddb0d5b4c14c776017b43e Mon Sep 17 00:00:00 2001 From: David Wood Date: Sat, 7 May 2022 07:26:03 +0100 Subject: [PATCH 4/7] errors: `set_arg` takes `IntoDiagnosticArg` Manual implementors of translatable diagnostics will need to call `set_arg`, not just the derive, so make this function a bit more ergonomic by taking `IntoDiagnosticArg` rather than `DiagnosticArgValue`. Signed-off-by: David Wood --- compiler/rustc_errors/src/diagnostic.rs | 4 ++-- .../rustc_errors/src/diagnostic_builder.rs | 4 ++-- .../src/diagnostics/diagnostic.rs | 2 +- .../src/diagnostics/subdiagnostic.rs | 2 +- .../session-diagnostic/diagnostic-derive.rs | 2 +- .../diagnostic-derive.stderr | 21 +++++++++++++------ 6 files changed, 22 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 83e6a751394..909ed566f64 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -821,9 +821,9 @@ pub fn args(&self) -> &[DiagnosticArg<'static>] { pub fn set_arg( &mut self, name: impl Into>, - arg: DiagnosticArgValue<'static>, + arg: impl IntoDiagnosticArg, ) -> &mut Self { - self.args.push((name.into(), arg)); + self.args.push((name.into(), arg.into_diagnostic_arg())); self } diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs index 96b730c2baa..53ad6e5a0ed 100644 --- a/compiler/rustc_errors/src/diagnostic_builder.rs +++ b/compiler/rustc_errors/src/diagnostic_builder.rs @@ -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; @@ -528,7 +528,7 @@ pub fn span_labels( forward!(pub fn set_arg( &mut self, name: impl Into>, - arg: DiagnosticArgValue<'static>, + arg: impl IntoDiagnosticArg, ) -> &mut Self); forward!(pub fn subdiagnostic( diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs index 6788ce37eae..dac3e986e7a 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs @@ -113,7 +113,7 @@ pub(crate) fn into_tokens(self) -> TokenStream { quote! { #diag.set_arg( stringify!(#ident), - #field_binding.into_diagnostic_arg() + #field_binding ); } } else { diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index 65b1328682f..ae5b9dbd9ba 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -349,7 +349,7 @@ fn generate_field_code( let generated = quote! { #diag.set_arg( stringify!(#ident), - #binding.into_diagnostic_arg() + #binding ); }; diff --git a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs index 1bc7475eca7..1cdc5d18c28 100644 --- a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs +++ b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs @@ -327,7 +327,7 @@ struct ErrorWithDefaultLabelAttr<'a> { } #[derive(SessionDiagnostic)] -//~^ ERROR no method named `into_diagnostic_arg` found for struct `Hello` in the current scope +//~^ ERROR the trait bound `Hello: IntoDiagnosticArg` is not satisfied #[error(code = "E0123", slug = "foo")] struct ArgFieldWithoutSkip { #[primary_span] diff --git a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr index b1738b60bc0..2583363120a 100644 --- a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr +++ b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr @@ -349,17 +349,26 @@ error: cannot find attribute `nonsense` in this scope LL | #[nonsense] | ^^^^^^^^ -error[E0599]: no method named `into_diagnostic_arg` found for struct `Hello` in the current scope +error[E0277]: the trait bound `Hello: IntoDiagnosticArg` is not satisfied --> $DIR/diagnostic-derive.rs:329:10 | -LL | struct Hello {} - | ------------ method `into_diagnostic_arg` not found for this -... LL | #[derive(SessionDiagnostic)] - | ^^^^^^^^^^^^^^^^^ method not found in `Hello` + | ^^^^^^^^^^^^^^^^^ the trait `IntoDiagnosticArg` is not implemented for `Hello` | + = help: the following other types implement trait `IntoDiagnosticArg`: + &'a str + Ident + String + Symbol + rustc_middle::ty::Ty<'tcx> + usize +note: required by a bound in `DiagnosticBuilder::<'a, G>::set_arg` + --> $COMPILER_DIR/rustc_errors/src/diagnostic_builder.rs:531:19 + | +LL | arg: impl IntoDiagnosticArg, + | ^^^^^^^^^^^^^^^^^ required by this bound in `DiagnosticBuilder::<'a, G>::set_arg` = note: this error originates in the derive macro `SessionDiagnostic` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 43 previous errors -For more information about this error, try `rustc --explain E0599`. +For more information about this error, try `rustc --explain E0277`. From 78cc331bd71e918cef3d33a6557b1be9ae84eb26 Mon Sep 17 00:00:00 2001 From: David Wood Date: Sat, 7 May 2022 07:32:01 +0100 Subject: [PATCH 5/7] typeck: port "missing type params" Port the "the type parameter `T` must be explicitly specified" diagnostic to using a diagnostic struct. Signed-off-by: David Wood --- .../locales/en-US/typeck.ftl | 22 +++++++ compiler/rustc_typeck/src/astconv/errors.rs | 63 ++---------------- compiler/rustc_typeck/src/errors.rs | 65 ++++++++++++++++++- 3 files changed, 92 insertions(+), 58 deletions(-) diff --git a/compiler/rustc_error_messages/locales/en-US/typeck.ftl b/compiler/rustc_error_messages/locales/en-US/typeck.ftl index be1a45fd872..a6b2b1dbd6a 100644 --- a/compiler/rustc_error_messages/locales/en-US/typeck.ftl +++ b/compiler/rustc_error_messages/locales/en-US/typeck.ftl @@ -100,3 +100,25 @@ typeck-explicit-generic-args-with-impl-trait = .label = explicit generic argument not allowed .note = see issue #83701 for more information .help = 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 diff --git a/compiler/rustc_typeck/src/astconv/errors.rs b/compiler/rustc_typeck/src/astconv/errors.rs index 38cc74a5e37..5cfed81017f 100644 --- a/compiler/rustc_typeck/src/astconv/errors.rs +++ b/compiler/rustc_typeck/src/astconv/errors.rs @@ -1,4 +1,5 @@ use crate::astconv::AstConv; +use crate::errors::MissingTypeParams; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{pluralize, struct_span_err, Applicability, ErrorGuaranteed}; use rustc_hir as hir; @@ -24,65 +25,13 @@ pub(crate) fn complain_about_missing_type_params( if missing_type_params.is_empty() { return; } - let display = - missing_type_params.iter().map(|n| format!("`{}`", n)).collect::>().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`. - 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 diff --git a/compiler/rustc_typeck/src/errors.rs b/compiler/rustc_typeck/src/errors.rs index 540fd63e137..93900ae24bb 100644 --- a/compiler/rustc_typeck/src/errors.rs +++ b/compiler/rustc_typeck/src/errors.rs @@ -1,7 +1,10 @@ //! Errors emitted by typeck. -use rustc_errors::Applicability; +use rustc_errors::{ + error_code, Applicability, DiagnosticBuilder, DiagnosticMessage, ErrorGuaranteed, +}; use rustc_macros::{SessionDiagnostic, SessionSubdiagnostic}; use rustc_middle::ty::Ty; +use rustc_session::{parse::ParseSess, SessionDiagnostic}; use rustc_span::{symbol::Ident, Span, Symbol}; #[derive(SessionDiagnostic)] @@ -250,3 +253,63 @@ pub struct ExplicitGenericArgsWithImplTrait { #[help] pub is_nightly_build: Option<()>, } + +pub struct MissingTypeParams { + pub span: Span, + pub def_span: Span, + pub missing_type_params: Vec, + pub empty_generic_args: bool, +} + +// Manual implementation of `SessionDiagnostic` to be able to call `span_to_snippet`. +impl<'a> SessionDiagnostic<'a> for MissingTypeParams { + fn into_diagnostic(self, sess: &'a ParseSess) -> DiagnosticBuilder<'a, ErrorGuaranteed> { + static SLUG: &'static str = "typeck-missing-type-params"; + let mut err = sess.span_diagnostic.struct_span_err_with_code( + self.span, + DiagnosticMessage::fluent(SLUG), + error_code!(E0393), + ); + err.set_arg("parameterCount", self.missing_type_params.len()); + err.set_arg( + "parameters", + self.missing_type_params + .iter() + .map(|n| format!("`{}`", n)) + .collect::>() + .join(", "), + ); + + err.span_label(self.def_span, DiagnosticMessage::fluent_attr(SLUG, "label")); + + let mut suggested = false; + if let (Ok(snippet), true) = ( + sess.source_map().span_to_snippet(self.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. + self.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`. + err.span_suggestion( + self.span, + DiagnosticMessage::fluent_attr(SLUG, "suggestion"), + format!("{}<{}>", snippet, self.missing_type_params.join(", ")), + Applicability::HasPlaceholders, + ); + suggested = true; + } + } + if !suggested { + err.span_label(self.span, DiagnosticMessage::fluent_attr(SLUG, "no-suggestion-label")); + } + + err.note(DiagnosticMessage::fluent_attr(SLUG, "note")); + err + } +} From 664733efd50eb4e2233d083abd159ca71f3844ff Mon Sep 17 00:00:00 2001 From: David Wood Date: Sat, 7 May 2022 07:50:01 +0100 Subject: [PATCH 6/7] typeck: port "manual implementations" Port the "manual implementations of `X` are experimental" diagnostic to use the diagnostic derive. Signed-off-by: David Wood --- .../locales/en-US/typeck.ftl | 5 +++++ compiler/rustc_typeck/src/astconv/errors.rs | 16 ++-------------- compiler/rustc_typeck/src/errors.rs | 10 ++++++++++ 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_error_messages/locales/en-US/typeck.ftl b/compiler/rustc_error_messages/locales/en-US/typeck.ftl index a6b2b1dbd6a..4122464a4a2 100644 --- a/compiler/rustc_error_messages/locales/en-US/typeck.ftl +++ b/compiler/rustc_error_messages/locales/en-US/typeck.ftl @@ -122,3 +122,8 @@ typeck-missing-type-params = *[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 diff --git a/compiler/rustc_typeck/src/astconv/errors.rs b/compiler/rustc_typeck/src/astconv/errors.rs index 5cfed81017f..8fe89c66389 100644 --- a/compiler/rustc_typeck/src/astconv/errors.rs +++ b/compiler/rustc_typeck/src/astconv/errors.rs @@ -1,5 +1,5 @@ use crate::astconv::AstConv; -use crate::errors::MissingTypeParams; +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; @@ -121,19 +121,7 @@ pub(crate) fn complain_about_internal_fn_trait( 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 }); } } diff --git a/compiler/rustc_typeck/src/errors.rs b/compiler/rustc_typeck/src/errors.rs index 93900ae24bb..1cb112e68ce 100644 --- a/compiler/rustc_typeck/src/errors.rs +++ b/compiler/rustc_typeck/src/errors.rs @@ -313,3 +313,13 @@ fn into_diagnostic(self, sess: &'a ParseSess) -> DiagnosticBuilder<'a, ErrorGuar err } } + +#[derive(SessionDiagnostic)] +#[error(code = "E0183", slug = "typeck-manual-implementation")] +#[help] +pub struct ManualImplementation { + #[primary_span] + #[label] + pub span: Span, + pub trait_name: String, +} From 47582471c61e15f9e409b45e11f2f15e61a88e29 Mon Sep 17 00:00:00 2001 From: David Wood Date: Sat, 7 May 2022 08:14:48 +0100 Subject: [PATCH 7/7] typeck: port "no resolve overridden impl substs" Port "could not resolve substs on overridden impl" diagnostic to use the diagnostic derive. Signed-off-by: David Wood --- compiler/rustc_error_messages/locales/en-US/typeck.ftl | 2 ++ compiler/rustc_typeck/src/errors.rs | 7 +++++++ .../rustc_typeck/src/impl_wf_check/min_specialization.rs | 3 ++- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_error_messages/locales/en-US/typeck.ftl b/compiler/rustc_error_messages/locales/en-US/typeck.ftl index 4122464a4a2..95b348ec613 100644 --- a/compiler/rustc_error_messages/locales/en-US/typeck.ftl +++ b/compiler/rustc_error_messages/locales/en-US/typeck.ftl @@ -127,3 +127,5 @@ 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 diff --git a/compiler/rustc_typeck/src/errors.rs b/compiler/rustc_typeck/src/errors.rs index 1cb112e68ce..cd3813ca4f5 100644 --- a/compiler/rustc_typeck/src/errors.rs +++ b/compiler/rustc_typeck/src/errors.rs @@ -323,3 +323,10 @@ pub struct ManualImplementation { pub span: Span, pub trait_name: String, } + +#[derive(SessionDiagnostic)] +#[error(slug = "typeck-substs-on-overridden-impl")] +pub struct SubstsOnOverriddenImpl { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs b/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs index 22db15f4d4e..bb97d00be32 100644 --- a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs @@ -66,6 +66,7 @@ //! on traits with methods can. use crate::constrained_generic_params as cgp; +use crate::errors::SubstsOnOverriddenImpl; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -165,7 +166,7 @@ fn get_impl_substs<'tcx>( let outlives_env = OutlivesEnvironment::new(ty::ParamEnv::empty()); infcx.resolve_regions_and_report_errors(impl1_def_id, &outlives_env, RegionckMode::default()); let Ok(impl2_substs) = infcx.fully_resolve(impl2_substs) else { - tcx.sess.struct_span_err(span, "could not resolve substs on overridden impl").emit(); + tcx.sess.emit_err(SubstsOnOverriddenImpl { span }); return None; }; Some((impl1_substs, impl2_substs))