Skip to content

Commit 24ba82d

Browse files
Rollup merge of rust-lang#111170 - compiler-errors:diag-doc, r=petrochenkov
Diagnostic args are still args if they're documented Fixes https://rust-lang.zulipchat.com/#narrow/stream/336883-i18n/topic/.60.23.5Bderive.28Diagnostic.29.5D.60.20works.20badly.20with.20docs/near/355597997 There's a lot of really strange code incongruencies between `Diagnostic` and `Subdiagnostic` derive. Perhaps those macros need some more overhaul, but I didn't really want to do it today.
2 parents beb4967 + a156bd7 commit 24ba82d

File tree

7 files changed

+116
-25
lines changed

7 files changed

+116
-25
lines changed

compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::diagnostics::utils::{
99
FieldInnerTy, FieldMap, HasFieldMap, SetOnce, SpannedOption, SubdiagnosticKind,
1010
};
1111
use proc_macro2::{Ident, Span, TokenStream};
12-
use quote::{format_ident, quote};
12+
use quote::{format_ident, quote, quote_spanned};
1313
use syn::Token;
1414
use syn::{parse_quote, spanned::Spanned, Attribute, Meta, Path, Type};
1515
use synstructure::{BindingInfo, Structure, VariantInfo};
@@ -251,7 +251,8 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
251251
let diag = &self.parent.diag;
252252

253253
let field = binding_info.ast();
254-
let field_binding = &binding_info.binding;
254+
let mut field_binding = binding_info.binding.clone();
255+
field_binding.set_span(field.ty.span());
255256

