#![feature(let_chains)]
#![feature(once_cell)]
#![feature(path_try_exists)]
+#![feature(rustc_attrs)]
#![feature(type_alias_impl_trait)]
use fluent_bundle::FluentResource;
/// message so messages of this type must be combined with a `DiagnosticMessage` (using
/// `DiagnosticMessage::with_subdiagnostic_message`) before rendering. However, subdiagnostics from
/// the `SessionSubdiagnostic` derive refer to Fluent identifiers directly.
+#[rustc_diagnostic_item = "SubdiagnosticMessage"]
pub enum SubdiagnosticMessage {
/// Non-translatable diagnostic message.
// FIXME(davidtwco): can a `Cow<'static, str>` be used here?
///
/// Intended to be removed once diagnostics are entirely translatable.
#[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)]
+#[rustc_diagnostic_item = "DiagnosticMessage"]
pub enum DiagnosticMessage {
/// Non-translatable diagnostic message.
// FIXME(davidtwco): can a `Cow<'static, str>` be used here?
/// Trait implemented by error types. This should not be implemented manually. Instead, use
/// `#[derive(SessionSubdiagnostic)]` -- see [rustc_macros::SessionSubdiagnostic].
+#[rustc_diagnostic_item = "AddSubdiagnostic"]
pub trait AddSubdiagnostic {
/// Add a subdiagnostic to an existing diagnostic.
fn add_to_diagnostic(self, diag: &mut Diagnostic);
///
/// This span is *not* considered a ["primary span"][`MultiSpan`]; only
/// the `Span` supplied when creating the diagnostic is primary.
+ #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)]
pub fn span_label(&mut self, span: Span, label: impl Into<SubdiagnosticMessage>) -> &mut Self {
self.span.push_span_label(span, self.subdiagnostic_message_to_diagnostic_message(label));
self
}
/// Add a note attached to this diagnostic.
+ #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)]
pub fn note(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self {
self.sub(Level::Note, msg, MultiSpan::new(), None);
self
/// Prints the span with a note above it.
/// This is like [`Diagnostic::note()`], but it gets its own span.
+ #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)]
pub fn span_note<S: Into<MultiSpan>>(
&mut self,
sp: S,
}
/// Add a warning attached to this diagnostic.
+ #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)]
pub fn warn(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self {
self.sub(Level::Warning, msg, MultiSpan::new(), None);
self
/// Prints the span with a warning above it.
/// This is like [`Diagnostic::warn()`], but it gets its own span.
+ #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)]
pub fn span_warn<S: Into<MultiSpan>>(
&mut self,
sp: S,
}
/// Add a help message attached to this diagnostic.
+ #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)]
pub fn help(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self {
self.sub(Level::Help, msg, MultiSpan::new(), None);
self
/// Prints the span with some help above it.
/// This is like [`Diagnostic::help()`], but it gets its own span.
+ #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)]
pub fn span_help<S: Into<MultiSpan>>(
&mut self,
sp: S,
#![feature(let_else)]
#![feature(never_type)]
#![feature(adt_const_params)]
+#![feature(rustc_attrs)]
#![allow(incomplete_features)]
#![allow(rustc::potential_query_instability)]
/// Attempting to `.emit()` the builder will only emit if either:
/// * `can_emit_warnings` is `true`
/// * `is_force_warn` was set in `DiagnosticId::Lint`
+ #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)]
pub fn struct_span_warn(
&self,
span: impl Into<MultiSpan>,
}
/// Construct a builder at the `Allow` level at the given `span` and with the `msg`.
+ #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)]
pub fn struct_span_allow(
&self,
span: impl Into<MultiSpan>,
/// Construct a builder at the `Warning` level at the given `span` and with the `msg`.
/// Also include a code.
+ #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)]
pub fn struct_span_warn_with_code(
&self,
span: impl Into<MultiSpan>,
/// Attempting to `.emit()` the builder will only emit if either:
/// * `can_emit_warnings` is `true`
/// * `is_force_warn` was set in `DiagnosticId::Lint`
+ #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)]
pub fn struct_warn(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> {
DiagnosticBuilder::new(self, Level::Warning, msg)
}
/// Construct a builder at the `Allow` level with the `msg`.
+ #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)]
pub fn struct_allow(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> {
DiagnosticBuilder::new(self, Level::Allow, msg)
}
/// Construct a builder at the `Expect` level with the `msg`.
+ #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)]
pub fn struct_expect(
&self,
msg: impl Into<DiagnosticMessage>,
}
/// Construct a builder at the `Error` level at the given `span` and with the `msg`.
+ #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)]
pub fn struct_span_err(
&self,
span: impl Into<MultiSpan>,
}
/// Construct a builder at the `Error` level at the given `span`, with the `msg`, and `code`.
+ #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)]
pub fn struct_span_err_with_code(
&self,
span: impl Into<MultiSpan>,
/// Construct a builder at the `Error` level with the `msg`.
// FIXME: This method should be removed (every error should have an associated error code).
+ #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)]
pub fn struct_err(
&self,
msg: impl Into<DiagnosticMessage>,
}
/// Construct a builder at the `Error` level with the `msg` and the `code`.
+ #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)]
pub fn struct_err_with_code(
&self,
msg: impl Into<DiagnosticMessage>,
}
/// Construct a builder at the `Warn` level with the `msg` and the `code`.
+ #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)]
pub fn struct_warn_with_code(
&self,
msg: impl Into<DiagnosticMessage>,
}
/// Construct a builder at the `Fatal` level at the given `span` and with the `msg`.
+ #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)]
pub fn struct_span_fatal(
&self,
span: impl Into<MultiSpan>,
}
/// Construct a builder at the `Fatal` level at the given `span`, with the `msg`, and `code`.
+ #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)]
pub fn struct_span_fatal_with_code(
&self,
span: impl Into<MultiSpan>,
}
/// Construct a builder at the `Error` level with the `msg`.
+ #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)]
pub fn struct_fatal(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, !> {
DiagnosticBuilder::new_fatal(self, msg)
}
/// Construct a builder at the `Help` level with the `msg`.
+ #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)]
pub fn struct_help(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> {
DiagnosticBuilder::new(self, Level::Help, msg)
}
/// Construct a builder at the `Note` level with the `msg`.
+ #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)]
pub fn struct_note_without_error(
&self,
msg: impl Into<DiagnosticMessage>,
DiagnosticBuilder::new(self, Level::Note, msg)
}
+ #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)]
pub fn span_fatal(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) -> ! {
self.emit_diag_at_span(Diagnostic::new(Fatal, msg), span);
FatalError.raise()
}
+ #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)]
pub fn span_fatal_with_code(
&self,
span: impl Into<MultiSpan>,
FatalError.raise()
}
+ #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)]
pub fn span_err(
&self,
span: impl Into<MultiSpan>,
self.emit_diag_at_span(Diagnostic::new(Error { lint: false }, msg), span).unwrap()
}
+ #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)]
pub fn span_err_with_code(
&self,
span: impl Into<MultiSpan>,
);
}
+ #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)]
pub fn span_warn(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) {
self.emit_diag_at_span(Diagnostic::new(Warning, msg), span);
}
+ #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)]
pub fn span_warn_with_code(
&self,
span: impl Into<MultiSpan>,
// Used by the `rustc::potential_query_instability` lint to warn methods which
// might not be stable during incremental compilation.
rustc_attr!(rustc_lint_query_instability, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE),
+ // Used by the `rustc::untranslatable_diagnostic` and `rustc::diagnostic_outside_of_impl` lints
+ // to assist in changes to diagnostic APIs.
+ rustc_attr!(rustc_lint_diagnostics, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE),
// ==========================================================================
// Internal attributes, Const related:
use rustc_ast as ast;
use rustc_errors::Applicability;
use rustc_hir::def::Res;
-use rustc_hir::{Expr, ExprKind, GenericArg, PatKind, Path, PathSegment, QPath};
-use rustc_hir::{HirId, Item, ItemKind, Node, Pat, Ty, TyKind};
+use rustc_hir::{def_id::DefId, Expr, ExprKind, GenericArg, PatKind, Path, PathSegment, QPath};
+use rustc_hir::{HirId, Impl, Item, ItemKind, Node, Pat, Ty, TyKind};
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::hygiene::{ExpnKind, MacroKind};
use rustc_span::symbol::{kw, sym, Symbol};
+use rustc_span::Span;
+use tracing::debug;
declare_tool_lint! {
pub rustc::DEFAULT_HASH_TYPES,
}
}
+/// Helper function for lints that check for expressions with calls and use typeck results to
+/// get the `DefId` and `SubstsRef` of the function.
+fn typeck_results_of_method_fn<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &Expr<'_>,
+) -> Option<(Span, DefId, ty::subst::SubstsRef<'tcx>)> {
+ // FIXME(rustdoc): Lints which use this function use typecheck results which can cause
+ // `rustdoc` to error if there are resolution failures.
+ //
+ // As internal lints are currently always run if there are `unstable_options`, they are added
+ // to the lint store of rustdoc. Internal lints are also not used via the `lint_mod` query.
+ // Crate lints run outside of a query so rustdoc currently doesn't disable them.
+ //
+ // Instead of relying on this, either change crate lints to a query disabled by rustdoc, only
+ // run internal lints if the user is explicitly opting in or figure out a different way to
+ // avoid running lints for rustdoc.
+ if cx.tcx.sess.opts.actually_rustdoc {
+ return None;
+ }
+
+ match expr.kind {
+ ExprKind::MethodCall(segment, _, _)
+ if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) =>
+ {
+ Some((segment.ident.span, def_id, cx.typeck_results().node_substs(expr.hir_id)))
+ },
+ _ => {
+ match cx.typeck_results().node_type(expr.hir_id).kind() {
+ &ty::FnDef(def_id, substs) => Some((expr.span, def_id, substs)),
+ _ => None,
+ }
+ }
+ }
+}
+
declare_tool_lint! {
pub rustc::POTENTIAL_QUERY_INSTABILITY,
Allow,
impl LateLintPass<'_> for QueryStability {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
- // FIXME(rustdoc): This lint uses typecheck results, causing rustdoc to
- // error if there are resolution failures.
- //
- // As internal lints are currently always run if there are `unstable_options`,
- // they are added to the lint store of rustdoc. Internal lints are also
- // not used via the `lint_mod` query. Crate lints run outside of a query
- // so rustdoc currently doesn't disable them.
- //
- // Instead of relying on this, either change crate lints to a query disabled by
- // rustdoc, only run internal lints if the user is explicitly opting in
- // or figure out a different way to avoid running lints for rustdoc.
- if cx.tcx.sess.opts.actually_rustdoc {
- return;
- }
-
- let (span, def_id, substs) = match expr.kind {
- ExprKind::MethodCall(segment, _, _)
- if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) =>
- {
- (segment.ident.span, def_id, cx.typeck_results().node_substs(expr.hir_id))
- },
- _ => {
- let &ty::FnDef(def_id, substs) =
- cx.typeck_results()
- .node_type(expr.hir_id)
- .kind() else { return };
- (expr.span, def_id, substs)
- }
- };
+ let Some((span, def_id, substs)) = typeck_results_of_method_fn(cx, expr) else { return };
if let Ok(Some(instance)) = ty::Instance::resolve(cx.tcx, cx.param_env, def_id, substs) {
let def_id = instance.def_id();
if cx.tcx.has_attr(def_id, sym::rustc_lint_query_instability) {
}
}
}
+
+declare_tool_lint! {
+ pub rustc::UNTRANSLATABLE_DIAGNOSTIC,
+ Allow,
+ "prevent creation of diagnostics which cannot be translated",
+ report_in_external_macro: true
+}
+
+declare_tool_lint! {
+ pub rustc::DIAGNOSTIC_OUTSIDE_OF_IMPL,
+ Allow,
+ "prevent creation of diagnostics outside of `SessionDiagnostic`/`AddSubdiagnostic` impls",
+ report_in_external_macro: true
+}
+
+declare_lint_pass!(Diagnostics => [ UNTRANSLATABLE_DIAGNOSTIC, DIAGNOSTIC_OUTSIDE_OF_IMPL ]);
+
+impl LateLintPass<'_> for Diagnostics {
+ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
+ let Some((span, def_id, substs)) = typeck_results_of_method_fn(cx, expr) else { return };
+ debug!(?span, ?def_id, ?substs);
+ if let Ok(Some(instance)) = ty::Instance::resolve(cx.tcx, cx.param_env, def_id, substs) &&
+ !cx.tcx.has_attr(instance.def_id(), sym::rustc_lint_diagnostics)
+ {
+ return;
+ }
+
+ let mut found_impl = false;
+ for (_, parent) in cx.tcx.hir().parent_iter(expr.hir_id) {
+ debug!(?parent);
+ if let Node::Item(Item { kind: ItemKind::Impl(impl_), .. }) = parent &&
+ let Impl { of_trait: Some(of_trait), .. } = impl_ &&
+ let Some(def_id) = of_trait.trait_def_id() &&
+ let Some(name) = cx.tcx.get_diagnostic_name(def_id) &&
+ matches!(name, sym::SessionDiagnostic | sym::AddSubdiagnostic)
+ {
+ found_impl = true;
+ break;
+ }
+ }
+ debug!(?found_impl);
+ if !found_impl {
+ cx.struct_span_lint(DIAGNOSTIC_OUTSIDE_OF_IMPL, span, |lint| {
+ lint.build("diagnostics should only be created in `SessionDiagnostic`/`AddSubdiagnostic` impls")
+ .emit();
+ })
+ }
+
+ let mut found_diagnostic_message = false;
+ for ty in substs.types() {
+ debug!(?ty);
+ if let Some(adt_def) = ty.ty_adt_def() &&
+ let Some(name) = cx.tcx.get_diagnostic_name(adt_def.did()) &&
+ matches!(name, sym::DiagnosticMessage | sym::SubdiagnosticMessage)
+ {
+ found_diagnostic_message = true;
+ break;
+ }
+ }
+ debug!(?found_diagnostic_message);
+ if !found_diagnostic_message {
+ cx.struct_span_lint(UNTRANSLATABLE_DIAGNOSTIC, span, |lint| {
+ lint.build("diagnostics should be created using translatable messages").emit();
+ })
+ }
+ }
+}
store.register_late_pass(|| Box::new(ExistingDocKeyword));
store.register_lints(&TyTyKind::get_lints());
store.register_late_pass(|| Box::new(TyTyKind));
+ store.register_lints(&Diagnostics::get_lints());
+ store.register_late_pass(|| Box::new(Diagnostics));
store.register_lints(&PassByValue::get_lints());
store.register_late_pass(|| Box::new(PassByValue));
store.register_group(
sym::rustc_lint_query_instability => {
self.check_rustc_lint_query_instability(&attr, span, target)
}
+ sym::rustc_lint_diagnostics => {
+ self.check_rustc_lint_diagnostics(&attr, span, target)
+ }
sym::rustc_clean
| sym::rustc_dirty
| sym::rustc_if_this_changed
}
}
- fn check_rustc_lint_query_instability(
- &self,
- attr: &Attribute,
- span: Span,
- target: Target,
- ) -> bool {
+ /// Helper function for checking that the provided attribute is only applied to a function or
+ /// method.
+ fn check_applied_to_fn_or_method(&self, attr: &Attribute, span: Span, target: Target) -> bool {
let is_function = matches!(target, Target::Fn | Target::Method(..));
if !is_function {
self.tcx
}
}
+ /// Checks that the `#[rustc_lint_query_instability]` attribute is only applied to a function
+ /// or method.
+ fn check_rustc_lint_query_instability(
+ &self,
+ attr: &Attribute,
+ span: Span,
+ target: Target,
+ ) -> bool {
+ self.check_applied_to_fn_or_method(attr, span, target)
+ }
+
+ /// Checks that the `#[rustc_lint_diagnostics]` attribute is only applied to a function or
+ /// method.
+ fn check_rustc_lint_diagnostics(&self, attr: &Attribute, span: Span, target: Target) -> bool {
+ self.check_applied_to_fn_or_method(attr, span, target)
+ }
+
/// Checks that the dep-graph debugging attributes are only present when the query-dep-graph
/// option is passed to the compiler.
fn check_rustc_dirty_clean(&self, attr: &Attribute) -> bool {
#![feature(never_type)]
#![feature(once_cell)]
#![feature(option_get_or_insert_default)]
+#![feature(rustc_attrs)]
#![recursion_limit = "256"]
#![allow(rustc::potential_query_instability)]
self.create_warning(warning).emit()
}
+ #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)]
pub fn struct_err(
&self,
msg: impl Into<DiagnosticMessage>,
self.span_diagnostic.struct_err(msg)
}
+ #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)]
pub fn struct_warn(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> {
self.span_diagnostic.struct_warn(msg)
}
/// Trait implemented by error types. This should not be implemented manually. Instead, use
/// `#[derive(SessionDiagnostic)]` -- see [rustc_macros::SessionDiagnostic].
+#[rustc_diagnostic_item = "SessionDiagnostic"]
pub trait SessionDiagnostic<'a, T: EmissionGuarantee = ErrorGuaranteed> {
/// Write out as a diagnostic out of `sess`.
#[must_use]
Symbols {
AcqRel,
Acquire,
+ AddSubdiagnostic,
Alignment,
Any,
Arc,
Decoder,
Default,
Deref,
+ DiagnosticMessage,
DirBuilder,
Display,
DoubleEndedIterator,
RustcEncodable,
Send,
SeqCst,
+ SessionDiagnostic,
SliceIndex,
Some,
String,
StructuralEq,
StructuralPartialEq,
+ SubdiagnosticMessage,
Sync,
Target,
ToOwned,
rustc_layout_scalar_valid_range_end,
rustc_layout_scalar_valid_range_start,
rustc_legacy_const_generics,
+ rustc_lint_diagnostics,
rustc_lint_query_instability,
rustc_macro_transparency,
rustc_main,
--- /dev/null
+// compile-flags: -Z unstable-options
+
+#![crate_type = "lib"]
+#![feature(rustc_private)]
+#![deny(rustc::untranslatable_diagnostic)]
+#![deny(rustc::diagnostic_outside_of_impl)]
+
+extern crate rustc_errors;
+extern crate rustc_macros;
+extern crate rustc_session;
+extern crate rustc_span;
+
+use rustc_errors::{AddSubdiagnostic, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, fluent};
+use rustc_macros::{SessionDiagnostic, SessionSubdiagnostic};
+use rustc_session::{parse::ParseSess, SessionDiagnostic};
+use rustc_span::Span;
+
+#[derive(SessionDiagnostic)]
+#[error(slug = "parser-expect-path")]
+struct DeriveSessionDiagnostic {
+ #[primary_span]
+ span: Span,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[note(slug = "note")]
+struct Note {
+ #[primary_span]
+ span: Span,
+}
+
+pub struct UntranslatableInSessionDiagnostic;
+
+impl<'a> SessionDiagnostic<'a, ErrorGuaranteed> for UntranslatableInSessionDiagnostic {
+ fn into_diagnostic(self, sess: &'a ParseSess) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
+ sess.struct_err("untranslatable diagnostic")
+ //~^ ERROR diagnostics should be created using translatable messages
+ }
+}
+
+pub struct TranslatableInSessionDiagnostic;
+
+impl<'a> SessionDiagnostic<'a, ErrorGuaranteed> for TranslatableInSessionDiagnostic {
+ fn into_diagnostic(self, sess: &'a ParseSess) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
+ sess.struct_err(fluent::parser::expect_path)
+ }
+}
+
+pub struct UntranslatableInAddSubdiagnostic;
+
+impl AddSubdiagnostic for UntranslatableInAddSubdiagnostic {
+ fn add_to_diagnostic(self, diag: &mut Diagnostic) {
+ diag.note("untranslatable diagnostic");
+ //~^ ERROR diagnostics should be created using translatable messages
+ }
+}
+
+pub struct TranslatableInAddSubdiagnostic;
+
+impl AddSubdiagnostic for TranslatableInAddSubdiagnostic {
+ fn add_to_diagnostic(self, diag: &mut Diagnostic) {
+ diag.note(fluent::typeck::note);
+ }
+}
+
+pub fn make_diagnostics<'a>(sess: &'a ParseSess) {
+ let _diag = sess.struct_err(fluent::parser::expect_path);
+ //~^ ERROR diagnostics should only be created in `SessionDiagnostic`/`AddSubdiagnostic` impls
+
+ let _diag = sess.struct_err("untranslatable diagnostic");
+ //~^ ERROR diagnostics should only be created in `SessionDiagnostic`/`AddSubdiagnostic` impls
+ //~^^ ERROR diagnostics should be created using translatable messages
+}
--- /dev/null
+error: diagnostics should be created using translatable messages
+ --> $DIR/diagnostics.rs:36:14
+ |
+LL | sess.struct_err("untranslatable diagnostic")
+ | ^^^^^^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/diagnostics.rs:5:9
+ |
+LL | #![deny(rustc::untranslatable_diagnostic)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: diagnostics should be created using translatable messages
+ --> $DIR/diagnostics.rs:53:14
+ |
+LL | diag.note("untranslatable diagnostic");
+ | ^^^^
+
+error: diagnostics should only be created in `SessionDiagnostic`/`AddSubdiagnostic` impls
+ --> $DIR/diagnostics.rs:67:22
+ |
+LL | let _diag = sess.struct_err(fluent::parser::expect_path);
+ | ^^^^^^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/diagnostics.rs:6:9
+ |
+LL | #![deny(rustc::diagnostic_outside_of_impl)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: diagnostics should only be created in `SessionDiagnostic`/`AddSubdiagnostic` impls
+ --> $DIR/diagnostics.rs:70:22
+ |
+LL | let _diag = sess.struct_err("untranslatable diagnostic");
+ | ^^^^^^^^^^
+
+error: diagnostics should be created using translatable messages
+ --> $DIR/diagnostics.rs:70:22
+ |
+LL | let _diag = sess.struct_err("untranslatable diagnostic");
+ | ^^^^^^^^^^
+
+error: aborting due to 5 previous errors
+
--- /dev/null
+// compile-flags: -Z unstable-options
+
+#![feature(rustc_attrs)]
+
+#[rustc_lint_diagnostics]
+//~^ ERROR attribute should be applied to a function
+struct Foo;
+
+impl Foo {
+ #[rustc_lint_diagnostics(a)]
+ //~^ ERROR malformed `rustc_lint_diagnostics`
+ fn bar() {}
+}
+
+fn main() {}
--- /dev/null
+error: malformed `rustc_lint_diagnostics` attribute input
+ --> $DIR/diagnostics_incorrect.rs:10:5
+ |
+LL | #[rustc_lint_diagnostics(a)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[rustc_lint_diagnostics]`
+
+error: attribute should be applied to a function
+ --> $DIR/diagnostics_incorrect.rs:5:1
+ |
+LL | #[rustc_lint_diagnostics]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |
+LL | struct Foo;
+ | ----------- not a function
+
+error: aborting due to 2 previous errors
+