Skip to content

Commit 370143f

Browse files
committed
Auto merge of #151816 - Zalathar:rollup-z5YytdB, r=Zalathar
Rollup of 5 pull requests Successful merges: - #151775 (Portable SIMD subtree update) - #151488 (Tweak E0599 to consolidate unsatisfied trait bound messages) - #149823 (fix(parser): Disallow CR in frontmatter ) - #151475 (add foregin type tests for issue 64458) - #151657 (Cleanup of `#[derive(Diagnostic)]` attribute parsers)
2 parents 80b8982 + 617288e commit 370143f

60 files changed

Lines changed: 1558 additions & 1714 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

compiler/rustc_hir_typeck/src/method/suggest.rs

Lines changed: 142 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
1313
use rustc_data_structures::sorted_map::SortedMap;
1414
use rustc_data_structures::unord::UnordSet;
1515
use rustc_errors::codes::*;
16-
use rustc_errors::{Applicability, Diag, MultiSpan, StashKey, pluralize, struct_span_code_err};
16+
use rustc_errors::{
17+
Applicability, Diag, MultiSpan, StashKey, listify, pluralize, struct_span_code_err,
18+
};
1719
use rustc_hir::attrs::AttributeKind;
1820
use rustc_hir::def::{CtorKind, DefKind, Res};
1921
use rustc_hir::def_id::DefId;
@@ -50,6 +52,51 @@ use crate::errors::{self, CandidateTraitNote, NoAssociatedItem};
5052
use crate::method::probe::UnsatisfiedPredicates;
5153
use crate::{Expectation, FnCtxt};
5254

55+
/// Tracks trait bounds and detects duplicates between ref and non-ref versions of self types.
56+
/// This is used to condense error messages when the same trait bound appears for both
57+
/// `T` and `&T` (or `&mut T`).
58+
struct TraitBoundDuplicateTracker {
59+
trait_def_ids: FxIndexSet<DefId>,
60+
seen_ref: FxIndexSet<DefId>,
61+
seen_non_ref: FxIndexSet<DefId>,
62+
has_ref_dupes: bool,
63+
}
64+
65+
impl TraitBoundDuplicateTracker {
66+
fn new() -> Self {
67+
Self {
68+
trait_def_ids: FxIndexSet::default(),
69+
seen_ref: FxIndexSet::default(),
70+
seen_non_ref: FxIndexSet::default(),
71+
has_ref_dupes: false,
72+
}
73+
}
74+
75+
/// Track a trait bound. `is_ref` indicates whether the self type is a reference.
76+
fn track(&mut self, def_id: DefId, is_ref: bool) {
77+
self.trait_def_ids.insert(def_id);
78+
if is_ref {
79+
if self.seen_non_ref.contains(&def_id) {
80+
self.has_ref_dupes = true;
81+
}
82+
self.seen_ref.insert(def_id);
83+
} else {
84+
if self.seen_ref.contains(&def_id) {
85+
self.has_ref_dupes = true;
86+
}
87+
self.seen_non_ref.insert(def_id);
88+
}
89+
}
90+
91+
fn has_ref_dupes(&self) -> bool {
92+
self.has_ref_dupes
93+
}
94+
95+
fn into_trait_def_ids(self) -> FxIndexSet<DefId> {
96+
self.trait_def_ids
97+
}
98+
}
99+
53100
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
54101
fn is_slice_ty(&self, ty: Ty<'tcx>, span: Span) -> bool {
55102
self.autoderef(span, ty)
@@ -1004,6 +1051,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
10041051
item_ident: Ident,
10051052
item_kind: &str,
10061053
bound_spans: SortedMap<Span, Vec<String>>,
1054+
unsatisfied_predicates: &UnsatisfiedPredicates<'tcx>,
10071055
) {
10081056
let mut ty_span = match rcvr_ty.kind() {
10091057
ty::Param(param_type) => {
@@ -1012,13 +1060,61 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
10121060
ty::Adt(def, _) if def.did().is_local() => Some(self.tcx.def_span(def.did())),
10131061
_ => None,
10141062
};
1063+
let rcvr_ty_str = self.tcx.short_string(rcvr_ty, err.long_ty_path());
1064+
let mut tracker = TraitBoundDuplicateTracker::new();
1065+
for (predicate, _parent_pred, _cause) in unsatisfied_predicates {
1066+
if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) =
1067+
predicate.kind().skip_binder()
1068+
&& let self_ty = pred.trait_ref.self_ty()
1069+
&& self_ty.peel_refs() == rcvr_ty
1070+
{
1071+
let is_ref = matches!(self_ty.kind(), ty::Ref(..));
1072+
tracker.track(pred.trait_ref.def_id, is_ref);
1073+
}
1074+
}
1075+
let has_ref_dupes = tracker.has_ref_dupes();
1076+
let mut missing_trait_names = tracker
1077+
.into_trait_def_ids()
1078+
.into_iter()
1079+
.map(|def_id| format!("`{}`", self.tcx.def_path_str(def_id)))
1080+
.collect::<Vec<_>>();
1081+
missing_trait_names.sort();
1082+
let should_condense =
1083+
has_ref_dupes && missing_trait_names.len() > 1 && matches!(rcvr_ty.kind(), ty::Adt(..));
1084+
let missing_trait_list = if should_condense {
1085+
Some(match missing_trait_names.as_slice() {
1086+
[only] => only.clone(),
1087+
[first, second] => format!("{first} or {second}"),
1088+
[rest @ .., last] => format!("{} or {last}", rest.join(", ")),
1089+
[] => String::new(),
1090+
})
1091+
} else {
1092+
None
1093+
};
10151094
for (span, mut bounds) in bound_spans {
10161095
if !self.tcx.sess.source_map().is_span_accessible(span) {
10171096
continue;
10181097
}
10191098
bounds.sort();
10201099
bounds.dedup();
1021-
let pre = if Some(span) == ty_span {
1100+
let is_ty_span = Some(span) == ty_span;
1101+
if is_ty_span && should_condense {
1102+
ty_span.take();
1103+
let label = if let Some(missing_trait_list) = &missing_trait_list {
1104+
format!(
1105+
"{item_kind} `{item_ident}` not found for this {} because `{rcvr_ty_str}` doesn't implement {missing_trait_list}",
1106+
rcvr_ty.prefix_string(self.tcx)
1107+
)
1108+
} else {
1109+
format!(
1110+
"{item_kind} `{item_ident}` not found for this {}",
1111+
rcvr_ty.prefix_string(self.tcx)
1112+
)
1113+
};
1114+
err.span_label(span, label);
1115+
continue;
1116+
}
1117+
let pre = if is_ty_span {
10221118
ty_span.take();
10231119
format!(
10241120
"{item_kind} `{item_ident}` not found for this {} because it ",
@@ -1248,6 +1344,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
12481344
item_ident,
12491345
item_kind,
12501346
bound_spans,
1347+
unsatisfied_predicates,
12511348
);
12521349

12531350
self.note_derefed_ty_has_method(&mut err, source, rcvr_ty, item_ident, expected);
@@ -1507,6 +1604,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
15071604
bound_spans: &mut SortedMap<Span, Vec<String>>,
15081605
) {
15091606
let tcx = self.tcx;
1607+
let rcvr_ty_str = self.tcx.short_string(rcvr_ty, err.long_ty_path());
15101608
let mut type_params = FxIndexMap::default();
15111609

15121610
// Pick out the list of unimplemented traits on the receiver.
@@ -1798,14 +1896,55 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
17981896
let mut spanned_predicates: Vec<_> = spanned_predicates.into_iter().collect();
17991897
spanned_predicates.sort_by_key(|(span, _)| *span);
18001898
for (_, (primary_spans, span_labels, predicates)) in spanned_predicates {
1899+
let mut tracker = TraitBoundDuplicateTracker::new();
1900+
let mut all_trait_bounds_for_rcvr = true;
1901+
for pred in &predicates {
1902+
match pred.kind().skip_binder() {
1903+
ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => {
1904+
let self_ty = pred.trait_ref.self_ty();
1905+
if self_ty.peel_refs() != rcvr_ty {
1906+
all_trait_bounds_for_rcvr = false;
1907+
break;
1908+
}
1909+
let is_ref = matches!(self_ty.kind(), ty::Ref(..));
1910+
tracker.track(pred.trait_ref.def_id, is_ref);
1911+
}
1912+
_ => {
1913+
all_trait_bounds_for_rcvr = false;
1914+
break;
1915+
}
1916+
}
1917+
}
1918+
let has_ref_dupes = tracker.has_ref_dupes();
1919+
let trait_def_ids = tracker.into_trait_def_ids();
18011920
let mut preds: Vec<_> = predicates
18021921
.iter()
18031922
.filter_map(|pred| format_pred(**pred))
18041923
.map(|(p, _)| format!("`{p}`"))
18051924
.collect();
18061925
preds.sort();
18071926
preds.dedup();
1808-
let msg = if let [pred] = &preds[..] {
1927+
let availability_note = if all_trait_bounds_for_rcvr
1928+
&& has_ref_dupes
1929+
&& trait_def_ids.len() > 1
1930+
&& matches!(rcvr_ty.kind(), ty::Adt(..))
1931+
{
1932+
let mut trait_names = trait_def_ids
1933+
.into_iter()
1934+
.map(|def_id| format!("`{}`", tcx.def_path_str(def_id)))
1935+
.collect::<Vec<_>>();
1936+
trait_names.sort();
1937+
listify(&trait_names, |name| name.to_string()).map(|traits| {
1938+
format!(
1939+
"for `{item_ident}` to be available, `{rcvr_ty_str}` must implement {traits}"
1940+
)
1941+
})
1942+
} else {
1943+
None
1944+
};
1945+
let msg = if let Some(availability_note) = availability_note {
1946+
availability_note
1947+
} else if let [pred] = &preds[..] {
18091948
format!("trait bound {pred} was not satisfied")
18101949
} else {
18111950
format!("the following trait bounds were not satisfied:\n{}", preds.join("\n"),)

compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs

Lines changed: 45 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
use proc_macro2::{Ident, Span, TokenStream};
44
use quote::{format_ident, quote, quote_spanned};
5+
use syn::parse::ParseStream;
56
use syn::spanned::Spanned;
67
use syn::{Attribute, Meta, Path, Token, Type, parse_quote};
78
use synstructure::{BindingInfo, Structure, VariantInfo};
@@ -11,7 +12,7 @@ use crate::diagnostics::error::{
1112
DiagnosticDeriveError, span_err, throw_invalid_attr, throw_span_err,
1213
};
1314
use crate::diagnostics::utils::{
14-
FieldInfo, FieldInnerTy, FieldMap, HasFieldMap, SetOnce, SpannedOption, SubdiagnosticKind,
15+
FieldInfo, FieldInnerTy, FieldMap, SetOnce, SpannedOption, SubdiagnosticKind,
1516
build_field_mapping, is_doc_comment, report_error_if_not_applied_to_span, report_type_error,
1617
should_generate_arg, type_is_bool, type_is_unit, type_matches_path,
1718
};
@@ -42,19 +43,13 @@ pub(crate) struct DiagnosticDeriveVariantBuilder {
4243

4344
/// Slug is a mandatory part of the struct attribute as corresponds to the Fluent message that
4445
/// has the actual diagnostic message.
45-
pub slug: SpannedOption<Path>,
46+
pub slug: Option<Path>,
4647

4748
/// Error codes are a optional part of the struct attribute - this is only set to detect
4849
/// multiple specifications.
4950
pub code: SpannedOption<()>,
5051
}
5152

52-
impl HasFieldMap for DiagnosticDeriveVariantBuilder {
53-
fn get_field_binding(&self, field: &String) -> Option<&TokenStream> {
54-
self.field_map.get(field)
55-
}
56-
}
57-
5853
impl DiagnosticDeriveKind {
5954
/// Call `f` for the struct or for each variant of the enum, returning a `TokenStream` with the
6055
/// tokens from `f` wrapped in an `match` expression. Emits errors for use of derive on unions
@@ -111,7 +106,7 @@ impl DiagnosticDeriveKind {
111106

112107
impl DiagnosticDeriveVariantBuilder {
113108
pub(crate) fn primary_message(&self) -> Option<&Path> {
114-
match self.slug.value_ref() {
109+
match self.slug.as_ref() {
115110
None => {
116111
span_err(self.span, "diagnostic slug not specified")
117112
.help(
@@ -169,7 +164,7 @@ impl DiagnosticDeriveVariantBuilder {
169164
&self,
170165
attr: &Attribute,
171166
) -> Result<Option<(SubdiagnosticKind, Path, bool)>, DiagnosticDeriveError> {
172-
let Some(subdiag) = SubdiagnosticVariant::from_attr(attr, self)? else {
167+
let Some(subdiag) = SubdiagnosticVariant::from_attr(attr, &self.field_map)? else {
173168
// Some attributes aren't errors - like documentation comments - but also aren't
174169
// subdiagnostics.
175170
return Ok(None);
@@ -191,7 +186,7 @@ impl DiagnosticDeriveVariantBuilder {
191186
SubdiagnosticKind::MultipartSuggestion { .. } => unreachable!(),
192187
});
193188

194-
Ok(Some((subdiag.kind, slug, subdiag.no_span)))
189+
Ok(Some((subdiag.kind, slug, false)))
195190
}
196191

197192
/// Establishes state in the `DiagnosticDeriveBuilder` resulting from the struct
@@ -209,47 +204,54 @@ impl DiagnosticDeriveVariantBuilder {
209204
let name = attr.path().segments.last().unwrap().ident.to_string();
210205
let name = name.as_str();
211206

212-
let mut first = true;
213-
214207
if name == "diag" {
215208
let mut tokens = TokenStream::new();
216-
attr.parse_nested_meta(|nested| {
217-
let path = &nested.path;
209+
attr.parse_args_with(|input: ParseStream<'_>| {
210+
let mut input = &*input;
211+
let slug_recovery_point = input.fork();
218212

219-
if first && (nested.input.is_empty() || nested.input.peek(Token![,])) {
220-
self.slug.set_once(path.clone(), path.span().unwrap());
221-
first = false;
222-
return Ok(());
213+
let slug = input.parse::<Path>()?;
214+
if input.is_empty() || input.peek(Token![,]) {
215+
self.slug = Some(slug);
216+
} else {
217+
input = &slug_recovery_point;
223218
}
224219

225-
first = false;
226-
227-
let Ok(nested) = nested.value() else {
228-
span_err(
229-
nested.input.span().unwrap(),
230-
"diagnostic slug must be the first argument",
231-
)
232-
.emit();
233-
return Ok(());
234-
};
235-
236-
if path.is_ident("code") {
237-
self.code.set_once((), path.span().unwrap());
238-
239-
let code = nested.parse::<syn::Expr>()?;
240-
tokens.extend(quote! {
241-
diag.code(#code);
242-
});
243-
} else {
244-
span_err(path.span().unwrap(), "unknown argument")
245-
.note("only the `code` parameter is valid after the slug")
220+
while !input.is_empty() {
221+
input.parse::<Token![,]>()?;
222+
// Allow trailing comma
223+
if input.is_empty() {
224+
break;
225+
}
226+
let arg_name: Path = input.parse::<Path>()?;
227+
if input.peek(Token![,]) {
228+
span_err(
229+
arg_name.span().unwrap(),
230+
"diagnostic slug must be the first argument",
231+
)
246232
.emit();
247-
248-
// consume the buffer so we don't have syntax errors from syn
249-
let _ = nested.parse::<TokenStream>();
233+
continue;
234+
}
235+
let arg_name = arg_name.require_ident()?;
236+
input.parse::<Token![=]>()?;
237+
let arg_value = input.parse::<syn::Expr>()?;
238+
match arg_name.to_string().as_str() {
239+
"code" => {
240+
self.code.set_once((), arg_name.span().unwrap());
241+
tokens.extend(quote! {
242+
diag.code(#arg_value);
243+
});
244+
}
245+
_ => {
246+
span_err(arg_name.span().unwrap(), "unknown argument")
247+
.note("only the `code` parameter is valid after the slug")
248+
.emit();
249+
}
250+
}
250251
}
251252
Ok(())
252253
})?;
254+
253255
return Ok(tokens);
254256
}
255257

0 commit comments

Comments
 (0)