256257
let ident = field.ident.as_ref().unwrap();
257258
let ident = format_ident!("{}", ident); // strip `r#` prefix, if present
@@ -284,9 +285,9 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
284285
name == "primary_span" && matches!(inner_ty, FieldInnerTy::Vec(_));
285286
let (binding, needs_destructure) = if needs_clone {
286287
// `primary_span` can accept a `Vec<Span>` so don't destructure that.
287-
(quote! { #field_binding.clone() }, false)
288+
(quote_spanned! {inner_ty.span()=> #field_binding.clone() }, false)
288289
} else {
289-
(quote! { #field_binding }, true)
290+
(quote_spanned! {inner_ty.span()=> #field_binding }, true)
290291
};
291292

292293
let generated_code = self

compiler/rustc_macros/src/diagnostics/subdiagnostic.rs

+17-16
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,16 @@ use crate::diagnostics::error::{
44
invalid_attr, span_err, throw_invalid_attr, throw_span_err, DiagnosticDeriveError,
55
};
66
use crate::diagnostics::utils::{
7-
build_field_mapping, is_doc_comment, new_code_ident,
8-
report_error_if_not_applied_to_applicability, report_error_if_not_applied_to_span, FieldInfo,
9-
FieldInnerTy, FieldMap, HasFieldMap, SetOnce, SpannedOption, SubdiagnosticKind,
7+
build_field_mapping, build_suggestion_code, is_doc_comment, new_code_ident,
8+
report_error_if_not_applied_to_applicability, report_error_if_not_applied_to_span,
9+
should_generate_set_arg, AllowMultipleAlternatives, FieldInfo, FieldInnerTy, FieldMap,
10+
HasFieldMap, SetOnce, SpannedOption, SubdiagnosticKind,
1011
};
1112
use proc_macro2::TokenStream;
1213
use quote::{format_ident, quote};
1314
use syn::{spanned::Spanned, Attribute, Meta, MetaList, Path};
1415
use synstructure::{BindingInfo, Structure, VariantInfo};
1516

16-
use super::utils::{build_suggestion_code, AllowMultipleAlternatives};
17-
1817
/// The central struct for constructing the `add_to_diagnostic` method from an annotated struct.
1918
pub(crate) struct SubdiagnosticDeriveBuilder {
2019
diag: syn::Ident,
@@ -210,19 +209,20 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
210209
}
211210

212211
/// Generates the code for a field with no attributes.
213-
fn generate_field_set_arg(&mut self, binding: &BindingInfo<'_>) -> TokenStream {
214-
let ast = binding.ast();
215-
assert_eq!(ast.attrs.len(), 0, "field with attribute used as diagnostic arg");
216-
212+
fn generate_field_set_arg(&mut self, binding_info: &BindingInfo<'_>) -> TokenStream {
217213
let diag = &self.parent.diag;
218-
let ident = ast.ident.as_ref().unwrap();
219-
// strip `r#` prefix, if present
220-
let ident = format_ident!("{}", ident);
214+
215+
let field = binding_info.ast();
216+
let mut field_binding = binding_info.binding.clone();
217+
field_binding.set_span(field.ty.span());
218+
219+
let ident = field.ident.as_ref().unwrap();
220+
let ident = format_ident!("{}", ident); // strip `r#` prefix, if present
221221

222222
quote! {
223223
#diag.set_arg(
224224
stringify!(#ident),
225-
#binding
225+
#field_binding
226226
);
227227
}
228228
}
@@ -399,7 +399,8 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
399399
clone_suggestion_code: bool,
400400
) -> Result<TokenStream, DiagnosticDeriveError> {
401401
let span = attr.span().unwrap();
402-
let ident = &list.path.segments.last().unwrap().ident;
402+
let mut ident = list.path.segments.last().unwrap().ident.clone();
403+
ident.set_span(info.ty.span());
403404
let name = ident.to_string();
404405
let name = name.as_str();
405406

@@ -498,7 +499,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
498499
.variant
499500
.bindings()
500501
.iter()
501-
.filter(|binding| !binding.ast().attrs.is_empty())
502+
.filter(|binding| !should_generate_set_arg(binding.ast()))
502503
.map(|binding| self.generate_field_attr_code(binding, kind_stats))
503504
.collect();
504505

@@ -580,7 +581,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
580581
.variant
581582
.bindings()
582583
.iter()
583-
.filter(|binding| binding.ast().attrs.is_empty())
584+
.filter(|binding| should_generate_set_arg(binding.ast()))
584585
.map(|binding| self.generate_field_set_arg(binding))
585586
.collect();
586587

compiler/rustc_macros/src/diagnostics/utils.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,12 @@ impl<'ty> FieldInnerTy<'ty> {
207207
FieldInnerTy::Plain(..) => quote! { #inner },
208208
}
209209
}
210+
211+
pub fn span(&self) -> proc_macro2::Span {
212+
match self {
213+
FieldInnerTy::Option(ty) | FieldInnerTy::Vec(ty) | FieldInnerTy::Plain(ty) => ty.span(),
214+
}
215+
}
210216
}
211217

212218
/// Field information passed to the builder. Deliberately omits attrs to discourage the
@@ -851,7 +857,8 @@ impl quote::IdentFragment for SubdiagnosticKind {
851857
/// Returns `true` if `field` should generate a `set_arg` call rather than any other diagnostic
852858
/// call (like `span_label`).
853859
pub(super) fn should_generate_set_arg(field: &Field) -> bool {
854-
field.attrs.is_empty()
860+
// Perhaps this should be an exhaustive list...
861+
field.attrs.iter().all(|attr| is_doc_comment(attr))
855862
}
856863

857864
pub(super) fn is_doc_comment(attr: &Attribute) -> bool {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// check-fail
2+
// Tests that a doc comment will not preclude a field from being considered a diagnostic argument
3+
// normalize-stderr-test "the following other types implement trait `IntoDiagnosticArg`:(?:.*\n){0,9}\s+and \d+ others" -> "normalized in stderr"
4+
// normalize-stderr-test "diagnostic_builder\.rs:[0-9]+:[0-9]+" -> "diagnostic_builder.rs:LL:CC"
5+
6+
// The proc_macro2 crate handles spans differently when on beta/stable release rather than nightly,
7+
// changing the output of this test. Since Subdiagnostic is strictly internal to the compiler
8+
// the test is just ignored on stable and beta:
9+
// ignore-stage1
10+
// ignore-beta
11+
// ignore-stable
12+
13+
#![feature(rustc_private)]
14+
#![crate_type = "lib"]
15+
16+
extern crate rustc_errors;
17+
extern crate rustc_fluent_macro;
18+
extern crate rustc_macros;
19+
extern crate rustc_session;
20+
extern crate rustc_span;
21+
22+
use rustc_errors::{Applicability, DiagnosticMessage, SubdiagnosticMessage};
23+
use rustc_fluent_macro::fluent_messages;
24+
use rustc_macros::{Diagnostic, Subdiagnostic};
25+
use rustc_span::Span;
26+
27+
fluent_messages! { "./example.ftl" }
28+
29+
struct NotIntoDiagnosticArg;
30+
31+
#[derive(Diagnostic)]
32+
#[diag(no_crate_example)]
33+
struct Test {
34+
#[primary_span]
35+
span: Span,
36+
/// A doc comment
37+
arg: NotIntoDiagnosticArg,
38+
//~^ ERROR the trait bound `NotIntoDiagnosticArg: IntoDiagnosticArg` is not satisfied
39+
}
40+
41+
#[derive(Subdiagnostic)]
42+
#[label(no_crate_example)]
43+
struct SubTest {
44+
#[primary_span]
45+
span: Span,
46+
/// A doc comment
47+
arg: NotIntoDiagnosticArg,
48+
//~^ ERROR the trait bound `NotIntoDiagnosticArg: IntoDiagnosticArg` is not satisfied
49+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
error[E0277]: the trait bound `NotIntoDiagnosticArg: IntoDiagnosticArg` is not satisfied
2+
--> $DIR/diagnostic-derive-doc-comment-field.rs:37:10
3+
|
4+
LL | #[derive(Diagnostic)]
5+
| ---------- required by a bound introduced by this call
6+
...
7+
LL | arg: NotIntoDiagnosticArg,
8+
| ^^^^^^^^^^^^^^^^^^^^ the trait `IntoDiagnosticArg` is not implemented for `NotIntoDiagnosticArg`
9+
|
10+
= help: normalized in stderr
11+
note: required by a bound in `DiagnosticBuilder::<'a, G>::set_arg`
12+
--> $COMPILER_DIR/rustc_errors/src/diagnostic_builder.rs:LL:CC
13+
= note: this error originates in the macro `forward` (in Nightly builds, run with -Z macro-backtrace for more info)
14+
15+
error[E0277]: the trait bound `NotIntoDiagnosticArg: IntoDiagnosticArg` is not satisfied
16+
--> $DIR/diagnostic-derive-doc-comment-field.rs:47:10
17+
|
18+
LL | #[derive(Subdiagnostic)]
19+
| ------------- required by a bound introduced by this call
20+
...
21+
LL | arg: NotIntoDiagnosticArg,
22+
| ^^^^^^^^^^^^^^^^^^^^ the trait `IntoDiagnosticArg` is not implemented for `NotIntoDiagnosticArg`
23+
|
24+
= help: normalized in stderr
25+
note: required by a bound in `Diagnostic::set_arg`
26+
--> $COMPILER_DIR/rustc_errors/src/diagnostic.rs:964:5
27+
28+
error: aborting due to 2 previous errors
29+
30+
For more information about this error, try `rustc --explain E0277`.

tests/ui-fulldeps/session-diagnostic/diagnostic-derive.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -339,12 +339,12 @@ struct ErrorWithDefaultLabelAttr<'a> {
339339
}
340340

341341
#[derive(Diagnostic)]
342-
//~^ ERROR the trait bound `Hello: IntoDiagnosticArg` is not satisfied
343342
#[diag(no_crate_example, code = "E0123")]
344343
struct ArgFieldWithoutSkip {
345344
#[primary_span]
346345
span: Span,
347346
other: Hello,
347+
//~^ ERROR the trait bound `Hello: IntoDiagnosticArg` is not satisfied
348348
}
349349

350350
#[derive(Diagnostic)]

tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr

+6-3
Original file line numberDiff line numberDiff line change
@@ -641,15 +641,18 @@ LL | #[derive(Diagnostic)]
641641
= note: this error originates in the derive macro `Diagnostic` (in Nightly builds, run with -Z macro-backtrace for more info)
642642

643643
error[E0277]: the trait bound `Hello: IntoDiagnosticArg` is not satisfied
644-
--> $DIR/diagnostic-derive.rs:341:10
644+
--> $DIR/diagnostic-derive.rs:346:12
645645
|
646646
LL | #[derive(Diagnostic)]
647-
| ^^^^^^^^^^ the trait `IntoDiagnosticArg` is not implemented for `Hello`
647+
| ---------- required by a bound introduced by this call
648+
...
649+
LL | other: Hello,
650+
| ^^^^^ the trait `IntoDiagnosticArg` is not implemented for `Hello`
648651
|
649652
= help: normalized in stderr
650653
note: required by a bound in `DiagnosticBuilder::<'a, G>::set_arg`
651654
--> $COMPILER_DIR/rustc_errors/src/diagnostic_builder.rs:LL:CC
652-
= note: this error originates in the derive macro `Diagnostic` which comes from the expansion of the macro `forward` (in Nightly builds, run with -Z macro-backtrace for more info)
655+
= note: this error originates in the macro `forward` (in Nightly builds, run with -Z macro-backtrace for more info)
653656

654657
error: aborting due to 84 previous errors
655658

0 commit comments

Comments
 (0)