From 06f480661f66e35869521ccd60278107233f9670 Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 11 Jul 2022 16:28:38 +0100 Subject: [PATCH 1/5] errors: impl `IntoDiagnosticArg` for `char` Implements `IntoDiagnosticArg` for `char` using its `Debug` implementation and introduces a macro for those types which just delegate the implementation to `ToString`. Signed-off-by: David Wood --- compiler/rustc_errors/src/diagnostic.rs | 110 +++++++----------------- 1 file changed, 33 insertions(+), 77 deletions(-) diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index da321c4587509..0ce7f3c7e8270 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -40,6 +40,34 @@ pub trait IntoDiagnosticArg { fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static>; } +macro_rules! into_diagnostic_arg_using_display { + ($( $ty:ty ),+ $(,)?) => { + $( + impl IntoDiagnosticArg for $ty { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + self.to_string().into_diagnostic_arg() + } + } + )+ + } +} + +into_diagnostic_arg_using_display!( + i8, + u8, + i16, + u16, + i32, + u32, + i64, + u64, + i128, + u128, + std::num::NonZeroU32, + Edition, + Ident, +); + impl IntoDiagnosticArg for bool { fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { if self { @@ -50,81 +78,9 @@ impl IntoDiagnosticArg for bool { } } -impl IntoDiagnosticArg for i8 { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(self.to_string())) - } -} - -impl IntoDiagnosticArg for u8 { +impl IntoDiagnosticArg for char { fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(self.to_string())) - } -} - -impl IntoDiagnosticArg for i16 { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(self.to_string())) - } -} - -impl IntoDiagnosticArg for u16 { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(self.to_string())) - } -} - -impl IntoDiagnosticArg for i32 { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(self.to_string())) - } -} - -impl IntoDiagnosticArg for u32 { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(self.to_string())) - } -} - -impl IntoDiagnosticArg for i64 { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(self.to_string())) - } -} - -impl IntoDiagnosticArg for u64 { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(self.to_string())) - } -} - -impl IntoDiagnosticArg for i128 { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(self.to_string())) - } -} - -impl IntoDiagnosticArg for u128 { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(self.to_string())) - } -} - -impl IntoDiagnosticArg for String { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(self)) - } -} - -impl IntoDiagnosticArg for std::num::NonZeroU32 { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(self.to_string())) - } -} - -impl IntoDiagnosticArg for Edition { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(self.to_string())) + DiagnosticArgValue::Str(Cow::Owned(format!("{:?}", self))) } } @@ -134,15 +90,15 @@ impl IntoDiagnosticArg for Symbol { } } -impl IntoDiagnosticArg for Ident { +impl<'a> IntoDiagnosticArg for &'a str { fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { self.to_string().into_diagnostic_arg() } } -impl<'a> IntoDiagnosticArg for &'a str { +impl IntoDiagnosticArg for String { fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - self.to_string().into_diagnostic_arg() + DiagnosticArgValue::Str(Cow::Owned(self)) } } From c3fdf748856fa220dc251647808db8535ac0bba2 Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 11 Jul 2022 16:29:24 +0100 Subject: [PATCH 2/5] errors: lint on `LintDiagnosticBuilder::build` Apply the `#[rustc_lint_diagnostics]` attribute to `LintDiagnosticBuilder::build` so that diagnostic migration lints will trigger for it. Signed-off-by: David Wood --- .../locales/en-US/privacy.ftl | 9 +++++ .../rustc_errors/src/diagnostic_builder.rs | 1 + compiler/rustc_privacy/src/errors.rs | 18 +++++++++- compiler/rustc_privacy/src/lib.rs | 35 +++++++------------ 4 files changed, 39 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_error_messages/locales/en-US/privacy.ftl b/compiler/rustc_error_messages/locales/en-US/privacy.ftl index 2b0778f48caee..f8a750da93f8d 100644 --- a/compiler/rustc_error_messages/locales/en-US/privacy.ftl +++ b/compiler/rustc_error_messages/locales/en-US/privacy.ftl @@ -10,3 +10,12 @@ privacy-unnamed-item-is-private = {$kind} is private privacy-in-public-interface = {$vis_descr} {$kind} `{$descr}` in public interface .label = can't leak {$vis_descr} {$kind} .visibility-label = `{$descr}` declared as {$vis_descr} + +privacy-from-private-dep-in-public-interface = + {$kind} `{$descr}` from private dependency '{$krate}' in public interface + +private-in-public-lint = + {$vis_descr} {$kind} `{$descr}` in public interface (error {$kind -> + [trait] E0445 + *[other] E0446 + }) diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs index 99ac6a3546ed6..9e68ee282e652 100644 --- a/compiler/rustc_errors/src/diagnostic_builder.rs +++ b/compiler/rustc_errors/src/diagnostic_builder.rs @@ -595,6 +595,7 @@ macro_rules! error_code { pub struct LintDiagnosticBuilder<'a, G: EmissionGuarantee>(DiagnosticBuilder<'a, G>); impl<'a, G: EmissionGuarantee> LintDiagnosticBuilder<'a, G> { + #[rustc_lint_diagnostics] /// Return the inner `DiagnosticBuilder`, first setting the primary message to `msg`. pub fn build(mut self, msg: impl Into) -> DiagnosticBuilder<'a, G> { self.0.set_primary_message(msg); diff --git a/compiler/rustc_privacy/src/errors.rs b/compiler/rustc_privacy/src/errors.rs index 482721d373ab7..b0fac91f6ebc3 100644 --- a/compiler/rustc_privacy/src/errors.rs +++ b/compiler/rustc_privacy/src/errors.rs @@ -1,4 +1,4 @@ -use rustc_macros::{SessionDiagnostic, SessionSubdiagnostic}; +use rustc_macros::{LintDiagnostic, SessionDiagnostic, SessionSubdiagnostic}; use rustc_span::{Span, Symbol}; #[derive(SessionDiagnostic)] @@ -73,3 +73,19 @@ pub struct InPublicInterface<'a> { #[label(privacy::visibility_label)] pub vis_span: Span, } + +#[derive(LintDiagnostic)] +#[lint(privacy::from_private_dep_in_public_interface)] +pub struct FromPrivateDependencyInPublicInterface<'a> { + pub kind: &'a str, + pub descr: String, + pub krate: Symbol, +} + +#[derive(LintDiagnostic)] +#[lint(privacy::private_in_public_lint)] +pub struct PrivateInPublicLint<'a> { + pub vis_descr: &'static str, + pub kind: &'a str, + pub descr: String, +} diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 5b21c04664774..9a835808d4935 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -38,8 +38,8 @@ use std::ops::ControlFlow; use std::{cmp, fmt, mem}; use errors::{ - FieldIsPrivate, FieldIsPrivateLabel, InPublicInterface, InPublicInterfaceTraits, ItemIsPrivate, - UnnamedItemIsPrivate, + FieldIsPrivate, FieldIsPrivateLabel, FromPrivateDependencyInPublicInterface, InPublicInterface, + InPublicInterfaceTraits, ItemIsPrivate, PrivateInPublicLint, UnnamedItemIsPrivate, }; //////////////////////////////////////////////////////////////////////////////// @@ -1716,19 +1716,14 @@ impl SearchInterfaceForPrivateItemsVisitor<'_> { fn check_def_id(&mut self, def_id: DefId, kind: &str, descr: &dyn fmt::Display) -> bool { if self.leaks_private_dep(def_id) { - self.tcx.struct_span_lint_hir( + self.tcx.emit_spanned_lint( lint::builtin::EXPORTED_PRIVATE_DEPENDENCIES, self.tcx.hir().local_def_id_to_hir_id(self.item_def_id), self.tcx.def_span(self.item_def_id.to_def_id()), - |lint| { - lint.build(&format!( - "{} `{}` from private dependency '{}' in public \ - interface", - kind, - descr, - self.tcx.crate_name(def_id.krate) - )) - .emit(); + FromPrivateDependencyInPublicInterface { + kind, + descr: descr.to_string(), + krate: self.tcx.crate_name(def_id.krate), }, ); } @@ -1754,12 +1749,14 @@ impl SearchInterfaceForPrivateItemsVisitor<'_> { } }; let span = self.tcx.def_span(self.item_def_id.to_def_id()); + let descr = descr.to_string(); if self.has_old_errors || self.in_assoc_ty || self.tcx.resolutions(()).has_pub_restricted { let descr = descr.to_string(); - let vis_span = self.tcx.def_span(def_id); + let vis_span = + self.tcx.sess.source_map().guess_head_span(self.tcx.def_span(def_id)); if kind == "trait" { self.tcx.sess.emit_err(InPublicInterfaceTraits { span, @@ -1778,19 +1775,11 @@ impl SearchInterfaceForPrivateItemsVisitor<'_> { }); } } else { - let err_code = if kind == "trait" { "E0445" } else { "E0446" }; - self.tcx.struct_span_lint_hir( + self.tcx.emit_spanned_lint( lint::builtin::PRIVATE_IN_PUBLIC, hir_id, span, - |lint| { - lint.build(&format!( - "{} (error {})", - format!("{} {} `{}` in public interface", vis_descr, kind, descr), - err_code - )) - .emit(); - }, + PrivateInPublicLint { vis_descr, kind, descr }, ); } } From 88c11c5bfffea445c6cc49b62da17f172eb8f055 Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 11 Jul 2022 17:15:31 +0100 Subject: [PATCH 3/5] macros: support `MultiSpan` in diag derives Add support for `MultiSpan` with any of the attributes that work on a `Span` - requires that diagnostic logic generated for these attributes are emitted in the by-move block rather than the by-ref block that they would normally have been generated in. Signed-off-by: David Wood --- .../src/diagnostics/diagnostic_builder.rs | 139 ++++++++++-------- .../rustc_macros/src/diagnostics/utils.rs | 8 +- .../session-diagnostic/diagnostic-derive.rs | 15 +- .../diagnostic-derive.stderr | 6 +- .../subdiagnostic-derive.rs | 2 +- .../subdiagnostic-derive.stderr | 2 +- 6 files changed, 104 insertions(+), 68 deletions(-) diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs index 74ce1ab08c264..db3ba20d8f9d8 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -13,7 +13,8 @@ use quote::{format_ident, quote}; use std::collections::HashMap; use std::str::FromStr; use syn::{ - parse_quote, spanned::Spanned, Attribute, Meta, MetaList, MetaNameValue, NestedMeta, Path, Type, + parse_quote, spanned::Spanned, Attribute, Field, Meta, MetaList, MetaNameValue, NestedMeta, + Path, Type, }; use synstructure::{BindingInfo, Structure}; @@ -80,8 +81,8 @@ impl DiagnosticDeriveBuilder { } pub fn body<'s>(&mut self, structure: &mut Structure<'s>) -> (TokenStream, TokenStream) { - // Keep track of which fields are subdiagnostics or have no attributes. - let mut subdiagnostics_or_empty = std::collections::HashSet::new(); + // Keep track of which fields need to be handled with a by-move binding. + let mut needs_moved = std::collections::HashSet::new(); // Generates calls to `span_label` and similar functions based on the attributes // on fields. Code for suggestions uses formatting machinery and the value of @@ -92,16 +93,11 @@ impl DiagnosticDeriveBuilder { let attrs = structure .clone() .filter(|field_binding| { - let attrs = &field_binding.ast().attrs; - - (!attrs.is_empty() - && attrs.iter().all(|attr| { - "subdiagnostic" != attr.path.segments.last().unwrap().ident.to_string() - })) - || { - subdiagnostics_or_empty.insert(field_binding.binding.clone()); - false - } + let ast = &field_binding.ast(); + !self.needs_move(ast) || { + needs_moved.insert(field_binding.binding.clone()); + false + } }) .each(|field_binding| self.generate_field_attrs_code(field_binding)); @@ -111,12 +107,41 @@ impl DiagnosticDeriveBuilder { // attributes or a `#[subdiagnostic]` attribute then it must be passed as an // argument to the diagnostic so that it can be referred to by Fluent messages. let args = structure - .filter(|field_binding| subdiagnostics_or_empty.contains(&field_binding.binding)) + .filter(|field_binding| needs_moved.contains(&field_binding.binding)) .each(|field_binding| self.generate_field_attrs_code(field_binding)); (attrs, args) } + /// Returns `true` if `field` should generate a `set_arg` call rather than any other diagnostic + /// call (like `span_label`). + fn should_generate_set_arg(&self, field: &Field) -> bool { + field.attrs.is_empty() + } + + /// Returns `true` if `field` needs to have code generated in the by-move branch of the + /// generated derive rather than the by-ref branch. + fn needs_move(&self, field: &Field) -> bool { + let generates_set_arg = self.should_generate_set_arg(field); + let is_multispan = type_matches_path(&field.ty, &["rustc_errors", "MultiSpan"]); + // FIXME(davidtwco): better support for one field needing to be in the by-move and + // by-ref branches. + let is_subdiagnostic = field + .attrs + .iter() + .map(|attr| attr.path.segments.last().unwrap().ident.to_string()) + .any(|attr| attr == "subdiagnostic"); + + // `set_arg` calls take their argument by-move.. + generates_set_arg + // If this is a `MultiSpan` field then it needs to be moved to be used by any + // attribute.. + || is_multispan + // If this a `#[subdiagnostic]` then it needs to be moved as the other diagnostic is + // unlikely to be `Copy`.. + || is_subdiagnostic + } + /// Establishes state in the `DiagnosticDeriveBuilder` resulting from the struct /// attributes like `#[error(..)`, such as the diagnostic kind and slug. Generates /// diagnostic builder calls for setting error code and creating note/help messages. @@ -227,57 +252,55 @@ impl DiagnosticDeriveBuilder { let field = binding_info.ast(); let field_binding = &binding_info.binding; - let inner_ty = FieldInnerTy::from_type(&field.ty); - - // When generating `set_arg` or `add_subdiagnostic` calls, move data rather than - // borrow it to avoid requiring clones - this must therefore be the last use of - // each field (for example, any formatting machinery that might refer to a field - // should be generated already). - if field.attrs.is_empty() { + if self.should_generate_set_arg(&field) { let diag = &self.diag; let ident = field.ident.as_ref().unwrap(); - quote! { + return quote! { #diag.set_arg( stringify!(#ident), #field_binding ); - } - } else { - field - .attrs - .iter() - .map(move |attr| { - let name = attr.path.segments.last().unwrap().ident.to_string(); - let (binding, needs_destructure) = match (name.as_str(), &inner_ty) { - // `primary_span` can accept a `Vec` so don't destructure that. - ("primary_span", FieldInnerTy::Vec(_)) => { - (quote! { #field_binding.clone() }, false) - } - // `subdiagnostics` are not derefed because they are bound by value. - ("subdiagnostic", _) => (quote! { #field_binding }, true), - _ => (quote! { *#field_binding }, true), - }; - - let generated_code = self - .generate_inner_field_code( - attr, - FieldInfo { - binding: binding_info, - ty: inner_ty.inner_type().unwrap_or(&field.ty), - span: &field.span(), - }, - binding, - ) - .unwrap_or_else(|v| v.to_compile_error()); - - if needs_destructure { - inner_ty.with(field_binding, generated_code) - } else { - generated_code - } - }) - .collect() + }; } + + let needs_move = self.needs_move(&field); + let inner_ty = FieldInnerTy::from_type(&field.ty); + + field + .attrs + .iter() + .map(move |attr| { + let name = attr.path.segments.last().unwrap().ident.to_string(); + let needs_clone = + name == "primary_span" && matches!(inner_ty, FieldInnerTy::Vec(_)); + let (binding, needs_destructure) = if needs_clone { + // `primary_span` can accept a `Vec` so don't destructure that. + (quote! { #field_binding.clone() }, false) + } else if needs_move { + (quote! { #field_binding }, true) + } else { + (quote! { *#field_binding }, true) + }; + + let generated_code = self + .generate_inner_field_code( + attr, + FieldInfo { + binding: binding_info, + ty: inner_ty.inner_type().unwrap_or(&field.ty), + span: &field.span(), + }, + binding, + ) + .unwrap_or_else(|v| v.to_compile_error()); + + if needs_destructure { + inner_ty.with(field_binding, generated_code) + } else { + generated_code + } + }) + .collect() } fn generate_inner_field_code( diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs index 8977db4606c56..002abb152f759 100644 --- a/compiler/rustc_macros/src/diagnostics/utils.rs +++ b/compiler/rustc_macros/src/diagnostics/utils.rs @@ -85,7 +85,13 @@ pub(crate) fn report_error_if_not_applied_to_span( attr: &Attribute, info: &FieldInfo<'_>, ) -> Result<(), DiagnosticDeriveError> { - report_error_if_not_applied_to_ty(attr, info, &["rustc_span", "Span"], "`Span`") + if !type_matches_path(&info.ty, &["rustc_span", "Span"]) + && !type_matches_path(&info.ty, &["rustc_errors", "MultiSpan"]) + { + report_type_error(attr, "`Span` or `MultiSpan`")?; + } + + Ok(()) } /// Inner type of a field and type of wrapper. diff --git a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs index 56e95d70fd53d..b343bcb772142 100644 --- a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs +++ b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs @@ -23,7 +23,7 @@ extern crate rustc_middle; use rustc_middle::ty::Ty; extern crate rustc_errors; -use rustc_errors::Applicability; +use rustc_errors::{Applicability, MultiSpan}; extern crate rustc_session; @@ -140,7 +140,7 @@ struct CodeNotProvided {} #[error(typeck::ambiguous_lifetime_bound, code = "E0123")] struct MessageWrongType { #[primary_span] - //~^ ERROR `#[primary_span]` attribute can only be applied to fields of type `Span` + //~^ ERROR `#[primary_span]` attribute can only be applied to fields of type `Span` or `MultiSpan` foo: String, } @@ -165,7 +165,7 @@ struct ErrorWithField { #[error(typeck::ambiguous_lifetime_bound, code = "E0123")] struct ErrorWithMessageAppliedToField { #[label(typeck::label)] - //~^ ERROR the `#[label(...)]` attribute can only be applied to fields of type `Span` + //~^ ERROR the `#[label(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` name: String, } @@ -208,7 +208,7 @@ struct LabelOnSpan { #[error(typeck::ambiguous_lifetime_bound, code = "E0123")] struct LabelOnNonSpan { #[label(typeck::label)] - //~^ ERROR the `#[label(...)]` attribute can only be applied to fields of type `Span` + //~^ ERROR the `#[label(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` id: u32, } @@ -552,3 +552,10 @@ struct LintsGood { //~^ ERROR only `#[lint(..)]` is supported struct ErrorsBad { } + +#[derive(SessionDiagnostic)] +#[error(typeck::ambiguous_lifetime_bound, code = "E0123")] +struct ErrorWithMultiSpan { + #[primary_span] + span: MultiSpan, +} diff --git a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr index e282884289db3..e2580c6485a95 100644 --- a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr +++ b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr @@ -233,7 +233,7 @@ LL | | struct SlugNotProvided {} | = help: specify the slug as the first argument to the attribute, such as `#[error(typeck::example_error)]` -error: the `#[primary_span]` attribute can only be applied to fields of type `Span` +error: the `#[primary_span]` attribute can only be applied to fields of type `Span` or `MultiSpan` --> $DIR/diagnostic-derive.rs:142:5 | LL | #[primary_span] @@ -247,7 +247,7 @@ LL | #[nonsense] | = help: only `skip_arg`, `primary_span`, `label`, `note`, `help` and `subdiagnostic` are valid field attributes -error: the `#[label(...)]` attribute can only be applied to fields of type `Span` +error: the `#[label(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` --> $DIR/diagnostic-derive.rs:167:5 | LL | #[label(typeck::label)] @@ -279,7 +279,7 @@ LL | #[derive(SessionDiagnostic)] = note: if you intended to print `}`, you can escape it using `}}` = note: this error originates in the derive macro `SessionDiagnostic` (in Nightly builds, run with -Z macro-backtrace for more info) -error: the `#[label(...)]` attribute can only be applied to fields of type `Span` +error: the `#[label(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` --> $DIR/diagnostic-derive.rs:210:5 | LL | #[label(typeck::label)] diff --git a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs index 6f4b6105b3e49..cbb66c13c680c 100644 --- a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs +++ b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs @@ -244,7 +244,7 @@ enum V { //~^ ERROR label without `#[primary_span]` field struct W { #[primary_span] - //~^ ERROR the `#[primary_span]` attribute can only be applied to fields of type `Span` + //~^ ERROR the `#[primary_span]` attribute can only be applied to fields of type `Span` or `MultiSpan` span: String, } diff --git a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr index f833bd210f7f5..a289c4fffd936 100644 --- a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr +++ b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr @@ -120,7 +120,7 @@ error: subdiagnostic kind not specified LL | B { | ^ -error: the `#[primary_span]` attribute can only be applied to fields of type `Span` +error: the `#[primary_span]` attribute can only be applied to fields of type `Span` or `MultiSpan` --> $DIR/subdiagnostic-derive.rs:246:5 | LL | #[primary_span] From 81cf2294b4d912ec410696c5e2dec7659243d191 Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 11 Jul 2022 18:46:24 +0100 Subject: [PATCH 4/5] macros: support adding warnings to diags Both diagnostic and subdiagnostic derives were missing the ability to add warnings to diagnostics - this is made more difficult by the `warn` attribute already existing, so this name being unavailable for the derives to use. `#[warn_]` is used instead, which requires special-casing so that `{span_,}warn` is called instead of `{span_,}warn_`. Signed-off-by: David Wood --- .../src/diagnostics/diagnostic.rs | 2 +- .../src/diagnostics/diagnostic_builder.rs | 51 ++++++++++++------- .../rustc_macros/src/diagnostics/fluent.rs | 6 ++- .../src/diagnostics/subdiagnostic.rs | 4 ++ compiler/rustc_macros/src/lib.rs | 7 ++- .../session-diagnostic/diagnostic-derive.rs | 9 +++- .../diagnostic-derive.stderr | 4 +- .../subdiagnostic-derive.rs | 12 +++++ 8 files changed, 70 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs index 027f377b0acc7..6b5b8b5932018 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs @@ -59,7 +59,7 @@ impl<'a> SessionDiagnosticDerive<'a> { return DiagnosticDeriveError::ErrorHandled.to_compile_error(); } (Some(DiagnosticDeriveKind::Lint), _) => { - span_err(span, "only `#[error(..)]` and `#[warn(..)]` are supported") + span_err(span, "only `#[error(..)]` and `#[warning(..)]` are supported") .help("use the `#[error(...)]` attribute to create a error") .emit(); return DiagnosticDeriveError::ErrorHandled.to_compile_error(); diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs index db3ba20d8f9d8..5c5275b7cfb92 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -8,7 +8,7 @@ use crate::diagnostics::utils::{ report_error_if_not_applied_to_span, report_type_error, type_is_unit, type_matches_path, Applicability, FieldInfo, FieldInnerTy, HasFieldMap, SetOnce, }; -use proc_macro2::{Ident, TokenStream}; +use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote}; use std::collections::HashMap; use std::str::FromStr; @@ -156,7 +156,7 @@ impl DiagnosticDeriveBuilder { let name = name.as_str(); let meta = attr.parse_meta()?; - let is_help_or_note = matches!(name, "help" | "note"); + let is_help_note_or_warn = matches!(name, "help" | "note" | "warn_"); let nested = match meta { // Most attributes are lists, like `#[error(..)]`/`#[warning(..)]` for most cases or @@ -164,8 +164,12 @@ impl DiagnosticDeriveBuilder { Meta::List(MetaList { ref nested, .. }) => nested, // Subdiagnostics without spans can be applied to the type too, and these are just // paths: `#[help]` and `#[note]` - Meta::Path(_) if is_help_or_note => { - let fn_name = proc_macro2::Ident::new(name, attr.span()); + Meta::Path(_) if is_help_note_or_warn => { + let fn_name = if name == "warn_" { + Ident::new("warn", attr.span()) + } else { + Ident::new(name, attr.span()) + }; return Ok(quote! { #diag.#fn_name(rustc_errors::fluent::_subdiag::#fn_name); }); } _ => throw_invalid_attr!(attr, &meta), @@ -177,9 +181,11 @@ impl DiagnosticDeriveBuilder { "error" => self.kind.set_once((DiagnosticDeriveKind::Error, span)), "warning" => self.kind.set_once((DiagnosticDeriveKind::Warn, span)), "lint" => self.kind.set_once((DiagnosticDeriveKind::Lint, span)), - "help" | "note" => (), + "help" | "note" | "warn_" => (), _ => throw_invalid_attr!(attr, &meta, |diag| { - diag.help("only `error`, `warning`, `help` and `note` are valid attributes") + diag.help( + "only `error`, `warning`, `help`, `note` and `warn_` are valid attributes", + ) }), } @@ -188,14 +194,16 @@ impl DiagnosticDeriveBuilder { let mut nested_iter = nested.into_iter(); if let Some(nested_attr) = nested_iter.next() { // Report an error if there are any other list items after the path. - if is_help_or_note && nested_iter.next().is_some() { + if is_help_note_or_warn && nested_iter.next().is_some() { throw_invalid_nested_attr!(attr, &nested_attr, |diag| { - diag.help("`help` and `note` struct attributes can only have one argument") + diag.help( + "`help`, `note` and `warn_` struct attributes can only have one argument", + ) }); } match nested_attr { - NestedMeta::Meta(Meta::Path(path)) if is_help_or_note => { + NestedMeta::Meta(Meta::Path(path)) if is_help_note_or_warn => { let fn_name = proc_macro2::Ident::new(name, attr.span()); return Ok(quote! { #diag.#fn_name(rustc_errors::fluent::#path); }); } @@ -203,7 +211,7 @@ impl DiagnosticDeriveBuilder { self.slug.set_once((path.clone(), span)); } NestedMeta::Meta(meta @ Meta::NameValue(_)) - if !is_help_or_note + if !is_help_note_or_warn && meta.path().segments.last().unwrap().ident.to_string() == "code" => { // don't error for valid follow-up attributes @@ -347,10 +355,12 @@ impl DiagnosticDeriveBuilder { report_error_if_not_applied_to_span(attr, &info)?; Ok(self.add_spanned_subdiagnostic(binding, ident, parse_quote! { _subdiag::label })) } - "note" | "help" => { - let path = match name { - "note" => parse_quote! { _subdiag::note }, - "help" => parse_quote! { _subdiag::help }, + "note" | "help" | "warn_" => { + let warn_ident = Ident::new("warn", Span::call_site()); + let (ident, path) = match name { + "note" => (ident, parse_quote! { _subdiag::note }), + "help" => (ident, parse_quote! { _subdiag::help }), + "warn_" => (&warn_ident, parse_quote! { _subdiag::warn }), _ => unreachable!(), }; if type_matches_path(&info.ty, &["rustc_span", "Span"]) { @@ -387,10 +397,10 @@ impl DiagnosticDeriveBuilder { "suggestion" | "suggestion_short" | "suggestion_hidden" | "suggestion_verbose" => { return self.generate_inner_field_code_suggestion(attr, info); } - "label" | "help" | "note" => (), + "label" | "help" | "note" | "warn_" => (), _ => throw_invalid_attr!(attr, &meta, |diag| { diag.help( - "only `label`, `note`, `help` or `suggestion{,_short,_hidden,_verbose}` are \ + "only `label`, `help`, `note`, `warn` or `suggestion{,_short,_hidden,_verbose}` are \ valid field attributes", ) }), @@ -419,7 +429,14 @@ impl DiagnosticDeriveBuilder { Ok(self.add_spanned_subdiagnostic(binding, ident, msg)) } "note" | "help" if type_is_unit(&info.ty) => Ok(self.add_subdiagnostic(ident, msg)), - "note" | "help" => report_type_error(attr, "`Span` or `()`")?, + // `warn_` must be special-cased because the attribute `warn` already has meaning and + // so isn't used, despite the diagnostic API being named `warn`. + "warn_" if type_matches_path(&info.ty, &["rustc_span", "Span"]) => Ok(self + .add_spanned_subdiagnostic(binding, &Ident::new("warn", Span::call_site()), msg)), + "warn_" if type_is_unit(&info.ty) => { + Ok(self.add_subdiagnostic(&Ident::new("warn", Span::call_site()), msg)) + } + "note" | "help" | "warn_" => report_type_error(attr, "`Span` or `()`")?, _ => unreachable!(), } } diff --git a/compiler/rustc_macros/src/diagnostics/fluent.rs b/compiler/rustc_macros/src/diagnostics/fluent.rs index 2758fcd1310fe..1170d2b3c59a4 100644 --- a/compiler/rustc_macros/src/diagnostics/fluent.rs +++ b/compiler/rustc_macros/src/diagnostics/fluent.rs @@ -260,10 +260,12 @@ pub(crate) fn fluent_messages(input: proc_macro::TokenStream) -> proc_macro::Tok #generated pub mod _subdiag { - pub const note: crate::SubdiagnosticMessage = - crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("note")); pub const help: crate::SubdiagnosticMessage = crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("help")); + pub const note: crate::SubdiagnosticMessage = + crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("note")); + pub const warn: crate::SubdiagnosticMessage = + crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("warn")); pub const label: crate::SubdiagnosticMessage = crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("label")); pub const suggestion: crate::SubdiagnosticMessage = diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index 2a5b6beba94bb..edf4dbed9853e 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -37,6 +37,8 @@ enum SubdiagnosticKind { Note, /// `#[help(...)]` Help, + /// `#[warn_(...)]` + Warn, /// `#[suggestion{,_short,_hidden,_verbose}]` Suggestion(SubdiagnosticSuggestionKind), } @@ -49,6 +51,7 @@ impl FromStr for SubdiagnosticKind { "label" => Ok(SubdiagnosticKind::Label), "note" => Ok(SubdiagnosticKind::Note), "help" => Ok(SubdiagnosticKind::Help), + "warn_" => Ok(SubdiagnosticKind::Warn), "suggestion" => Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal)), "suggestion_short" => { Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Short)) @@ -70,6 +73,7 @@ impl quote::IdentFragment for SubdiagnosticKind { SubdiagnosticKind::Label => write!(f, "label"), SubdiagnosticKind::Note => write!(f, "note"), SubdiagnosticKind::Help => write!(f, "help"), + SubdiagnosticKind::Warn => write!(f, "warn"), SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal) => { write!(f, "suggestion") } diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs index 168530c54b99e..ab509b26f1c55 100644 --- a/compiler/rustc_macros/src/lib.rs +++ b/compiler/rustc_macros/src/lib.rs @@ -130,8 +130,9 @@ decl_derive!( warning, error, lint, - note, help, + note, + warn_, // field attributes skip_arg, primary_span, @@ -148,8 +149,9 @@ decl_derive!( warning, error, lint, - note, help, + note, + warn_, // field attributes skip_arg, primary_span, @@ -166,6 +168,7 @@ decl_derive!( label, help, note, + warn_, suggestion, suggestion_short, suggestion_hidden, diff --git a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs index b343bcb772142..0a210cbdc9430 100644 --- a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs +++ b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs @@ -538,7 +538,7 @@ struct LabelWithTrailingList { #[derive(SessionDiagnostic)] #[lint(typeck::ambiguous_lifetime_bound)] -//~^ ERROR only `#[error(..)]` and `#[warn(..)]` are supported +//~^ ERROR only `#[error(..)]` and `#[warning(..)]` are supported struct LintsBad { } @@ -559,3 +559,10 @@ struct ErrorWithMultiSpan { #[primary_span] span: MultiSpan, } + +#[derive(SessionDiagnostic)] +#[error(typeck::ambiguous_lifetime_bound, code = "E0123")] +#[warn_] +struct ErrorWithWarn { + val: String, +} diff --git a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr index e2580c6485a95..c1080aa24521f 100644 --- a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr +++ b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr @@ -21,7 +21,7 @@ error: `#[nonsense(...)]` is not a valid attribute LL | #[nonsense(typeck::ambiguous_lifetime_bound, code = "E0123")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: only `error`, `warning`, `help` and `note` are valid attributes + = help: only `error`, `warning`, `help`, `note` and `warn_` are valid attributes error: diagnostic kind not specified --> $DIR/diagnostic-derive.rs:53:1 @@ -363,7 +363,7 @@ error: `#[label(...)]` is not a valid attribute LL | #[label(typeck::label, foo("..."))] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: only `#[error(..)]` and `#[warn(..)]` are supported +error: only `#[error(..)]` and `#[warning(..)]` are supported --> $DIR/diagnostic-derive.rs:540:1 | LL | / #[lint(typeck::ambiguous_lifetime_bound)] diff --git a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs index cbb66c13c680c..16da25c402b57 100644 --- a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs +++ b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs @@ -508,3 +508,15 @@ enum AX { span: Span, } } + +#[derive(SessionSubdiagnostic)] +#[warn_(parser::add_paren)] +struct AY { +} + +#[derive(SessionSubdiagnostic)] +#[warn_(parser::add_paren)] +struct AZ { + #[primary_span] + span: Span, +} From 78b19a90b7d728f3bde6a70a2509ae177561ce5f Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 11 Jul 2022 18:59:04 +0100 Subject: [PATCH 5/5] passes: migrate half of `check_attr` Migrate half of the `rustc_passes::check_attr` diagnostics to using diagnostic derives and being translatable. --- Cargo.lock | 1 + .../locales/en-US/passes.ftl | 151 ++++ compiler/rustc_error_messages/src/lib.rs | 1 + compiler/rustc_errors/src/diagnostic.rs | 1 + compiler/rustc_passes/Cargo.toml | 1 + compiler/rustc_passes/src/check_attr.rs | 690 +++++++----------- compiler/rustc_passes/src/errors.rs | 362 +++++++++ compiler/rustc_passes/src/lib.rs | 3 +- src/test/rustdoc-ui/invalid-doc-attr.stderr | 10 +- .../ui/attributes/invalid-doc-attr.stderr | 10 +- .../ui/attributes/multiple-invalid.stderr | 4 +- ...issue-43106-gating-of-builtin-attrs.stderr | 22 +- .../ui/future-incompatible-lint-group.stderr | 2 +- src/test/ui/issues/issue-54044.stderr | 8 +- src/test/ui/issues/issue-78957.stderr | 12 +- src/test/ui/macros/issue-68060.stderr | 4 +- .../marker-attribute-on-non-trait.rs | 12 +- .../marker-attribute-on-non-trait.stderr | 12 +- .../invalid-attribute.rs | 4 +- .../invalid-attribute.stderr | 4 +- .../ui/rfc-2091-track-caller/only-for-fns.rs | 2 +- .../rfc-2091-track-caller/only-for-fns.stderr | 4 +- src/test/ui/rustdoc/doc_keyword.rs | 2 +- src/test/ui/rustdoc/doc_keyword.stderr | 6 +- .../target-feature/invalid-attribute.stderr | 32 +- .../rustc_must_implement_one_of_misuse.rs | 4 +- .../rustc_must_implement_one_of_misuse.stderr | 4 +- 27 files changed, 844 insertions(+), 524 deletions(-) create mode 100644 compiler/rustc_error_messages/locales/en-US/passes.ftl create mode 100644 compiler/rustc_passes/src/errors.rs diff --git a/Cargo.lock b/Cargo.lock index 0b44201d56f0b..9a8d9d40b6e45 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4239,6 +4239,7 @@ dependencies = [ "rustc_hir", "rustc_index", "rustc_lexer", + "rustc_macros", "rustc_middle", "rustc_serialize", "rustc_session", diff --git a/compiler/rustc_error_messages/locales/en-US/passes.ftl b/compiler/rustc_error_messages/locales/en-US/passes.ftl new file mode 100644 index 0000000000000..e4c9a4dad7b48 --- /dev/null +++ b/compiler/rustc_error_messages/locales/en-US/passes.ftl @@ -0,0 +1,151 @@ +-passes-previously-accepted = + this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +-passes-see-issue = + see issue #{$issue} for more information + +passes-outer-crate-level-attr = + crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` + +passes-inner-crate-level-attr = + crate-level attribute should be in the root module + +passes-ignored-attr-with-macro = `#[{$sym}]` is ignored on struct fields, match arms and macro defs + .warn = {-passes-previously-accepted} + .note = {-passes-see-issue(issue: "80564")} + +passes-ignored-attr = `#[{$sym}]` is ignored on struct fields and match arms + .warn = {-passes-previously-accepted} + .note = {-passes-see-issue(issue: "80564")} + +passes-inline-ignored-function-prototype = `#[inline]` is ignored on function prototypes + +passes-inline-ignored-constants = `#[inline]` is ignored on constants + .warn = {-passes-previously-accepted} + .note = {-passes-see-issue(issue: "65833")} + +passes-inline-not-fn-or-closure = attribute should be applied to function or closure + .label = not a function or closure + +passes-no-coverage-ignored-function-prototype = `#[no_coverage]` is ignored on function prototypes + +passes-no-coverage-propagate = + `#[no_coverage]` does not propagate into items and must be applied to the contained functions directly + +passes-no-coverage-fn-defn = `#[no_coverage]` may only be applied to function definitions + +passes-no-coverage-not-coverable = `#[no_coverage]` must be applied to coverable code + .label = not coverable code + +passes-should-be-applied-to-fn = attribute should be applied to a function definition + .label = not a function definition + +passes-naked-tracked-caller = cannot use `#[track_caller]` with `#[naked]` + +passes-should-be-applied-to-struct-enum = attribute should be applied to a struct or enum + .label = not a struct or enum + +passes-should-be-applied-to-trait = attribute should be applied to a trait + .label = not a trait + +passes-target-feature-on-statement = {passes-should-be-applied-to-fn} + .warn = {-passes-previously-accepted} + .label = {passes-should-be-applied-to-fn.label} + +passes-should-be-applied-to-static = attribute should be applied to a static + .label = not a static + +passes-doc-expect-str = doc {$attr_name} attribute expects a string: #[doc({$attr_name} = "a")] + +passes-doc-alias-empty = {$attr_str} attribute cannot have empty value + +passes-doc-alias-bad-char = {$char_} character isn't allowed in {$attr_str} + +passes-doc-alias-start-end = {$attr_str} cannot start or end with ' ' + +passes-doc-alias-bad-location = {$attr_str} isn't allowed on {$location} + +passes-doc-alias-not-an-alias = {$attr_str} is the same as the item's name + +passes-doc-alias-duplicated = doc alias is duplicated + .label = first defined here + +passes-doc-alias-not-string-literal = `#[doc(alias("a"))]` expects string literals + +passes-doc-alias-malformed = + doc alias attribute expects a string `#[doc(alias = "a")]` or a list of strings `#[doc(alias("a", "b"))]` + +passes-doc-keyword-empty-mod = `#[doc(keyword = "...")]` should be used on empty modules + +passes-doc-keyword-not-mod = `#[doc(keyword = "...")]` should be used on modules + +passes-doc-keyword-invalid-ident = `{$doc_keyword}` is not a valid identifier + +passes-doc-tuple-variadic-not-first = + `#[doc(tuple_variadic)]` must be used on the first of a set of tuple trait impls with varying arity + +passes-doc-keyword-only-impl = `#[doc(keyword = "...")]` should be used on impl blocks + +passes-doc-inline-conflict-first = this attribute... +passes-doc-inline-conflict-second = ...conflicts with this attribute +passes-doc-inline-conflict = conflicting doc inlining attributes + .help = remove one of the conflicting attributes + +passes-doc-inline-only-use = this attribute can only be applied to a `use` item + .label = only applicable on `use` items + .not-a-use-item-label = not a `use` item + .note = read for more information + +passes-doc-attr-not-crate-level = + `#![doc({$attr_name} = "...")]` isn't allowed as a crate-level attribute + +passes-attr-crate-level = this attribute can only be applied at the crate level + .suggestion = to apply to the crate, use an inner attribute + .help = to apply to the crate, use an inner attribute + .note = read for more information + +passes-doc-test-unknown = unknown `doc(test)` attribute `{$path}` + +passes-doc-test-takes-list = `#[doc(test(...)]` takes a list of attributes + +passes-doc-primitive = `doc(primitive)` should never have been stable + +passes-doc-test-unknown-any = unknown `doc` attribute `{$path}` + +passes-doc-test-unknown-spotlight = unknown `doc` attribute `{$path}` + .note = `doc(spotlight)` was renamed to `doc(notable_trait)` + .suggestion = use `notable_trait` instead + .no-op-note = `doc(spotlight)` is now a no-op + +passes-doc-test-unknown-include = unknown `doc` attribute `{$path}` + .suggestion = use `doc = include_str!` instead + +passes-doc-invalid = invalid `doc` attribute + +passes-pass-by-value = `pass_by_value` attribute should be applied to a struct, enum or type alias + .label = is not a struct, enum or type alias + +passes-allow-incoherent-impl = + `rustc_allow_incoherent_impl` attribute should be applied to impl items. + .label = the only currently supported targets are inherent methods + +passes-has-incoherent-inherent-impl = + `rustc_has_incoherent_inherent_impls` attribute should be applied to types or traits. + .label = only adts, extern types and traits are supported + +passes-must-use-async = + `must_use` attribute on `async` functions applies to the anonymous `Future` returned by the function, not the value within + .label = this attribute does nothing, the `Future`s returned by async functions are already `must_use` + +passes-must-use-no-effect = `#[must_use]` has no effect when applied to {$article} {$target} + +passes-must-not-suspend = `must_not_suspend` attribute should be applied to a struct, enum, or trait + .label = is not a struct, enum, or trait + +passes-cold = {passes-should-be-applied-to-fn} + .warn = {-passes-previously-accepted} + .label = {passes-should-be-applied-to-fn.label} + +passes-link = attribute should be applied to an `extern` block with non-Rust ABI + .warn = {-passes-previously-accepted} + .label = not an `extern` block diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index d16171cb162d6..a3040f83fdfc7 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -37,6 +37,7 @@ fluent_messages! { expand => "../locales/en-US/expand.ftl", lint => "../locales/en-US/lint.ftl", parser => "../locales/en-US/parser.ftl", + passes => "../locales/en-US/passes.ftl", privacy => "../locales/en-US/privacy.ftl", typeck => "../locales/en-US/typeck.ftl", } diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 0ce7f3c7e8270..267beb514847c 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -64,6 +64,7 @@ into_diagnostic_arg_using_display!( i128, u128, std::num::NonZeroU32, + hir::Target, Edition, Ident, ); diff --git a/compiler/rustc_passes/Cargo.toml b/compiler/rustc_passes/Cargo.toml index 676812db59ae5..faa9c493d8875 100644 --- a/compiler/rustc_passes/Cargo.toml +++ b/compiler/rustc_passes/Cargo.toml @@ -15,6 +15,7 @@ rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } rustc_session = { path = "../rustc_session" } rustc_target = { path = "../rustc_target" } +rustc_macros = { path = "../rustc_macros" } rustc_ast = { path = "../rustc_ast" } rustc_serialize = { path = "../rustc_serialize" } rustc_span = { path = "../rustc_span" } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index e626a1e4ed101..d96e7d3efe83d 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -4,9 +4,10 @@ //! conflicts between multiple such attributes attached to the same //! item. +use crate::errors; use rustc_ast::{ast, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem}; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{pluralize, struct_span_err, Applicability, MultiSpan}; +use rustc_errors::{fluent, pluralize, struct_span_err, Applicability, MultiSpan}; use rustc_expand::base::resolve_path; use rustc_feature::{AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP}; use rustc_hir as hir; @@ -175,16 +176,20 @@ impl CheckAttrVisitor<'_> { if let Some(BuiltinAttribute { type_: AttributeType::CrateLevel, .. }) = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)) { - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - let msg = match attr.style { - ast::AttrStyle::Outer => { - "crate-level attribute should be an inner attribute: add an exclamation \ - mark: `#![foo]`" - } - ast::AttrStyle::Inner => "crate-level attribute should be in the root module", - }; - lint.build(msg).emit(); - }); + match attr.style { + ast::AttrStyle::Outer => self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::OuterCrateLevelAttr, + ), + ast::AttrStyle::Inner => self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::InnerCrateLevelAttr, + ), + } } } @@ -209,37 +214,21 @@ impl CheckAttrVisitor<'_> { } fn inline_attr_str_error_with_macro_def(&self, hir_id: HirId, attr: &Attribute, sym: &str) { - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build(&format!( - "`#[{sym}]` is ignored on struct fields, match arms and macro defs", - )) - .warn( - "this was previously accepted by the compiler but is \ - being phased out; it will become a hard error in \ - a future release!", - ) - .note( - "see issue #80564 \ - for more information", - ) - .emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::IgnoredAttrWithMacro { sym }, + ); } fn inline_attr_str_error_without_macro_def(&self, hir_id: HirId, attr: &Attribute, sym: &str) { - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build(&format!("`#[{sym}]` is ignored on struct fields and match arms")) - .warn( - "this was previously accepted by the compiler but is \ - being phased out; it will become a hard error in \ - a future release!", - ) - .note( - "see issue #80564 \ - for more information", - ) - .emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::IgnoredAttr { sym }, + ); } /// Checks if an `#[inline]` is applied to a function or a closure. Returns `true` if valid. @@ -249,9 +238,12 @@ impl CheckAttrVisitor<'_> { | Target::Closure | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true, Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => { - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build("`#[inline]` is ignored on function prototypes").emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::IgnoredInlineAttrFnProto, + ); true } // FIXME(#65833): We permit associated consts to have an `#[inline]` attribute with @@ -259,19 +251,12 @@ impl CheckAttrVisitor<'_> { // accidentally, to to be compatible with crates depending on them, we can't throw an // error here. Target::AssocConst => { - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build("`#[inline]` is ignored on constants") - .warn( - "this was previously accepted by the compiler but is \ - being phased out; it will become a hard error in \ - a future release!", - ) - .note( - "see issue #65833 \ - for more information", - ) - .emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::IgnoredInlineAttrConstants, + ); true } // FIXME(#80564): Same for fields, arms, and macro defs @@ -280,14 +265,10 @@ impl CheckAttrVisitor<'_> { true } _ => { - struct_span_err!( - self.tcx.sess, - attr.span, - E0518, - "attribute should be applied to function or closure", - ) - .span_label(span, "not a function or closure") - .emit(); + self.tcx.sess.emit_err(errors::InlineNotFnOrClosure { + attr_span: attr.span, + defn_span: span, + }); false } } @@ -309,36 +290,40 @@ impl CheckAttrVisitor<'_> { // function prototypes can't be covered Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => { - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build("`#[no_coverage]` is ignored on function prototypes").emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::IgnoredNoCoverageFnProto, + ); true } Target::Mod | Target::ForeignMod | Target::Impl | Target::Trait => { - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build("`#[no_coverage]` does not propagate into items and must be applied to the contained functions directly").emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::IgnoredNoCoveragePropagate, + ); true } Target::Expression | Target::Statement | Target::Arm => { - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build("`#[no_coverage]` may only be applied to function definitions") - .emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::IgnoredNoCoverageFnDefn, + ); true } _ => { - struct_span_err!( - self.tcx.sess, - attr.span, - E0788, - "`#[no_coverage]` must be applied to coverable code", - ) - .span_label(span, "not coverable code") - .emit(); + self.tcx.sess.emit_err(errors::IgnoredNoCoverageNotCoverable { + attr_span: attr.span, + defn_span: span, + }); false } } @@ -389,14 +374,10 @@ impl CheckAttrVisitor<'_> { true } _ => { - self.tcx - .sess - .struct_span_err( - attr.span, - "attribute should be applied to a function definition", - ) - .span_label(span, "not a function definition") - .emit(); + self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToFn { + attr_span: attr.span, + defn_span: span, + }); false } } @@ -408,14 +389,10 @@ impl CheckAttrVisitor<'_> { Target::Fn | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true, _ => { - self.tcx - .sess - .struct_span_err( - attr.span, - "attribute should be applied to a function definition", - ) - .span_label(span, "not a function definition") - .emit(); + self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToFn { + attr_span: attr.span, + defn_span: span, + }); false } } @@ -432,13 +409,7 @@ impl CheckAttrVisitor<'_> { ) -> bool { match target { _ if attrs.iter().any(|attr| attr.has_name(sym::naked)) => { - struct_span_err!( - self.tcx.sess, - attr_span, - E0736, - "cannot use `#[track_caller]` with `#[naked]`", - ) - .emit(); + self.tcx.sess.emit_err(errors::NakedTrackedCaller { attr_span }); false } Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => true, @@ -453,14 +424,9 @@ impl CheckAttrVisitor<'_> { true } _ => { - struct_span_err!( - self.tcx.sess, - attr_span, - E0739, - "attribute should be applied to function" - ) - .span_label(span, "not a function") - .emit(); + self.tcx + .sess + .emit_err(errors::TrackedCallerWrongLocation { attr_span, defn_span: span }); false } } @@ -485,14 +451,10 @@ impl CheckAttrVisitor<'_> { true } _ => { - struct_span_err!( - self.tcx.sess, - attr.span, - E0701, - "attribute can only be applied to a struct or enum" - ) - .span_label(span, "not a struct or enum") - .emit(); + self.tcx.sess.emit_err(errors::NonExhaustiveWrongLocation { + attr_span: attr.span, + defn_span: span, + }); false } } @@ -511,11 +473,10 @@ impl CheckAttrVisitor<'_> { true } _ => { - self.tcx - .sess - .struct_span_err(attr.span, "attribute can only be applied to a trait") - .span_label(span, "not a trait") - .emit(); + self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToTrait { + attr_span: attr.span, + defn_span: span, + }); false } } @@ -531,11 +492,10 @@ impl CheckAttrVisitor<'_> { match target { Target::Trait => true, _ => { - self.tcx - .sess - .struct_span_err(attr.span, "attribute can only be applied to a trait") - .span_label(span, "not a trait") - .emit(); + self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToTrait { + attr_span: attr.span, + defn_span: span, + }); false } } @@ -555,16 +515,12 @@ impl CheckAttrVisitor<'_> { // FIXME: #[target_feature] was previously erroneously allowed on statements and some // crates used this, so only emit a warning. Target::Statement => { - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build("attribute should be applied to a function") - .warn( - "this was previously accepted by the compiler but is \ - being phased out; it will become a hard error in \ - a future release!", - ) - .span_label(span, "not a function") - .emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::TargetFeatureOnStatement, + ); true } // FIXME(#80564): We permit struct fields, match arms and macro defs to have an @@ -576,11 +532,10 @@ impl CheckAttrVisitor<'_> { true } _ => { - self.tcx - .sess - .struct_span_err(attr.span, "attribute should be applied to a function") - .span_label(span, "not a function") - .emit(); + self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToFn { + attr_span: attr.span, + defn_span: span, + }); false } } @@ -591,24 +546,17 @@ impl CheckAttrVisitor<'_> { match target { Target::ForeignStatic | Target::Static => true, _ => { - self.tcx - .sess - .struct_span_err(attr.span, "attribute should be applied to a static") - .span_label(span, "not a static") - .emit(); + self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToStatic { + attr_span: attr.span, + defn_span: span, + }); false } } } fn doc_attr_str_error(&self, meta: &NestedMetaItem, attr_name: &str) { - self.tcx - .sess - .struct_span_err( - meta.span(), - &format!("doc {0} attribute expects a string: #[doc({0} = \"a\")]", attr_name), - ) - .emit(); + self.tcx.sess.emit_err(errors::DocExpectStr { attr_span: meta.span(), attr_name }); } fn check_doc_alias_value( @@ -621,22 +569,12 @@ impl CheckAttrVisitor<'_> { aliases: &mut FxHashMap, ) -> bool { let tcx = self.tcx; - let err_fn = move |span: Span, msg: &str| { - tcx.sess.span_err( - span, - &format!( - "`#[doc(alias{})]` {}", - if is_list { "(\"...\")" } else { " = \"...\"" }, - msg, - ), - ); - false - }; + let span = meta.name_value_literal_span().unwrap_or_else(|| meta.span()); + let attr_str = + &format!("`#[doc(alias{})]`", if is_list { "(\"...\")" } else { " = \"...\"" }); if doc_alias == kw::Empty { - return err_fn( - meta.name_value_literal_span().unwrap_or_else(|| meta.span()), - "attribute cannot have empty value", - ); + tcx.sess.emit_err(errors::DocAliasEmpty { span, attr_str }); + return false; } let doc_alias_str = doc_alias.as_str(); @@ -644,23 +582,16 @@ impl CheckAttrVisitor<'_> { .chars() .find(|&c| c == '"' || c == '\'' || (c.is_whitespace() && c != ' ')) { - self.tcx.sess.span_err( - meta.name_value_literal_span().unwrap_or_else(|| meta.span()), - &format!( - "{:?} character isn't allowed in `#[doc(alias{})]`", - c, - if is_list { "(\"...\")" } else { " = \"...\"" }, - ), - ); + tcx.sess.emit_err(errors::DocAliasBadChar { span, attr_str, char_: c }); return false; } if doc_alias_str.starts_with(' ') || doc_alias_str.ends_with(' ') { - return err_fn( - meta.name_value_literal_span().unwrap_or_else(|| meta.span()), - "cannot start or end with ' '", - ); + tcx.sess.emit_err(errors::DocAliasStartEnd { span, attr_str }); + return false; } - if let Some(err) = match target { + + let span = meta.span(); + if let Some(location) = match target { Target::Impl => Some("implementation block"), Target::ForeignMod => Some("extern block"), Target::AssocTy => { @@ -686,19 +617,21 @@ impl CheckAttrVisitor<'_> { Target::Param => return false, _ => None, } { - return err_fn(meta.span(), &format!("isn't allowed on {}", err)); + tcx.sess.emit_err(errors::DocAliasBadLocation { span, attr_str, location }); + return false; } let item_name = self.tcx.hir().name(hir_id); if item_name == doc_alias { - return err_fn(meta.span(), "is the same as the item's name"); + tcx.sess.emit_err(errors::DocAliasNotAnAlias { span, attr_str }); + return false; } - let span = meta.span(); if let Err(entry) = aliases.try_insert(doc_alias_str.to_owned(), span) { - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, span, |lint| { - lint.build("doc alias is duplicated") - .span_label(*entry.entry.get(), "first defined here") - .emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + span, + errors::DocAliasDuplicated { first_defn: *entry.entry.get() }, + ); } true } @@ -723,22 +656,12 @@ impl CheckAttrVisitor<'_> { _ => { self.tcx .sess - .struct_span_err( - v.span(), - "`#[doc(alias(\"a\"))]` expects string literals", - ) - .emit(); + .emit_err(errors::DocAliasNotStringLiteral { span: v.span() }); errors += 1; } }, None => { - self.tcx - .sess - .struct_span_err( - v.span(), - "`#[doc(alias(\"a\"))]` expects string literals", - ) - .emit(); + self.tcx.sess.emit_err(errors::DocAliasNotStringLiteral { span: v.span() }); errors += 1; } } @@ -747,14 +670,7 @@ impl CheckAttrVisitor<'_> { } else if let Some(doc_alias) = meta.value_str() { self.check_doc_alias_value(meta, doc_alias, hir_id, target, false, aliases) } else { - self.tcx - .sess - .struct_span_err( - meta.span(), - "doc alias attribute expects a string `#[doc(alias = \"a\")]` or a list of \ - strings `#[doc(alias(\"a\", \"b\"))]`", - ) - .emit(); + self.tcx.sess.emit_err(errors::DocAliasMalformed { span: meta.span() }); false } } @@ -771,35 +687,20 @@ impl CheckAttrVisitor<'_> { }) { Some(ItemKind::Mod(ref module)) => { if !module.item_ids.is_empty() { - self.tcx - .sess - .struct_span_err( - meta.span(), - "`#[doc(keyword = \"...\")]` can only be used on empty modules", - ) - .emit(); + self.tcx.sess.emit_err(errors::DocKeywordEmptyMod { span: meta.span() }); return false; } } _ => { - self.tcx - .sess - .struct_span_err( - meta.span(), - "`#[doc(keyword = \"...\")]` can only be used on modules", - ) - .emit(); + self.tcx.sess.emit_err(errors::DocKeywordNotMod { span: meta.span() }); return false; } } if !rustc_lexer::is_ident(doc_keyword.as_str()) { - self.tcx - .sess - .struct_span_err( - meta.name_value_literal_span().unwrap_or_else(|| meta.span()), - &format!("`{doc_keyword}` is not a valid identifier"), - ) - .emit(); + self.tcx.sess.emit_err(errors::DocKeywordInvalidIdent { + span: meta.name_value_literal_span().unwrap_or_else(|| meta.span()), + doc_keyword, + }); return false; } true @@ -812,24 +713,12 @@ impl CheckAttrVisitor<'_> { }) { Some(ItemKind::Impl(ref i)) => { if !matches!(&i.self_ty.kind, hir::TyKind::Tup([_])) { - self.tcx - .sess - .struct_span_err( - meta.span(), - "`#[doc(tuple_variadic)]` must be used on the first of a set of tuple trait impls with varying arity", - ) - .emit(); + self.tcx.sess.emit_err(errors::DocTupleVariadicNotFirst { span: meta.span() }); return false; } } _ => { - self.tcx - .sess - .struct_span_err( - meta.span(), - "`#[doc(keyword = \"...\")]` can only be used on impl blocks", - ) - .emit(); + self.tcx.sess.emit_err(errors::DocKeywordOnlyImpl { span: meta.span() }); return false; } } @@ -858,13 +747,9 @@ impl CheckAttrVisitor<'_> { if let Some((prev_inline, prev_span)) = *specified_inline { if do_inline != prev_inline { let mut spans = MultiSpan::from_spans(vec![prev_span, meta.span()]); - spans.push_span_label(prev_span, "this attribute..."); - spans.push_span_label(meta.span(), "...conflicts with this attribute"); - self.tcx - .sess - .struct_span_err(spans, "conflicting doc inlining attributes") - .help("remove one of the conflicting attributes") - .emit(); + spans.push_span_label(prev_span, fluent::passes::doc_inline_conflict_first); + spans.push_span_label(meta.span(), fluent::passes::doc_inline_conflict_second); + self.tcx.sess.emit_err(errors::DocKeywordConflict { spans }); return false; } true @@ -873,23 +758,14 @@ impl CheckAttrVisitor<'_> { true } } else { - self.tcx.struct_span_lint_hir( + self.tcx.emit_spanned_lint( INVALID_DOC_ATTRIBUTES, hir_id, meta.span(), - |lint| { - let mut err = lint.build( - "this attribute can only be applied to a `use` item", - ); - err.span_label(meta.span(), "only applicable on `use` items"); - if attr.style == AttrStyle::Outer { - err.span_label( - self.tcx.hir().span(hir_id), - "not a `use` item", - ); - } - err.note("read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#inline-and-no_inline for more information") - .emit(); + errors::DocInlineOnlyUse { + attr_span: meta.span(), + item_span: (attr.style == AttrStyle::Outer) + .then(|| self.tcx.hir().span(hir_id)), }, ); false @@ -904,15 +780,7 @@ impl CheckAttrVisitor<'_> { attr_name: &str, ) -> bool { if CRATE_HIR_ID == hir_id { - self.tcx - .sess - .struct_span_err( - meta.span(), - &format!( - "`#![doc({attr_name} = \"...\")]` isn't allowed as a crate-level attribute", - ), - ) - .emit(); + self.tcx.sess.emit_err(errors::DocAttrNotCrateLevel { span: meta.span(), attr_name }); return false; } true @@ -926,36 +794,25 @@ impl CheckAttrVisitor<'_> { hir_id: HirId, ) -> bool { if hir_id != CRATE_HIR_ID { - self.tcx.struct_span_lint_hir( - INVALID_DOC_ATTRIBUTES, - hir_id, - meta.span(), - |lint| { - let mut err = lint.build( - "this attribute can only be applied at the crate level", - ); - if attr.style == AttrStyle::Outer && self.tcx.hir().get_parent_item(hir_id) == CRATE_DEF_ID { - if let Ok(mut src) = - self.tcx.sess.source_map().span_to_snippet(attr.span) - { - src.insert(1, '!'); - err.span_suggestion_verbose( - attr.span, - "to apply to the crate, use an inner attribute", - src, - Applicability::MaybeIncorrect, - ); - } else { - err.span_help( - attr.span, - "to apply to the crate, use an inner attribute", - ); - } + self.tcx.struct_span_lint_hir(INVALID_DOC_ATTRIBUTES, hir_id, meta.span(), |lint| { + let mut err = lint.build(fluent::passes::attr_crate_level); + if attr.style == AttrStyle::Outer + && self.tcx.hir().get_parent_item(hir_id) == CRATE_DEF_ID + { + if let Ok(mut src) = self.tcx.sess.source_map().span_to_snippet(attr.span) { + src.insert(1, '!'); + err.span_suggestion_verbose( + attr.span, + fluent::passes::suggestion, + src, + Applicability::MaybeIncorrect, + ); + } else { + err.span_help(attr.span, fluent::passes::help); } - err.note("read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#at-the-crate-level for more information") - .emit(); - }, - ); + } + err.note(fluent::passes::note).emit(); + }); return false; } true @@ -970,18 +827,14 @@ impl CheckAttrVisitor<'_> { match i_meta.name_or_empty() { sym::attr | sym::no_crate_inject => {} _ => { - self.tcx.struct_span_lint_hir( + self.tcx.emit_spanned_lint( INVALID_DOC_ATTRIBUTES, hir_id, i_meta.span(), - |lint| { - lint.build(&format!( - "unknown `doc(test)` attribute `{}`", - rustc_ast_pretty::pprust::path_to_string( - &i_meta.meta_item().unwrap().path - ), - )) - .emit(); + errors::DocTestUnknown { + path: rustc_ast_pretty::pprust::path_to_string( + &i_meta.meta_item().unwrap().path, + ), }, ); is_valid = false; @@ -989,9 +842,12 @@ impl CheckAttrVisitor<'_> { } } } else { - self.tcx.struct_span_lint_hir(INVALID_DOC_ATTRIBUTES, hir_id, meta.span(), |lint| { - lint.build("`#[doc(test(...)]` takes a list of attributes").emit(); - }); + self.tcx.emit_spanned_lint( + INVALID_DOC_ATTRIBUTES, + hir_id, + meta.span(), + errors::DocTestTakesList, + ); is_valid = false; } is_valid @@ -1093,79 +949,66 @@ impl CheckAttrVisitor<'_> { sym::primitive => { if !self.tcx.features().rustdoc_internals { - self.tcx.struct_span_lint_hir( + self.tcx.emit_spanned_lint( INVALID_DOC_ATTRIBUTES, hir_id, i_meta.span, - |lint| { - let mut diag = lint.build( - "`doc(primitive)` should never have been stable", - ); - diag.emit(); - }, + errors::DocPrimitive, ); } } _ => { - self.tcx.struct_span_lint_hir( - INVALID_DOC_ATTRIBUTES, - hir_id, - i_meta.span, - |lint| { - let mut diag = lint.build(&format!( - "unknown `doc` attribute `{}`", - rustc_ast_pretty::pprust::path_to_string(&i_meta.path), - )); - if i_meta.has_name(sym::spotlight) { - diag.note( - "`doc(spotlight)` was renamed to `doc(notable_trait)`", - ); - diag.span_suggestion_short( - i_meta.span, - "use `notable_trait` instead", - "notable_trait", - Applicability::MachineApplicable, - ); - diag.note("`doc(spotlight)` is now a no-op"); + let path = rustc_ast_pretty::pprust::path_to_string(&i_meta.path); + if i_meta.has_name(sym::spotlight) { + self.tcx.emit_spanned_lint( + INVALID_DOC_ATTRIBUTES, + hir_id, + i_meta.span, + errors::DocTestUnknownSpotlight { + path, + span: i_meta.span } - if i_meta.has_name(sym::include) { - if let Some(value) = i_meta.value_str() { - // if there are multiple attributes, the suggestion would suggest deleting all of them, which is incorrect - let applicability = if list.len() == 1 { - Applicability::MachineApplicable - } else { - Applicability::MaybeIncorrect - }; - let inner = if attr.style == AttrStyle::Inner { - "!" - } else { - "" - }; - diag.span_suggestion( - attr.meta().unwrap().span, - "use `doc = include_str!` instead", - format!( - "#{inner}[doc = include_str!(\"{value}\")]", - ), - applicability, - ); - } + ); + } else if i_meta.has_name(sym::include) && + let Some(value) = i_meta.value_str() { + let applicability = if list.len() == 1 { + Applicability::MachineApplicable + } else { + Applicability::MaybeIncorrect + }; + // If there are multiple attributes, the suggestion would suggest + // deleting all of them, which is incorrect. + self.tcx.emit_spanned_lint( + INVALID_DOC_ATTRIBUTES, + hir_id, + i_meta.span, + errors::DocTestUnknownInclude { + path, + value: value.to_string(), + inner: (attr.style == AttrStyle::Inner) + .then_some("!") + .unwrap_or(""), + sugg: (attr.meta().unwrap().span, applicability), } - diag.emit(); - }, - ); + ); + } else { + self.tcx.emit_spanned_lint( + INVALID_DOC_ATTRIBUTES, + hir_id, + i_meta.span, + errors::DocTestUnknownAny { path } + ); + } is_valid = false; } } } else { - self.tcx.struct_span_lint_hir( + self.tcx.emit_spanned_lint( INVALID_DOC_ATTRIBUTES, hir_id, meta.span(), - |lint| { - lint.build("invalid `doc` attribute").emit(); - }, + errors::DocInvalid, ); is_valid = false; } @@ -1180,14 +1023,7 @@ impl CheckAttrVisitor<'_> { match target { Target::Struct | Target::Enum | Target::TyAlias => true, _ => { - self.tcx - .sess - .struct_span_err( - attr.span, - "`pass_by_value` attribute should be applied to a struct, enum or type alias.", - ) - .span_label(span, "is not a struct, enum or type alias") - .emit(); + self.tcx.sess.emit_err(errors::PassByValue { attr_span: attr.span, span }); false } } @@ -1197,14 +1033,7 @@ impl CheckAttrVisitor<'_> { match target { Target::Method(MethodKind::Inherent) => true, _ => { - self.tcx - .sess - .struct_span_err( - attr.span, - "`rustc_allow_incoherent_impl` attribute should be applied to impl items.", - ) - .span_label(span, "the only currently supported targets are inherent methods") - .emit(); + self.tcx.sess.emit_err(errors::AllowIncoherentImpl { attr_span: attr.span, span }); false } } @@ -1223,12 +1052,7 @@ impl CheckAttrVisitor<'_> { _ => { self.tcx .sess - .struct_span_err( - attr.span, - "`rustc_has_incoherent_inherent_impls` attribute should be applied to types or traits.", - ) - .span_label(span, "only adts, extern types and traits are supported") - .emit(); + .emit_err(errors::HasIncoherentInherentImpl { attr_span: attr.span, span }); false } } @@ -1238,19 +1062,12 @@ impl CheckAttrVisitor<'_> { fn check_must_use(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool { let node = self.tcx.hir().get(hir_id); if let Some(kind) = node.fn_kind() && let rustc_hir::IsAsync::Async = kind.asyncness() { - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build( - "`must_use` attribute on `async` functions \ - applies to the anonymous `Future` returned by the \ - function, not the value within", - ) - .span_label( - span, - "this attribute does nothing, the `Future`s \ - returned by async functions are already `must_use`", - ) - .emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::MustUseAsync { span } + ); } if !matches!( @@ -1278,12 +1095,12 @@ impl CheckAttrVisitor<'_> { _ => "a", }; - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build(&format!( - "`#[must_use]` has no effect when applied to {article} {target}" - )) - .emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::MustUseNoEffect { article, target }, + ); } // For now, its always valid @@ -1295,11 +1112,7 @@ impl CheckAttrVisitor<'_> { match target { Target::Struct | Target::Enum | Target::Union | Target::Trait => true, _ => { - self.tcx - .sess - .struct_span_err(attr.span, "`must_not_suspend` attribute should be applied to a struct, enum, or trait") - .span_label(span, "is not a struct, enum, or trait") - .emit(); + self.tcx.sess.emit_err(errors::MustNotSuspend { attr_span: attr.span, span }); false } } @@ -1319,16 +1132,12 @@ impl CheckAttrVisitor<'_> { _ => { // FIXME: #[cold] was previously allowed on non-functions and some crates used // this, so only emit a warning. - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build("attribute should be applied to a function") - .warn( - "this was previously accepted by the compiler but is \ - being phased out; it will become a hard error in \ - a future release!", - ) - .span_label(span, "not a function") - .emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::Cold { span }, + ); } } } @@ -1343,19 +1152,12 @@ impl CheckAttrVisitor<'_> { return; } - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - let mut diag = - lint.build("attribute should be applied to an `extern` block with non-Rust ABI"); - diag.warn( - "this was previously accepted by the compiler but is \ - being phased out; it will become a hard error in \ - a future release!", - ); - if target != Target::ForeignMod { - diag.span_label(span, "not an `extern` block"); - } - diag.emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::Link { span: (target != Target::ForeignMod).then_some(span) }, + ); } /// Checks if `#[link_name]` is applied to an item other than a foreign function or static. diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs new file mode 100644 index 0000000000000..f8e8720ab5474 --- /dev/null +++ b/compiler/rustc_passes/src/errors.rs @@ -0,0 +1,362 @@ +use rustc_errors::{Applicability, MultiSpan}; +use rustc_macros::{LintDiagnostic, SessionDiagnostic}; +use rustc_span::{Span, Symbol}; + +#[derive(LintDiagnostic)] +#[lint(passes::outer_crate_level_attr)] +pub struct OuterCrateLevelAttr; + +#[derive(LintDiagnostic)] +#[lint(passes::inner_crate_level_attr)] +pub struct InnerCrateLevelAttr; + +#[derive(LintDiagnostic)] +#[lint(passes::ignored_attr_with_macro)] +pub struct IgnoredAttrWithMacro<'a> { + pub sym: &'a str, +} + +#[derive(LintDiagnostic)] +#[lint(passes::ignored_attr)] +pub struct IgnoredAttr<'a> { + pub sym: &'a str, +} + +#[derive(LintDiagnostic)] +#[lint(passes::inline_ignored_function_prototype)] +pub struct IgnoredInlineAttrFnProto; + +#[derive(LintDiagnostic)] +#[lint(passes::inline_ignored_constants)] +#[warn_] +#[note] +pub struct IgnoredInlineAttrConstants; + +#[derive(SessionDiagnostic)] +#[error(passes::inline_not_fn_or_closure, code = "E0518")] +pub struct InlineNotFnOrClosure { + #[primary_span] + pub attr_span: Span, + #[label] + pub defn_span: Span, +} + +#[derive(LintDiagnostic)] +#[lint(passes::no_coverage_ignored_function_prototype)] +pub struct IgnoredNoCoverageFnProto; + +#[derive(LintDiagnostic)] +#[lint(passes::no_coverage_propagate)] +pub struct IgnoredNoCoveragePropagate; + +#[derive(LintDiagnostic)] +#[lint(passes::no_coverage_fn_defn)] +pub struct IgnoredNoCoverageFnDefn; + +#[derive(SessionDiagnostic)] +#[error(passes::no_coverage_not_coverable, code = "E0788")] +pub struct IgnoredNoCoverageNotCoverable { + #[primary_span] + pub attr_span: Span, + #[label] + pub defn_span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::should_be_applied_to_fn)] +pub struct AttrShouldBeAppliedToFn { + #[primary_span] + pub attr_span: Span, + #[label] + pub defn_span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::naked_tracked_caller, code = "E0736")] +pub struct NakedTrackedCaller { + #[primary_span] + pub attr_span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::should_be_applied_to_fn, code = "E0739")] +pub struct TrackedCallerWrongLocation { + #[primary_span] + pub attr_span: Span, + #[label] + pub defn_span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::should_be_applied_to_struct_enum, code = "E0701")] +pub struct NonExhaustiveWrongLocation { + #[primary_span] + pub attr_span: Span, + #[label] + pub defn_span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::should_be_applied_to_trait)] +pub struct AttrShouldBeAppliedToTrait { + #[primary_span] + pub attr_span: Span, + #[label] + pub defn_span: Span, +} + +#[derive(LintDiagnostic)] +#[lint(passes::target_feature_on_statement)] +pub struct TargetFeatureOnStatement; + +#[derive(SessionDiagnostic)] +#[error(passes::should_be_applied_to_static)] +pub struct AttrShouldBeAppliedToStatic { + #[primary_span] + pub attr_span: Span, + #[label] + pub defn_span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::doc_expect_str)] +pub struct DocExpectStr<'a> { + #[primary_span] + pub attr_span: Span, + pub attr_name: &'a str, +} + +#[derive(SessionDiagnostic)] +#[error(passes::doc_alias_empty)] +pub struct DocAliasEmpty<'a> { + #[primary_span] + pub span: Span, + pub attr_str: &'a str, +} + +#[derive(SessionDiagnostic)] +#[error(passes::doc_alias_bad_char)] +pub struct DocAliasBadChar<'a> { + #[primary_span] + pub span: Span, + pub attr_str: &'a str, + pub char_: char, +} + +#[derive(SessionDiagnostic)] +#[error(passes::doc_alias_start_end)] +pub struct DocAliasStartEnd<'a> { + #[primary_span] + pub span: Span, + pub attr_str: &'a str, +} + +#[derive(SessionDiagnostic)] +#[error(passes::doc_alias_bad_location)] +pub struct DocAliasBadLocation<'a> { + #[primary_span] + pub span: Span, + pub attr_str: &'a str, + pub location: &'a str, +} + +#[derive(SessionDiagnostic)] +#[error(passes::doc_alias_not_an_alias)] +pub struct DocAliasNotAnAlias<'a> { + #[primary_span] + pub span: Span, + pub attr_str: &'a str, +} + +#[derive(LintDiagnostic)] +#[lint(passes::doc_alias_duplicated)] +pub struct DocAliasDuplicated { + #[label] + pub first_defn: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::doc_alias_not_string_literal)] +pub struct DocAliasNotStringLiteral { + #[primary_span] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::doc_alias_malformed)] +pub struct DocAliasMalformed { + #[primary_span] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::doc_keyword_empty_mod)] +pub struct DocKeywordEmptyMod { + #[primary_span] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::doc_keyword_not_mod)] +pub struct DocKeywordNotMod { + #[primary_span] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::doc_keyword_invalid_ident)] +pub struct DocKeywordInvalidIdent { + #[primary_span] + pub span: Span, + pub doc_keyword: Symbol, +} + +#[derive(SessionDiagnostic)] +#[error(passes::doc_tuple_variadic_not_first)] +pub struct DocTupleVariadicNotFirst { + #[primary_span] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::doc_keyword_only_impl)] +pub struct DocKeywordOnlyImpl { + #[primary_span] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::doc_inline_conflict)] +#[help] +pub struct DocKeywordConflict { + #[primary_span] + pub spans: MultiSpan, +} + +#[derive(LintDiagnostic)] +#[lint(passes::doc_inline_only_use)] +#[note] +pub struct DocInlineOnlyUse { + #[label] + pub attr_span: Span, + #[label(passes::not_a_use_item_label)] + pub item_span: Option, +} + +#[derive(SessionDiagnostic)] +#[error(passes::doc_attr_not_crate_level)] +pub struct DocAttrNotCrateLevel<'a> { + #[primary_span] + pub span: Span, + pub attr_name: &'a str, +} + +#[derive(LintDiagnostic)] +#[lint(passes::doc_test_unknown)] +pub struct DocTestUnknown { + pub path: String, +} + +#[derive(LintDiagnostic)] +#[lint(passes::doc_test_takes_list)] +pub struct DocTestTakesList; + +#[derive(LintDiagnostic)] +#[lint(passes::doc_primitive)] +pub struct DocPrimitive; + +#[derive(LintDiagnostic)] +#[lint(passes::doc_test_unknown_any)] +pub struct DocTestUnknownAny { + pub path: String, +} + +#[derive(LintDiagnostic)] +#[lint(passes::doc_test_unknown_spotlight)] +#[note] +#[note(passes::no_op_note)] +pub struct DocTestUnknownSpotlight { + pub path: String, + #[suggestion_short(applicability = "machine-applicable", code = "notable_trait")] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[lint(passes::doc_test_unknown_include)] +pub struct DocTestUnknownInclude { + pub path: String, + pub value: String, + pub inner: &'static str, + #[suggestion(code = "#{inner}[doc = include_str!(\"{value}\")]")] + pub sugg: (Span, Applicability), +} + +#[derive(LintDiagnostic)] +#[lint(passes::doc_invalid)] +pub struct DocInvalid; + +#[derive(SessionDiagnostic)] +#[error(passes::pass_by_value)] +pub struct PassByValue { + #[primary_span] + pub attr_span: Span, + #[label] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::allow_incoherent_impl)] +pub struct AllowIncoherentImpl { + #[primary_span] + pub attr_span: Span, + #[label] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::has_incoherent_inherent_impl)] +pub struct HasIncoherentInherentImpl { + #[primary_span] + pub attr_span: Span, + #[label] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[lint(passes::must_use_async)] +pub struct MustUseAsync { + #[label] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[lint(passes::must_use_no_effect)] +pub struct MustUseNoEffect { + pub article: &'static str, + pub target: rustc_hir::Target, +} + +#[derive(SessionDiagnostic)] +#[error(passes::must_not_suspend)] +pub struct MustNotSuspend { + #[primary_span] + pub attr_span: Span, + #[label] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[lint(passes::cold)] +#[warn_] +pub struct Cold { + #[label] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[lint(passes::link)] +#[warn_] +pub struct Link { + #[label] + pub span: Option, +} diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs index 497c0931c2182..7b2f83958af85 100644 --- a/compiler/rustc_passes/src/lib.rs +++ b/compiler/rustc_passes/src/lib.rs @@ -7,8 +7,8 @@ #![allow(rustc::potential_query_instability)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(iter_intersperse)] -#![feature(let_else)] #![feature(let_chains)] +#![feature(let_else)] #![feature(map_try_insert)] #![feature(min_specialization)] #![feature(try_blocks)] @@ -27,6 +27,7 @@ pub mod dead; mod debugger_visualizer; mod diagnostic_items; pub mod entry; +mod errors; pub mod hir_id_validator; pub mod hir_stats; mod lang_items; diff --git a/src/test/rustdoc-ui/invalid-doc-attr.stderr b/src/test/rustdoc-ui/invalid-doc-attr.stderr index 55006b2087eb0..a4fa3817905c7 100644 --- a/src/test/rustdoc-ui/invalid-doc-attr.stderr +++ b/src/test/rustdoc-ui/invalid-doc-attr.stderr @@ -12,7 +12,7 @@ LL | #![deny(warnings)] = note: `#[deny(invalid_doc_attributes)]` implied by `#[deny(warnings)]` = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #82730 - = note: read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#at-the-crate-level for more information + = note: read for more information help: to apply to the crate, use an inner attribute | LL | #![doc(test(no_crate_inject))] @@ -29,7 +29,7 @@ LL | pub fn foo() {} | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #82730 - = note: read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#inline-and-no_inline for more information + = note: read for more information error: this attribute can only be applied at the crate level --> $DIR/invalid-doc-attr.rs:15:12 @@ -39,7 +39,7 @@ LL | #![doc(test(no_crate_inject))] | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #82730 - = note: read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#at-the-crate-level for more information + = note: read for more information error: conflicting doc inlining attributes --> $DIR/invalid-doc-attr.rs:28:7 @@ -59,7 +59,7 @@ LL | #[doc(test(no_crate_inject))] | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #82730 - = note: read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#at-the-crate-level for more information + = note: read for more information error: this attribute can only be applied to a `use` item --> $DIR/invalid-doc-attr.rs:22:11 @@ -72,7 +72,7 @@ LL | pub fn baz() {} | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #82730 - = note: read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#inline-and-no_inline for more information + = note: read for more information error: aborting due to 6 previous errors diff --git a/src/test/ui/attributes/invalid-doc-attr.stderr b/src/test/ui/attributes/invalid-doc-attr.stderr index 55006b2087eb0..a4fa3817905c7 100644 --- a/src/test/ui/attributes/invalid-doc-attr.stderr +++ b/src/test/ui/attributes/invalid-doc-attr.stderr @@ -12,7 +12,7 @@ LL | #![deny(warnings)] = note: `#[deny(invalid_doc_attributes)]` implied by `#[deny(warnings)]` = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #82730 - = note: read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#at-the-crate-level for more information + = note: read for more information help: to apply to the crate, use an inner attribute | LL | #![doc(test(no_crate_inject))] @@ -29,7 +29,7 @@ LL | pub fn foo() {} | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #82730 - = note: read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#inline-and-no_inline for more information + = note: read for more information error: this attribute can only be applied at the crate level --> $DIR/invalid-doc-attr.rs:15:12 @@ -39,7 +39,7 @@ LL | #![doc(test(no_crate_inject))] | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #82730 - = note: read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#at-the-crate-level for more information + = note: read for more information error: conflicting doc inlining attributes --> $DIR/invalid-doc-attr.rs:28:7 @@ -59,7 +59,7 @@ LL | #[doc(test(no_crate_inject))] | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #82730 - = note: read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#at-the-crate-level for more information + = note: read for more information error: this attribute can only be applied to a `use` item --> $DIR/invalid-doc-attr.rs:22:11 @@ -72,7 +72,7 @@ LL | pub fn baz() {} | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #82730 - = note: read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#inline-and-no_inline for more information + = note: read for more information error: aborting due to 6 previous errors diff --git a/src/test/ui/attributes/multiple-invalid.stderr b/src/test/ui/attributes/multiple-invalid.stderr index 9bd29f15dbcca..a8dba0ba37d3a 100644 --- a/src/test/ui/attributes/multiple-invalid.stderr +++ b/src/test/ui/attributes/multiple-invalid.stderr @@ -7,14 +7,14 @@ LL | #[inline] LL | const FOO: u8 = 0; | ------------------ not a function or closure -error: attribute should be applied to a function +error: attribute should be applied to a function definition --> $DIR/multiple-invalid.rs:6:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | LL | const FOO: u8 = 0; - | ------------------ not a function + | ------------------ not a function definition error: aborting due to 2 previous errors diff --git a/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr b/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr index 3f838fcf52355..5d6796b49448a 100644 --- a/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr +++ b/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr @@ -259,7 +259,7 @@ warning: crate-level attribute should be an inner attribute: add an exclamation LL | #[no_std] | ^^^^^^^^^ -warning: attribute should be applied to a function +warning: attribute should be applied to a function definition --> $DIR/issue-43106-gating-of-builtin-attrs.rs:453:1 | LL | #[cold] @@ -272,7 +272,7 @@ LL | | mod inner { #![cold] } ... | LL | | LL | | } - | |_- not a function + | |_- not a function definition | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! @@ -399,7 +399,7 @@ warning: `#[proc_macro_derive]` only has an effect on functions LL | #![proc_macro_derive()] | ^^^^^^^^^^^^^^^^^^^^^^^ -warning: attribute should be applied to a function +warning: attribute should be applied to a function definition --> $DIR/issue-43106-gating-of-builtin-attrs.rs:62:1 | LL | #![cold] @@ -743,35 +743,35 @@ warning: crate-level attribute should be an inner attribute: add an exclamation LL | #[no_std] impl S { } | ^^^^^^^^^ -warning: attribute should be applied to a function +warning: attribute should be applied to a function definition --> $DIR/issue-43106-gating-of-builtin-attrs.rs:459:17 | LL | mod inner { #![cold] } - | ------------^^^^^^^^-- not a function + | ------------^^^^^^^^-- not a function definition | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -warning: attribute should be applied to a function +warning: attribute should be applied to a function definition --> $DIR/issue-43106-gating-of-builtin-attrs.rs:466:5 | LL | #[cold] struct S; - | ^^^^^^^ --------- not a function + | ^^^^^^^ --------- not a function definition | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -warning: attribute should be applied to a function +warning: attribute should be applied to a function definition --> $DIR/issue-43106-gating-of-builtin-attrs.rs:471:5 | LL | #[cold] type T = S; - | ^^^^^^^ ----------- not a function + | ^^^^^^^ ----------- not a function definition | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -warning: attribute should be applied to a function +warning: attribute should be applied to a function definition --> $DIR/issue-43106-gating-of-builtin-attrs.rs:476:5 | LL | #[cold] impl S { } - | ^^^^^^^ ---------- not a function + | ^^^^^^^ ---------- not a function definition | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! diff --git a/src/test/ui/future-incompatible-lint-group.stderr b/src/test/ui/future-incompatible-lint-group.stderr index d822847a7a589..8f6dde665e628 100644 --- a/src/test/ui/future-incompatible-lint-group.stderr +++ b/src/test/ui/future-incompatible-lint-group.stderr @@ -22,7 +22,7 @@ LL | #![deny(future_incompatible)] = note: `#[deny(invalid_doc_attributes)]` implied by `#[deny(future_incompatible)]` = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #82730 - = note: read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#at-the-crate-level for more information + = note: read for more information error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/issues/issue-54044.stderr b/src/test/ui/issues/issue-54044.stderr index 0200a6a629d8f..100965de1aa84 100644 --- a/src/test/ui/issues/issue-54044.stderr +++ b/src/test/ui/issues/issue-54044.stderr @@ -1,11 +1,11 @@ -error: attribute should be applied to a function +error: attribute should be applied to a function definition --> $DIR/issue-54044.rs:3:1 | LL | #[cold] | ^^^^^^^ ... LL | struct Foo; - | ----------- not a function + | ----------- not a function definition | note: the lint level is defined here --> $DIR/issue-54044.rs:1:9 @@ -14,14 +14,14 @@ LL | #![deny(unused_attributes)] | ^^^^^^^^^^^^^^^^^ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -error: attribute should be applied to a function +error: attribute should be applied to a function definition --> $DIR/issue-54044.rs:9:5 | LL | #[cold] | ^^^^^^^ ... LL | 5; - | - not a function + | - not a function definition | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! diff --git a/src/test/ui/issues/issue-78957.stderr b/src/test/ui/issues/issue-78957.stderr index fa2eaab5b417b..45fa69d6fd708 100644 --- a/src/test/ui/issues/issue-78957.stderr +++ b/src/test/ui/issues/issue-78957.stderr @@ -4,11 +4,11 @@ error[E0518]: attribute should be applied to function or closure LL | pub struct Foo<#[inline] const N: usize>; | ^^^^^^^^^ -------------- not a function or closure -error: attribute should be applied to a function +error: attribute should be applied to a function definition --> $DIR/issue-78957.rs:7:16 | LL | pub struct Bar<#[cold] const N: usize>; - | ^^^^^^^ -------------- not a function + | ^^^^^^^ -------------- not a function definition | note: the lint level is defined here --> $DIR/issue-78957.rs:1:9 @@ -29,11 +29,11 @@ error[E0518]: attribute should be applied to function or closure LL | pub struct Foo2<#[inline] 'a>(PhantomData<&'a ()>); | ^^^^^^^^^ -- not a function or closure -error: attribute should be applied to a function +error: attribute should be applied to a function definition --> $DIR/issue-78957.rs:15:17 | LL | pub struct Bar2<#[cold] 'a>(PhantomData<&'a ()>); - | ^^^^^^^ -- not a function + | ^^^^^^^ -- not a function definition | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! @@ -49,11 +49,11 @@ error[E0518]: attribute should be applied to function or closure LL | pub struct Foo3<#[inline] T>(PhantomData); | ^^^^^^^^^ - not a function or closure -error: attribute should be applied to a function +error: attribute should be applied to a function definition --> $DIR/issue-78957.rs:23:17 | LL | pub struct Bar3<#[cold] T>(PhantomData); - | ^^^^^^^ - not a function + | ^^^^^^^ - not a function definition | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! diff --git a/src/test/ui/macros/issue-68060.stderr b/src/test/ui/macros/issue-68060.stderr index 1b58cf9c4ede5..b13e418e664a6 100644 --- a/src/test/ui/macros/issue-68060.stderr +++ b/src/test/ui/macros/issue-68060.stderr @@ -1,11 +1,11 @@ -error: attribute should be applied to a function +error: attribute should be applied to a function definition --> $DIR/issue-68060.rs:4:13 | LL | #[target_feature(enable = "")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ... LL | |_| (), - | ------ not a function + | ------ not a function definition error: aborting due to previous error diff --git a/src/test/ui/marker_trait_attr/marker-attribute-on-non-trait.rs b/src/test/ui/marker_trait_attr/marker-attribute-on-non-trait.rs index 66156b6e53bab..0bf620934ec7b 100644 --- a/src/test/ui/marker_trait_attr/marker-attribute-on-non-trait.rs +++ b/src/test/ui/marker_trait_attr/marker-attribute-on-non-trait.rs @@ -1,23 +1,23 @@ #![feature(marker_trait_attr)] -#[marker] //~ ERROR attribute can only be applied to a trait +#[marker] //~ ERROR attribute should be applied to a trait struct Struct {} -#[marker] //~ ERROR attribute can only be applied to a trait +#[marker] //~ ERROR attribute should be applied to a trait impl Struct {} -#[marker] //~ ERROR attribute can only be applied to a trait +#[marker] //~ ERROR attribute should be applied to a trait union Union { x: i32, } -#[marker] //~ ERROR attribute can only be applied to a trait +#[marker] //~ ERROR attribute should be applied to a trait const CONST: usize = 10; -#[marker] //~ ERROR attribute can only be applied to a trait +#[marker] //~ ERROR attribute should be applied to a trait fn function() {} -#[marker] //~ ERROR attribute can only be applied to a trait +#[marker] //~ ERROR attribute should be applied to a trait type Type = (); fn main() {} diff --git a/src/test/ui/marker_trait_attr/marker-attribute-on-non-trait.stderr b/src/test/ui/marker_trait_attr/marker-attribute-on-non-trait.stderr index d30b990caac2b..19a5290dd7eb6 100644 --- a/src/test/ui/marker_trait_attr/marker-attribute-on-non-trait.stderr +++ b/src/test/ui/marker_trait_attr/marker-attribute-on-non-trait.stderr @@ -1,4 +1,4 @@ -error: attribute can only be applied to a trait +error: attribute should be applied to a trait --> $DIR/marker-attribute-on-non-trait.rs:3:1 | LL | #[marker] @@ -6,7 +6,7 @@ LL | #[marker] LL | struct Struct {} | ---------------- not a trait -error: attribute can only be applied to a trait +error: attribute should be applied to a trait --> $DIR/marker-attribute-on-non-trait.rs:6:1 | LL | #[marker] @@ -14,7 +14,7 @@ LL | #[marker] LL | impl Struct {} | -------------- not a trait -error: attribute can only be applied to a trait +error: attribute should be applied to a trait --> $DIR/marker-attribute-on-non-trait.rs:9:1 | LL | #[marker] @@ -24,7 +24,7 @@ LL | | x: i32, LL | | } | |_- not a trait -error: attribute can only be applied to a trait +error: attribute should be applied to a trait --> $DIR/marker-attribute-on-non-trait.rs:14:1 | LL | #[marker] @@ -32,7 +32,7 @@ LL | #[marker] LL | const CONST: usize = 10; | ------------------------ not a trait -error: attribute can only be applied to a trait +error: attribute should be applied to a trait --> $DIR/marker-attribute-on-non-trait.rs:17:1 | LL | #[marker] @@ -40,7 +40,7 @@ LL | #[marker] LL | fn function() {} | ---------------- not a trait -error: attribute can only be applied to a trait +error: attribute should be applied to a trait --> $DIR/marker-attribute-on-non-trait.rs:20:1 | LL | #[marker] diff --git a/src/test/ui/rfc-2008-non-exhaustive/invalid-attribute.rs b/src/test/ui/rfc-2008-non-exhaustive/invalid-attribute.rs index 3c4a09fafd2db..143f9a3009b61 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/invalid-attribute.rs +++ b/src/test/ui/rfc-2008-non-exhaustive/invalid-attribute.rs @@ -3,11 +3,11 @@ struct Foo; #[non_exhaustive] -//~^ ERROR attribute can only be applied to a struct or enum [E0701] +//~^ ERROR attribute should be applied to a struct or enum [E0701] trait Bar { } #[non_exhaustive] -//~^ ERROR attribute can only be applied to a struct or enum [E0701] +//~^ ERROR attribute should be applied to a struct or enum [E0701] union Baz { f1: u16, f2: u16 diff --git a/src/test/ui/rfc-2008-non-exhaustive/invalid-attribute.stderr b/src/test/ui/rfc-2008-non-exhaustive/invalid-attribute.stderr index 76d9e2d8205b7..136cd763b05c1 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/invalid-attribute.stderr +++ b/src/test/ui/rfc-2008-non-exhaustive/invalid-attribute.stderr @@ -4,7 +4,7 @@ error: malformed `non_exhaustive` attribute input LL | #[non_exhaustive(anything)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[non_exhaustive]` -error[E0701]: attribute can only be applied to a struct or enum +error[E0701]: attribute should be applied to a struct or enum --> $DIR/invalid-attribute.rs:5:1 | LL | #[non_exhaustive] @@ -13,7 +13,7 @@ LL | LL | trait Bar { } | ------------- not a struct or enum -error[E0701]: attribute can only be applied to a struct or enum +error[E0701]: attribute should be applied to a struct or enum --> $DIR/invalid-attribute.rs:9:1 | LL | #[non_exhaustive] diff --git a/src/test/ui/rfc-2091-track-caller/only-for-fns.rs b/src/test/ui/rfc-2091-track-caller/only-for-fns.rs index bc0ca9552806f..2d2b01b6f947a 100644 --- a/src/test/ui/rfc-2091-track-caller/only-for-fns.rs +++ b/src/test/ui/rfc-2091-track-caller/only-for-fns.rs @@ -1,5 +1,5 @@ #[track_caller] struct S; -//~^^ ERROR attribute should be applied to function +//~^^ ERROR attribute should be applied to a function definition fn main() {} diff --git a/src/test/ui/rfc-2091-track-caller/only-for-fns.stderr b/src/test/ui/rfc-2091-track-caller/only-for-fns.stderr index 6666dcfa6e599..b36597bded941 100644 --- a/src/test/ui/rfc-2091-track-caller/only-for-fns.stderr +++ b/src/test/ui/rfc-2091-track-caller/only-for-fns.stderr @@ -1,10 +1,10 @@ -error[E0739]: attribute should be applied to function +error[E0739]: attribute should be applied to a function definition --> $DIR/only-for-fns.rs:1:1 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ LL | struct S; - | --------- not a function + | --------- not a function definition error: aborting due to previous error diff --git a/src/test/ui/rustdoc/doc_keyword.rs b/src/test/ui/rustdoc/doc_keyword.rs index 43b84e5018cda..68a8802b2f645 100644 --- a/src/test/ui/rustdoc/doc_keyword.rs +++ b/src/test/ui/rustdoc/doc_keyword.rs @@ -15,6 +15,6 @@ fn foo() {} // Regression test for the ICE described in #83512. trait Foo { #[doc(keyword = "match")] - //~^ ERROR: `#[doc(keyword = "...")]` can only be used on modules + //~^ ERROR: `#[doc(keyword = "...")]` should be used on modules fn quux() {} } diff --git a/src/test/ui/rustdoc/doc_keyword.stderr b/src/test/ui/rustdoc/doc_keyword.stderr index 6ba7034d54122..a1d0e4ffc0938 100644 --- a/src/test/ui/rustdoc/doc_keyword.stderr +++ b/src/test/ui/rustdoc/doc_keyword.stderr @@ -1,16 +1,16 @@ -error: `#[doc(keyword = "...")]` can only be used on empty modules +error: `#[doc(keyword = "...")]` should be used on empty modules --> $DIR/doc_keyword.rs:6:7 | LL | #[doc(keyword = "hell")] | ^^^^^^^^^^^^^^^^ -error: `#[doc(keyword = "...")]` can only be used on modules +error: `#[doc(keyword = "...")]` should be used on modules --> $DIR/doc_keyword.rs:11:7 | LL | #[doc(keyword = "hall")] | ^^^^^^^^^^^^^^^^ -error: `#[doc(keyword = "...")]` can only be used on modules +error: `#[doc(keyword = "...")]` should be used on modules --> $DIR/doc_keyword.rs:17:11 | LL | #[doc(keyword = "match")] diff --git a/src/test/ui/target-feature/invalid-attribute.stderr b/src/test/ui/target-feature/invalid-attribute.stderr index 25a2c1975e7b2..889ced9752bd4 100644 --- a/src/test/ui/target-feature/invalid-attribute.stderr +++ b/src/test/ui/target-feature/invalid-attribute.stderr @@ -34,43 +34,43 @@ LL | fn bar() {} = note: see issue #69098 for more information = help: add `#![feature(target_feature_11)]` to the crate attributes to enable -error: attribute should be applied to a function +error: attribute should be applied to a function definition --> $DIR/invalid-attribute.rs:34:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | LL | mod another {} - | -------------- not a function + | -------------- not a function definition -error: attribute should be applied to a function +error: attribute should be applied to a function definition --> $DIR/invalid-attribute.rs:39:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | LL | const FOO: usize = 7; - | --------------------- not a function + | --------------------- not a function definition -error: attribute should be applied to a function +error: attribute should be applied to a function definition --> $DIR/invalid-attribute.rs:44:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | LL | struct Foo; - | ----------- not a function + | ----------- not a function definition -error: attribute should be applied to a function +error: attribute should be applied to a function definition --> $DIR/invalid-attribute.rs:49:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | LL | enum Bar {} - | ----------- not a function + | ----------- not a function definition -error: attribute should be applied to a function +error: attribute should be applied to a function definition --> $DIR/invalid-attribute.rs:54:1 | LL | #[target_feature(enable = "sse2")] @@ -81,16 +81,16 @@ LL | | LL | | f1: u16, LL | | f2: u16, LL | | } - | |_- not a function + | |_- not a function definition -error: attribute should be applied to a function +error: attribute should be applied to a function definition --> $DIR/invalid-attribute.rs:62:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | LL | trait Baz {} - | ------------ not a function + | ------------ not a function definition error: cannot use `#[inline(always)]` with `#[target_feature]` --> $DIR/invalid-attribute.rs:67:1 @@ -98,7 +98,7 @@ error: cannot use `#[inline(always)]` with `#[target_feature]` LL | #[inline(always)] | ^^^^^^^^^^^^^^^^^ -error: attribute should be applied to a function +error: attribute should be applied to a function definition --> $DIR/invalid-attribute.rs:85:5 | LL | #[target_feature(enable = "sse2")] @@ -108,16 +108,16 @@ LL | / unsafe { LL | | foo(); LL | | bar(); LL | | } - | |_____- not a function + | |_____- not a function definition -error: attribute should be applied to a function +error: attribute should be applied to a function definition --> $DIR/invalid-attribute.rs:93:5 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | LL | || {}; - | ----- not a function + | ----- not a function definition error[E0658]: `#[target_feature(..)]` can only be applied to `unsafe` functions --> $DIR/invalid-attribute.rs:77:5 diff --git a/src/test/ui/traits/default-method/rustc_must_implement_one_of_misuse.rs b/src/test/ui/traits/default-method/rustc_must_implement_one_of_misuse.rs index d9de6d5edb9b4..1f896da94db57 100644 --- a/src/test/ui/traits/default-method/rustc_must_implement_one_of_misuse.rs +++ b/src/test/ui/traits/default-method/rustc_must_implement_one_of_misuse.rs @@ -36,11 +36,11 @@ trait Tr5 { } #[rustc_must_implement_one_of(abc, xyz)] -//~^ attribute can only be applied to a trait +//~^ attribute should be applied to a trait fn function() {} #[rustc_must_implement_one_of(abc, xyz)] -//~^ attribute can only be applied to a trait +//~^ attribute should be applied to a trait struct Struct {} fn main() {} diff --git a/src/test/ui/traits/default-method/rustc_must_implement_one_of_misuse.stderr b/src/test/ui/traits/default-method/rustc_must_implement_one_of_misuse.stderr index bc28dc2c4f40d..869184f0d1a69 100644 --- a/src/test/ui/traits/default-method/rustc_must_implement_one_of_misuse.stderr +++ b/src/test/ui/traits/default-method/rustc_must_implement_one_of_misuse.stderr @@ -4,7 +4,7 @@ error: malformed `rustc_must_implement_one_of` attribute input LL | #[rustc_must_implement_one_of] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[rustc_must_implement_one_of(function1, function2, ...)]` -error: attribute can only be applied to a trait +error: attribute should be applied to a trait --> $DIR/rustc_must_implement_one_of_misuse.rs:38:1 | LL | #[rustc_must_implement_one_of(abc, xyz)] @@ -13,7 +13,7 @@ LL | LL | fn function() {} | ---------------- not a trait -error: attribute can only be applied to a trait +error: attribute should be applied to a trait --> $DIR/rustc_must_implement_one_of_misuse.rs:42:1 | LL | #[rustc_must_implement_one_of(abc, xyz)]