Skip to content

rustc_macros: Make it possible to derive both Diagnostic and LintDiagnostic on the same type #125169

Closed as not planned

Description

In #117164 I had to copy the diagnostic structs TyParamFirstLocal and TyParamSome and define the separate structs TyParamFirstLocalLint and TyParamSomeLint to be able to use the translatable diagnostics hir_analysis_ty_param_first_local and hir_analysis_ty_param_some as both a Diagnostic and a LintDiagnostic:

#[derive(Diagnostic)]
#[diag(hir_analysis_ty_param_first_local, code = E0210)]
#[note]
pub struct TyParamFirstLocal<'tcx> {
#[primary_span]
#[label]
pub span: Span,
#[note(hir_analysis_case_note)]
pub note: (),
pub param: Symbol,
pub local_type: Ty<'tcx>,
}
#[derive(LintDiagnostic)]
#[diag(hir_analysis_ty_param_first_local, code = E0210)]
#[note]
pub struct TyParamFirstLocalLint<'tcx> {
#[label]
pub span: Span,
#[note(hir_analysis_case_note)]
pub note: (),
pub param: Symbol,
pub local_type: Ty<'tcx>,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_ty_param_some, code = E0210)]
#[note]
pub struct TyParamSome {
#[primary_span]
#[label]
pub span: Span,
#[note(hir_analysis_only_note)]
pub note: (),
pub param: Symbol,
}
#[derive(LintDiagnostic)]
#[diag(hir_analysis_ty_param_some, code = E0210)]
#[note]
pub struct TyParamSomeLint {
#[label]
pub span: Span,
#[note(hir_analysis_only_note)]
pub note: (),
pub param: Symbol,
}

In #116829, had to duplicate the lint diagnostic struct ReprConflicting and split it into the separate structs ReprConflicting and ReprConflictingLint to be able to use the translatable diagnostic passes_repr_conflicting as both a Diagnostic and a LintDiagnostic:

#[derive(Diagnostic)]
#[diag(passes_repr_conflicting, code = E0566)]
pub struct ReprConflicting {
#[primary_span]
pub hint_spans: Vec<Span>,
}
#[derive(LintDiagnostic)]
#[diag(passes_repr_conflicting, code = E0566)]
pub struct ReprConflictingLint;

In all three cases, I could've probably turned the derivation of LintDiagnostic into a manual impl but that doesn't seem very enticing either for various reasons.

For context, this situation arises whenever we want to emit a diagnostic for something but can't report a hard error unconditionally in all cases for backward compatibility and therefore have to emit the very same diagnostic at different “levels of severity” depending on a set of conditions. With level of severity I'm specifically referring to the set {hard error, lint} here (where lint has its own level of course).


More concretely, the reason why you can't just #[derive(Diagnostic, LintDiagnostic)] struct Ty/*...*/ is because of #[primary_span]: If the diagnostic struct contains a #[primary_span] (which it does most of the time) for the derive macro Diagnostic, then the derive macro LintDiagnostic will reject #[primary_span] rendering the two derives incompatible with one another.

Individually, LintDiagnostic rejecting #[primary_span] makes sense because the span(s) for a lint are to be provided to the function that emits the lint like emit_span_lint, emit_node_span_lint.


There are probably multiple ways to approach this but I'm not super familiar with internals of rustc's linting API. Anyways, I'd like to see this “just work” because it won't be the last time this case will occur.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

Labels

A-contributor-roadblockArea: Makes things more difficult for new contributors to rust itselfA-lintArea: Lints (warnings about flaws in source code) such as unused_mut.A-translationArea: Translation infrastructure, and migrating existing diagnostics to SessionDiagnosticC-enhancementCategory: An issue proposing an enhancement or a PR with one.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions