Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Forbid #[suggestion_*(...)] on Vecs
It is ambiguous whether this should produce several `.span_suggestions()`
calls or one `.multipart_suggestions()` call.
  • Loading branch information
Xiretza committed Feb 1, 2023
commit f2acbb9d1eb5cb59199f16a608a291b4c884e967
21 changes: 13 additions & 8 deletions compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -322,11 +322,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
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(),
},
FieldInfo { binding: binding_info, ty: inner_ty, span: &field.span() },
binding,
)
.unwrap_or_else(|v| v.to_compile_error());
Expand Down Expand Up @@ -418,9 +414,9 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
Ok(self.add_spanned_subdiagnostic(binding, &fn_ident, slug))
}
SubdiagnosticKind::Note | SubdiagnosticKind::Help | SubdiagnosticKind::Warn => {
if type_matches_path(info.ty, &["rustc_span", "Span"]) {
if type_matches_path(info.ty.inner_type(), &["rustc_span", "Span"]) {
Ok(self.add_spanned_subdiagnostic(binding, &fn_ident, slug))
} else if type_is_unit(info.ty) {
} else if type_is_unit(info.ty.inner_type()) {
Ok(self.add_subdiagnostic(&fn_ident, slug))
} else {
report_type_error(attr, "`Span` or `()`")?
Expand All @@ -432,6 +428,15 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
code_field,
code_init,
} => {
if let FieldInnerTy::Vec(_) = info.ty {
throw_invalid_attr!(attr, &meta, |diag| {
diag
.note("`#[suggestion(...)]` applied to `Vec` field is ambiguous")
.help("to show a suggestion consisting of multiple parts, use a `Subdiagnostic` annotated with `#[multipart_suggestion(...)]`")
.help("to show a variable set of suggestions, use a `Vec` of `Subdiagnostic`s annotated with `#[suggestion(...)]`")
});
}

let (span_field, mut applicability) = self.span_and_applicability_of_ty(info)?;

if let Some((static_applicability, span)) = static_applicability {
Expand Down Expand Up @@ -489,7 +494,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
&self,
info: FieldInfo<'_>,
) -> Result<(TokenStream, SpannedOption<TokenStream>), DiagnosticDeriveError> {
match &info.ty {
match &info.ty.inner_type() {
// If `ty` is `Span` w/out applicability, then use `Applicability::Unspecified`.
ty @ Type::Path(..) if type_matches_path(ty, &["rustc_span", "Span"]) => {
let binding = &info.binding.binding;
Expand Down
21 changes: 16 additions & 5 deletions compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -247,11 +247,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
return quote! {};
}

let info = FieldInfo {
binding,
ty: inner_ty.inner_type().unwrap_or(&ast.ty),
span: &ast.span(),
};
let info = FieldInfo { binding, ty: inner_ty, span: &ast.span() };

let generated = self
.generate_field_code_inner(kind_stats, attr, info, inner_ty.will_iterate())
Expand Down Expand Up @@ -312,6 +308,21 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
let binding = info.binding.binding.clone();
// FIXME(#100717): support `Option<Span>` on `primary_span` like in the
// diagnostic derive
if !matches!(info.ty, FieldInnerTy::Plain(_)) {
throw_invalid_attr!(attr, &Meta::Path(path), |diag| {
let diag = diag.note("there must be exactly one primary span");

if kind_stats.has_normal_suggestion {
diag.help(
"to create a suggestion with multiple spans, \
use `#[multipart_suggestion]` instead",
)
} else {
diag
}
});
}

self.span_field.set_once(binding, span);
}

Expand Down
67 changes: 37 additions & 30 deletions compiler/rustc_macros/src/diagnostics/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ fn report_error_if_not_applied_to_ty(
path: &[&str],
ty_name: &str,
) -> Result<(), DiagnosticDeriveError> {
if !type_matches_path(info.ty, path) {
if !type_matches_path(info.ty.inner_type(), path) {
report_type_error(attr, ty_name)?;
}

Expand All @@ -105,8 +105,8 @@ pub(crate) fn report_error_if_not_applied_to_span(
attr: &Attribute,
info: &FieldInfo<'_>,
) -> Result<(), DiagnosticDeriveError> {
if !type_matches_path(info.ty, &["rustc_span", "Span"])
&& !type_matches_path(info.ty, &["rustc_errors", "MultiSpan"])
if !type_matches_path(info.ty.inner_type(), &["rustc_span", "Span"])
&& !type_matches_path(info.ty.inner_type(), &["rustc_errors", "MultiSpan"])
{
report_type_error(attr, "`Span` or `MultiSpan`")?;
}
Expand All @@ -115,60 +115,67 @@ pub(crate) fn report_error_if_not_applied_to_span(
}

/// Inner type of a field and type of wrapper.
#[derive(Copy, Clone)]
pub(crate) enum FieldInnerTy<'ty> {
/// Field is wrapped in a `Option<$inner>`.
Option(&'ty Type),
/// Field is wrapped in a `Vec<$inner>`.
Vec(&'ty Type),
/// Field isn't wrapped in an outer type.
None,
Plain(&'ty Type),
}

impl<'ty> FieldInnerTy<'ty> {
/// Returns inner type for a field, if there is one.
///
/// - If `ty` is an `Option`, returns `FieldInnerTy::Option { inner: (inner type) }`.
/// - If `ty` is a `Vec`, returns `FieldInnerTy::Vec { inner: (inner type) }`.
/// - Otherwise returns `None`.
/// - If `ty` is an `Option<Inner>`, returns `FieldInnerTy::Option(Inner)`.
/// - If `ty` is a `Vec<Inner>`, returns `FieldInnerTy::Vec(Inner)`.
/// - Otherwise returns `FieldInnerTy::Plain(ty)`.
pub(crate) fn from_type(ty: &'ty Type) -> Self {
let variant: &dyn Fn(&'ty Type) -> FieldInnerTy<'ty> =
if type_matches_path(ty, &["std", "option", "Option"]) {
&FieldInnerTy::Option
} else if type_matches_path(ty, &["std", "vec", "Vec"]) {
&FieldInnerTy::Vec
} else {
return FieldInnerTy::None;
fn single_generic_type(ty: &Type) -> &Type {
let Type::Path(ty_path) = ty else {
panic!("expected path type");
};

if let Type::Path(ty_path) = ty {
let path = &ty_path.path;
let ty = path.segments.iter().last().unwrap();
if let syn::PathArguments::AngleBracketed(bracketed) = &ty.arguments {
if bracketed.args.len() == 1 {
if let syn::GenericArgument::Type(ty) = &bracketed.args[0] {
return variant(ty);
}
}
}
let syn::PathArguments::AngleBracketed(bracketed) = &ty.arguments else {
panic!("expected bracketed generic arguments");
};

assert_eq!(bracketed.args.len(), 1);

let syn::GenericArgument::Type(ty) = &bracketed.args[0] else {
panic!("expected generic parameter to be a type generic");
};

ty
}

unreachable!();
if type_matches_path(ty, &["std", "option", "Option"]) {
FieldInnerTy::Option(single_generic_type(ty))
} else if type_matches_path(ty, &["std", "vec", "Vec"]) {
FieldInnerTy::Vec(single_generic_type(ty))
} else {
FieldInnerTy::Plain(ty)
}
}

/// Returns `true` if `FieldInnerTy::with` will result in iteration for this inner type (i.e.
/// that cloning might be required for values moved in the loop body).
pub(crate) fn will_iterate(&self) -> bool {
match self {
FieldInnerTy::Vec(..) => true,
FieldInnerTy::Option(..) | FieldInnerTy::None => false,
FieldInnerTy::Option(..) | FieldInnerTy::Plain(_) => false,
}
}

/// Returns `Option` containing inner type if there is one.
pub(crate) fn inner_type(&self) -> Option<&'ty Type> {
/// Returns the inner type.
pub(crate) fn inner_type(&self) -> &'ty Type {
match self {
FieldInnerTy::Option(inner) | FieldInnerTy::Vec(inner) => Some(inner),
FieldInnerTy::None => None,
FieldInnerTy::Option(inner) | FieldInnerTy::Vec(inner) | FieldInnerTy::Plain(inner) => {
inner
}
}
}

Expand All @@ -185,7 +192,7 @@ impl<'ty> FieldInnerTy<'ty> {
#inner
}
},
FieldInnerTy::None => quote! { #inner },
FieldInnerTy::Plain(..) => quote! { #inner },
}
}
}
Expand All @@ -194,7 +201,7 @@ impl<'ty> FieldInnerTy<'ty> {
/// `generate_*` methods from walking the attributes themselves.
pub(crate) struct FieldInfo<'a> {
pub(crate) binding: &'a BindingInfo<'a>,
pub(crate) ty: &'a Type,
pub(crate) ty: FieldInnerTy<'a>,
pub(crate) span: &'a proc_macro2::Span,
}

Expand Down
8 changes: 8 additions & 0 deletions tests/ui-fulldeps/session-diagnostic/diagnostic-derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -799,3 +799,11 @@ struct SuggestionStyleGood {
#[suggestion(code = "", style = "hidden")]
sub: Span,
}

#[derive(Diagnostic)]
#[diag(compiletest_example)]
struct SuggestionOnVec {
#[suggestion(suggestion, code = "")]
//~^ ERROR `#[suggestion(...)]` is not a valid attribute
sub: Vec<Span>,
}
12 changes: 11 additions & 1 deletion tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,16 @@ error: `code = "..."`/`code(...)` must contain only string literals
LL | #[suggestion(code = 3)]
| ^^^^^^^^

error: `#[suggestion(...)]` is not a valid attribute
--> $DIR/diagnostic-derive.rs:806:5
|
LL | #[suggestion(suggestion, code = "")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[suggestion(...)]` applied to `Vec` field is ambiguous
= help: to show a suggestion consisting of multiple parts, use a `Subdiagnostic` annotated with `#[multipart_suggestion(...)]`
= help: to show a variable set of suggestions, use a `Vec` of `Subdiagnostic`s annotated with `#[suggestion(...)]`

error: cannot find attribute `nonsense` in this scope
--> $DIR/diagnostic-derive.rs:55:3
|
Expand Down Expand Up @@ -660,7 +670,7 @@ note: required by a bound in `DiagnosticBuilder::<'a, G>::set_arg`
--> $COMPILER_DIR/rustc_errors/src/diagnostic_builder.rs:LL:CC
= 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)

error: aborting due to 83 previous errors
error: aborting due to 84 previous errors

Some errors have detailed explanations: E0277, E0425.
For more information about an error, try `rustc --explain E0277`.
10 changes: 10 additions & 0 deletions tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -798,3 +798,13 @@ struct SuggestionStyleInvalid4 {
#[primary_span]
sub: Span,
}

#[derive(Subdiagnostic)]
#[suggestion(parse_add_paren, code = "")]
//~^ ERROR suggestion without `#[primary_span]` field
struct PrimarySpanOnVec {
#[primary_span]
//~^ ERROR `#[primary_span]` is not a valid attribute
//~| NOTE there must be exactly one primary span
sub: Vec<Span>,
}
23 changes: 22 additions & 1 deletion tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,27 @@ error: `#[suggestion(style(...))]` is not a valid attribute
LL | #[suggestion(parse_add_paren, code = "", style("foo"))]
| ^^^^^^^^^^^^

error: `#[primary_span]` is not a valid attribute
--> $DIR/subdiagnostic-derive.rs:806:5
|
LL | #[primary_span]
| ^^^^^^^^^^^^^^^
|
= note: there must be exactly one primary span
= help: to create a suggestion with multiple spans, use `#[multipart_suggestion]` instead

error: suggestion without `#[primary_span]` field
--> $DIR/subdiagnostic-derive.rs:803:1
|
LL | / #[suggestion(parse_add_paren, code = "")]
LL | |
LL | | struct PrimarySpanOnVec {
LL | | #[primary_span]
... |
LL | | sub: Vec<Span>,
LL | | }
| |_^

error: cannot find attribute `foo` in this scope
--> $DIR/subdiagnostic-derive.rs:63:3
|
Expand Down Expand Up @@ -561,6 +582,6 @@ error[E0425]: cannot find value `slug` in module `rustc_errors::fluent`
LL | #[label(slug)]
| ^^^^ not found in `rustc_errors::fluent`

error: aborting due to 79 previous errors
error: aborting due to 81 previous errors

For more information about this error, try `rustc --explain E0425`.