Rollup merge of #97948 - davidtwco:diagnostic-translation-lints, r=oli-obk
lint: add diagnostic translation migration lints Introduce allow-by-default lints for checking whether diagnostics are written in `SessionDiagnostic` or `AddSubdiagnostic` impls and whether diagnostics are translatable. These lints can be denied for modules once they are fully migrated to impls and translation. These lints are intended to be temporary - once all diagnostics have been changed then we can just change the APIs we have and that will enforce these constraints thereafter. r? `````@oli-obk`````
This commit is contained in:
commit
d8333a7b59
@ -1,6 +1,7 @@
|
||||
#![feature(let_chains)]
|
||||
#![feature(once_cell)]
|
||||
#![feature(path_try_exists)]
|
||||
#![feature(rustc_attrs)]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
use fluent_bundle::FluentResource;
|
||||
@ -241,6 +242,7 @@ type FluentId = Cow<'static, str>;
|
||||
/// 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?
|
||||
@ -281,6 +283,7 @@ impl<S: Into<String>> From<S> for SubdiagnosticMessage {
|
||||
///
|
||||
/// 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?
|
||||
|
@ -80,6 +80,7 @@ impl<'source> Into<FluentValue<'source>> for DiagnosticArgValue<'source> {
|
||||
|
||||
/// 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);
|
||||
@ -283,6 +284,7 @@ impl 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
|
||||
@ -401,6 +403,7 @@ impl Diagnostic {
|
||||
}
|
||||
|
||||
/// 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
|
||||
@ -423,6 +426,7 @@ impl Diagnostic {
|
||||
|
||||
/// 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,
|
||||
@ -444,6 +448,7 @@ impl Diagnostic {
|
||||
}
|
||||
|
||||
/// 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
|
||||
@ -451,6 +456,7 @@ impl Diagnostic {
|
||||
|
||||
/// 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,
|
||||
@ -461,6 +467,7 @@ impl Diagnostic {
|
||||
}
|
||||
|
||||
/// 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
|
||||
@ -474,6 +481,7 @@ impl Diagnostic {
|
||||
|
||||
/// 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,
|
||||
|
@ -9,6 +9,7 @@
|
||||
#![feature(let_else)]
|
||||
#![feature(never_type)]
|
||||
#![feature(adt_const_params)]
|
||||
#![feature(rustc_attrs)]
|
||||
#![allow(incomplete_features)]
|
||||
#![allow(rustc::potential_query_instability)]
|
||||
|
||||
@ -648,6 +649,7 @@ impl Handler {
|
||||
/// 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>,
|
||||
@ -659,6 +661,7 @@ impl Handler {
|
||||
}
|
||||
|
||||
/// 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>,
|
||||
@ -671,6 +674,7 @@ impl Handler {
|
||||
|
||||
/// 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>,
|
||||
@ -687,16 +691,19 @@ impl Handler {
|
||||
/// 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>,
|
||||
@ -706,6 +713,7 @@ impl Handler {
|
||||
}
|
||||
|
||||
/// 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>,
|
||||
@ -717,6 +725,7 @@ impl Handler {
|
||||
}
|
||||
|
||||
/// 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>,
|
||||
@ -730,6 +739,7 @@ impl Handler {
|
||||
|
||||
/// 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>,
|
||||
@ -744,6 +754,7 @@ impl Handler {
|
||||
}
|
||||
|
||||
/// 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>,
|
||||
@ -755,6 +766,7 @@ impl Handler {
|
||||
}
|
||||
|
||||
/// 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>,
|
||||
@ -766,6 +778,7 @@ impl Handler {
|
||||
}
|
||||
|
||||
/// 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>,
|
||||
@ -777,6 +790,7 @@ impl Handler {
|
||||
}
|
||||
|
||||
/// 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>,
|
||||
@ -789,16 +803,19 @@ impl Handler {
|
||||
}
|
||||
|
||||
/// 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>,
|
||||
@ -806,11 +823,13 @@ impl Handler {
|
||||
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>,
|
||||
@ -821,6 +840,7 @@ impl Handler {
|
||||
FatalError.raise()
|
||||
}
|
||||
|
||||
#[cfg_attr(not(bootstrap), rustc_lint_diagnostics)]
|
||||
pub fn span_err(
|
||||
&self,
|
||||
span: impl Into<MultiSpan>,
|
||||
@ -829,6 +849,7 @@ impl Handler {
|
||||
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>,
|
||||
@ -841,10 +862,12 @@ impl Handler {
|
||||
);
|
||||
}
|
||||
|
||||
#[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>,
|
||||
|
@ -615,6 +615,9 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
||||
// 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:
|
||||
|
@ -5,12 +5,14 @@ use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}
|
||||
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,
|
||||
@ -46,6 +48,41 @@ impl LateLintPass<'_> for DefaultHashTypes {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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,
|
||||
@ -57,35 +94,7 @@ declare_lint_pass!(QueryStability => [POTENTIAL_QUERY_INSTABILITY]);
|
||||
|
||||
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) {
|
||||
@ -376,3 +385,70 @@ impl<'tcx> LateLintPass<'tcx> for ExistingDocKeyword {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -508,6 +508,8 @@ fn register_internals(store: &mut LintStore) {
|
||||
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(
|
||||
|
@ -118,6 +118,9 @@ impl CheckAttrVisitor<'_> {
|
||||
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
|
||||
@ -1624,12 +1627,9 @@ impl CheckAttrVisitor<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
@ -1643,6 +1643,23 @@ impl CheckAttrVisitor<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
|
@ -5,6 +5,7 @@
|
||||
#![feature(never_type)]
|
||||
#![feature(once_cell)]
|
||||
#![feature(option_get_or_insert_default)]
|
||||
#![feature(rustc_attrs)]
|
||||
#![recursion_limit = "256"]
|
||||
#![allow(rustc::potential_query_instability)]
|
||||
|
||||
|
@ -311,6 +311,7 @@ impl ParseSess {
|
||||
self.create_warning(warning).emit()
|
||||
}
|
||||
|
||||
#[cfg_attr(not(bootstrap), rustc_lint_diagnostics)]
|
||||
pub fn struct_err(
|
||||
&self,
|
||||
msg: impl Into<DiagnosticMessage>,
|
||||
@ -318,6 +319,7 @@ impl ParseSess {
|
||||
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)
|
||||
}
|
||||
|
@ -209,6 +209,7 @@ pub struct PerfStats {
|
||||
|
||||
/// 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]
|
||||
|
@ -125,6 +125,7 @@ symbols! {
|
||||
Symbols {
|
||||
AcqRel,
|
||||
Acquire,
|
||||
AddSubdiagnostic,
|
||||
Alignment,
|
||||
Any,
|
||||
Arc,
|
||||
@ -169,6 +170,7 @@ symbols! {
|
||||
Decoder,
|
||||
Default,
|
||||
Deref,
|
||||
DiagnosticMessage,
|
||||
DirBuilder,
|
||||
Display,
|
||||
DoubleEndedIterator,
|
||||
@ -253,11 +255,13 @@ symbols! {
|
||||
RustcEncodable,
|
||||
Send,
|
||||
SeqCst,
|
||||
SessionDiagnostic,
|
||||
SliceIndex,
|
||||
Some,
|
||||
String,
|
||||
StructuralEq,
|
||||
StructuralPartialEq,
|
||||
SubdiagnosticMessage,
|
||||
Sync,
|
||||
Target,
|
||||
ToOwned,
|
||||
@ -1205,6 +1209,7 @@ symbols! {
|
||||
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,
|
||||
|
73
src/test/ui-fulldeps/internal-lints/diagnostics.rs
Normal file
73
src/test/ui-fulldeps/internal-lints/diagnostics.rs
Normal file
@ -0,0 +1,73 @@
|
||||
// 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
|
||||
}
|
44
src/test/ui-fulldeps/internal-lints/diagnostics.stderr
Normal file
44
src/test/ui-fulldeps/internal-lints/diagnostics.stderr
Normal file
@ -0,0 +1,44 @@
|
||||
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
|
||||
|
15
src/test/ui-fulldeps/internal-lints/diagnostics_incorrect.rs
Normal file
15
src/test/ui-fulldeps/internal-lints/diagnostics_incorrect.rs
Normal file
@ -0,0 +1,15 @@
|
||||
// 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() {}
|
@ -0,0 +1,17 @@
|
||||
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
|
||||
|
Loading…
x
Reference in New Issue
Block a user