Skip to content

Commit efec545

Browse files
committed
Auto merge of rust-lang#91517 - matthiaskrgr:rollup-3fmp4go, r=matthiaskrgr
Rollup of 3 pull requests Successful merges: - rust-lang#87054 (Add a `try_reduce` method to the Iterator trait) - rust-lang#89701 (Updated error message for accidental uses of derive attribute as a crate attribute) - rust-lang#90519 (Keep spans for generics in `#[derive(_)]` desugaring) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
2 parents 887999d + 0311cfa commit efec545

29 files changed

+731
-148
lines changed

compiler/rustc_builtin_macros/src/deriving/generic/mod.rs

+76-78
Large diffs are not rendered by default.

compiler/rustc_builtin_macros/src/deriving/generic/ty.rs

+6-10
Original file line numberDiff line numberDiff line change
@@ -211,14 +211,6 @@ fn mk_ty_param(
211211
cx.typaram(span, Ident::new(name, span), attrs.to_owned(), bounds, None)
212212
}
213213

214-
fn mk_generics(params: Vec<ast::GenericParam>, span: Span) -> Generics {
215-
Generics {
216-
params,
217-
where_clause: ast::WhereClause { has_where_token: false, predicates: Vec::new(), span },
218-
span,
219-
}
220-
}
221-
222214
/// Bounds on type parameters.
223215
#[derive(Clone)]
224216
pub struct Bounds {
@@ -236,7 +228,7 @@ impl Bounds {
236228
self_ty: Ident,
237229
self_generics: &Generics,
238230
) -> Generics {
239-
let generic_params = self
231+
let params = self
240232
.bounds
241233
.iter()
242234
.map(|t| {
@@ -245,7 +237,11 @@ impl Bounds {
245237
})
246238
.collect();
247239

248-
mk_generics(generic_params, span)
240+
Generics {
241+
params,
242+
where_clause: ast::WhereClause { has_where_token: false, predicates: Vec::new(), span },
243+
span,
244+
}
249245
}
250246
}
251247

compiler/rustc_hir/src/hir.rs

+15-8
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@ use rustc_index::vec::IndexVec;
1717
use rustc_macros::HashStable_Generic;
1818
use rustc_span::source_map::Spanned;
1919
use rustc_span::symbol::{kw, sym, Ident, Symbol};
20-
use rustc_span::{def_id::LocalDefId, BytePos};
21-
use rustc_span::{MultiSpan, Span, DUMMY_SP};
20+
use rustc_span::{def_id::LocalDefId, BytePos, MultiSpan, Span, DUMMY_SP};
2221
use rustc_target::asm::InlineAsmRegOrRegClass;
2322
use rustc_target::spec::abi::Abi;
2423

@@ -526,12 +525,20 @@ pub struct GenericParam<'hir> {
526525
}
527526

528527
impl GenericParam<'hir> {
529-
pub fn bounds_span(&self) -> Option<Span> {
530-
self.bounds.iter().fold(None, |span, bound| {
531-
let span = span.map(|s| s.to(bound.span())).unwrap_or_else(|| bound.span());
532-
533-
Some(span)
534-
})
528+
pub fn bounds_span_for_suggestions(&self) -> Option<Span> {
529+
self.bounds
530+
.iter()
531+
.fold(None, |span: Option<Span>, bound| {
532+
// We include bounds that come from a `#[derive(_)]` but point at the user's code,
533+
// as we use this method to get a span appropriate for suggestions.
534+
if !bound.span().can_be_used_for_suggestions() {
535+
None
536+
} else {
537+
let span = span.map(|s| s.to(bound.span())).unwrap_or_else(|| bound.span());
538+
Some(span)
539+
}
540+
})
541+
.map(|sp| sp.shrink_to_hi())
535542
}
536543
}
537544

compiler/rustc_middle/src/ty/diagnostics.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ pub fn suggest_constraining_type_param(
270270
// `where` clause instead of `trait Base<T: Copy = String>: Super<T>`.
271271
&& !matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. })
272272
{
273-
if let Some(bounds_span) = param.bounds_span() {
273+
if let Some(span) = param.bounds_span_for_suggestions() {
274274
// If user has provided some bounds, suggest restricting them:
275275
//
276276
// fn foo<T: Foo>(t: T) { ... }
@@ -284,7 +284,7 @@ pub fn suggest_constraining_type_param(
284284
// --
285285
// |
286286
// replace with: `T: Bar +`
287-
suggest_restrict(bounds_span.shrink_to_hi());
287+
suggest_restrict(span);
288288
} else {
289289
// If user hasn't provided any bounds, suggest adding a new one:
290290
//

compiler/rustc_passes/src/check_attr.rs

+30-6
Original file line numberDiff line numberDiff line change
@@ -1953,28 +1953,52 @@ fn is_c_like_enum(item: &Item<'_>) -> bool {
19531953
}
19541954
}
19551955

1956+
// FIXME: Fix "Cannot determine resolution" error and remove built-in macros
1957+
// from this check.
19561958
fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
1959+
// Check for builtin attributes at the crate level
1960+
// which were unsuccessfully resolved due to cannot determine
1961+
// resolution for the attribute macro error.
19571962
const ATTRS_TO_CHECK: &[Symbol] = &[
19581963
sym::macro_export,
19591964
sym::repr,
19601965
sym::path,
19611966
sym::automatically_derived,
19621967
sym::start,
19631968
sym::rustc_main,
1969+
sym::derive,
1970+
sym::test,
1971+
sym::test_case,
1972+
sym::global_allocator,
1973+
sym::bench,
19641974
];
19651975

19661976
for attr in attrs {
1967-
for attr_to_check in ATTRS_TO_CHECK {
1968-
if attr.has_name(*attr_to_check) {
1969-
tcx.sess
1970-
.struct_span_err(
1977+
// This function should only be called with crate attributes
1978+
// which are inner attributes always but lets check to make sure
1979+
if attr.style == AttrStyle::Inner {
1980+
for attr_to_check in ATTRS_TO_CHECK {
1981+
if attr.has_name(*attr_to_check) {
1982+
let mut err = tcx.sess.struct_span_err(
19711983
attr.span,
19721984
&format!(
19731985
"`{}` attribute cannot be used at crate level",
19741986
attr_to_check.to_ident_string()
19751987
),
1976-
)
1977-
.emit();
1988+
);
1989+
// Only emit an error with a suggestion if we can create a
1990+
// string out of the attribute span
1991+
if let Ok(src) = tcx.sess.source_map().span_to_snippet(attr.span) {
1992+
let replacement = src.replace("#!", "#");
1993+
err.span_suggestion_verbose(
1994+
attr.span,
1995+
"perhaps you meant to use an outer attribute",
1996+
replacement,
1997+
rustc_errors::Applicability::MachineApplicable,
1998+
);
1999+
}
2000+
err.emit()
2001+
}
19782002
}
19792003
}
19802004
}

compiler/rustc_resolve/src/late/diagnostics.rs

+7-13
Original file line numberDiff line numberDiff line change
@@ -1735,7 +1735,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
17351735
(generics.span, format!("<{}>", ident))
17361736
};
17371737
// Do not suggest if this is coming from macro expansion.
1738-
if !span.from_expansion() {
1738+
if span.can_be_used_for_suggestions() {
17391739
return Some((
17401740
span.shrink_to_hi(),
17411741
msg,
@@ -1803,7 +1803,7 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
18031803
);
18041804
err.span_label(lifetime_ref.span, "undeclared lifetime");
18051805
let mut suggests_in_band = false;
1806-
let mut suggest_note = true;
1806+
let mut suggested_spans = vec![];
18071807
for missing in &self.missing_named_lifetime_spots {
18081808
match missing {
18091809
MissingLifetimeSpot::Generics(generics) => {
@@ -1821,23 +1821,17 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
18211821
suggests_in_band = true;
18221822
(generics.span, format!("<{}>", lifetime_ref))
18231823
};
1824-
if !span.from_expansion() {
1824+
if suggested_spans.contains(&span) {
1825+
continue;
1826+
}
1827+
suggested_spans.push(span);
1828+
if span.can_be_used_for_suggestions() {
18251829
err.span_suggestion(
18261830
span,
18271831
&format!("consider introducing lifetime `{}` here", lifetime_ref),
18281832
sugg,
18291833
Applicability::MaybeIncorrect,
18301834
);
1831-
} else if suggest_note {
1832-
suggest_note = false; // Avoid displaying the same help multiple times.
1833-
err.span_label(
1834-
span,
1835-
&format!(
1836-
"lifetime `{}` is missing in item created through this procedural \
1837-
macro",
1838-
lifetime_ref,
1839-
),
1840-
);
18411835
}
18421836
}
18431837
MissingLifetimeSpot::HigherRanked { span, span_type } => {

compiler/rustc_span/src/lib.rs

+10
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,16 @@ impl Span {
551551
matches!(self.ctxt().outer_expn_data().kind, ExpnKind::Macro(MacroKind::Derive, _))
552552
}
553553

554+
/// Gate suggestions that would not be appropriate in a context the user didn't write.
555+
pub fn can_be_used_for_suggestions(self) -> bool {
556+
!self.from_expansion()
557+
// FIXME: If this span comes from a `derive` macro but it points at code the user wrote,
558+
// the callsite span and the span will be pointing at different places. It also means that
559+
// we can safely provide suggestions on this span.
560+
|| (matches!(self.ctxt().outer_expn_data().kind, ExpnKind::Macro(MacroKind::Derive, _))
561+
&& self.parent_callsite().map(|p| (p.lo(), p.hi())) != Some((self.lo(), self.hi())))
562+
}
563+
554564
#[inline]
555565
pub fn with_root_ctxt(lo: BytePos, hi: BytePos) -> Span {
556566
Span::new(lo, hi, SyntaxContext::root(), None)

compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -262,16 +262,16 @@ fn suggest_restriction(
262262
match generics
263263
.params
264264
.iter()
265-
.map(|p| p.bounds_span().unwrap_or(p.span))
266-
.filter(|&span| generics.span.contains(span) && span.desugaring_kind().is_none())
265+
.map(|p| p.bounds_span_for_suggestions().unwrap_or(p.span.shrink_to_hi()))
266+
.filter(|&span| generics.span.contains(span) && span.can_be_used_for_suggestions())
267267
.max_by_key(|span| span.hi())
268268
{
269269
// `fn foo(t: impl Trait)`
270270
// ^ suggest `<T: Trait>` here
271271
None => (generics.span, format!("<{}>", type_param)),
272272
// `fn foo<A>(t: impl Trait)`
273273
// ^^^ suggest `<A, T: Trait>` here
274-
Some(span) => (span.shrink_to_hi(), format!(", {}", type_param)),
274+
Some(span) => (span, format!(", {}", type_param)),
275275
},
276276
// `fn foo(t: impl Trait)`
277277
// ^ suggest `where <T as Trait>::A: Bound`

compiler/rustc_typeck/src/collect.rs

+3-5
Original file line numberDiff line numberDiff line change
@@ -177,11 +177,9 @@ crate fn placeholder_type_error(
177177
sugg.push((arg.span, (*type_name).to_string()));
178178
} else {
179179
let last = generics.iter().last().unwrap();
180-
sugg.push((
181-
// Account for bounds, we want `fn foo<T: E, K>(_: K)` not `fn foo<T, K: E>(_: K)`.
182-
last.bounds_span().unwrap_or(last.span).shrink_to_hi(),
183-
format!(", {}", type_name),
184-
));
180+
// Account for bounds, we want `fn foo<T: E, K>(_: K)` not `fn foo<T, K: E>(_: K)`.
181+
let span = last.bounds_span_for_suggestions().unwrap_or(last.span.shrink_to_hi());
182+
sugg.push((span, format!(", {}", type_name)));
185183
}
186184

187185
let mut err = bad_placeholder_type(tcx, placeholder_types, kind);

library/core/src/iter/traits/iterator.rs

+80
Original file line numberDiff line numberDiff line change
@@ -2216,6 +2216,86 @@ pub trait Iterator {
22162216
Some(self.fold(first, f))
22172217
}
22182218

2219+
/// Reduces the elements to a single one by repeatedly applying a reducing operation. If the
2220+
/// closure returns a failure, the failure is propagated back to the caller immediately.
2221+
///
2222+
/// The return type of this method depends on the return type of the closure. If the closure
2223+
/// returns `Result<Self::Item, E>`, then this function will return `Result<Option<Self::Item>,
2224+
/// E>`. If the closure returns `Option<Self::Item>`, then this function will return
2225+
/// `Option<Option<Self::Item>>`.
2226+
///
2227+
/// When called on an empty iterator, this function will return either `Some(None)` or
2228+
/// `Ok(None)` depending on the type of the provided closure.
2229+
///
2230+
/// For iterators with at least one element, this is essentially the same as calling
2231+
/// [`try_fold()`] with the first element of the iterator as the initial accumulator value.
2232+
///
2233+
/// [`try_fold()`]: Iterator::try_fold
2234+
///
2235+
/// # Examples
2236+
///
2237+
/// Safely calculate the sum of a series of numbers:
2238+
///
2239+
/// ```
2240+
/// #![feature(iterator_try_reduce)]
2241+
///
2242+
/// let numbers: Vec<usize> = vec![10, 20, 5, 23, 0];
2243+
/// let sum = numbers.into_iter().try_reduce(|x, y| x.checked_add(y));
2244+
/// assert_eq!(sum, Some(Some(58)));
2245+
/// ```
2246+
///
2247+
/// Determine when a reduction short circuited:
2248+
///
2249+
/// ```
2250+
/// #![feature(iterator_try_reduce)]
2251+
///
2252+
/// let numbers = vec![1, 2, 3, usize::MAX, 4, 5];
2253+
/// let sum = numbers.into_iter().try_reduce(|x, y| x.checked_add(y));
2254+
/// assert_eq!(sum, None);
2255+
/// ```
2256+
///
2257+
/// Determine when a reduction was not performed because there are no elements:
2258+
///
2259+
/// ```
2260+
/// #![feature(iterator_try_reduce)]
2261+
///
2262+
/// let numbers: Vec<usize> = Vec::new();
2263+
/// let sum = numbers.into_iter().try_reduce(|x, y| x.checked_add(y));
2264+
/// assert_eq!(sum, Some(None));
2265+
/// ```
2266+
///
2267+
/// Use a [`Result`] instead of an [`Option`]:
2268+
///
2269+
/// ```
2270+
/// #![feature(iterator_try_reduce)]
2271+
///
2272+
/// let numbers = vec!["1", "2", "3", "4", "5"];
2273+
/// let max: Result<Option<_>, <usize as std::str::FromStr>::Err> =
2274+
/// numbers.into_iter().try_reduce(|x, y| {
2275+
/// if x.parse::<usize>()? > y.parse::<usize>()? { Ok(x) } else { Ok(y) }
2276+
/// });
2277+
/// assert_eq!(max, Ok(Some("5")));
2278+
/// ```
2279+
#[inline]
2280+
#[unstable(feature = "iterator_try_reduce", reason = "new API", issue = "87053")]
2281+
fn try_reduce<F, R>(&mut self, f: F) -> ChangeOutputType<R, Option<R::Output>>
2282+
where
2283+
Self: Sized,
2284+
F: FnMut(Self::Item, Self::Item) -> R,
2285+
R: Try<Output = Self::Item>,
2286+
R::Residual: Residual<Option<Self::Item>>,
2287+
{
2288+
let first = match self.next() {
2289+
Some(i) => i,
2290+
None => return Try::from_output(None),
2291+
};
2292+
2293+
match self.try_fold(first, f).branch() {
2294+
ControlFlow::Break(r) => FromResidual::from_residual(r),
2295+
ControlFlow::Continue(i) => Try::from_output(Some(i)),
2296+
}
2297+
}
2298+
22192299
/// Tests if every element of the iterator matches a predicate.
22202300
///
22212301
/// `all()` takes a closure that returns `true` or `false`. It applies

library/core/tests/iter/traits/iterator.rs

+28
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,34 @@ fn test_find_map() {
454454
}
455455
}
456456

457+
#[test]
458+
fn test_try_reduce() {
459+
let v: Vec<usize> = vec![1, 2, 3, 4, 5];
460+
let sum = v.into_iter().try_reduce(|x, y| x.checked_add(y));
461+
assert_eq!(sum, Some(Some(15)));
462+
463+
let v: Vec<usize> = vec![1, 2, 3, 4, 5, usize::MAX];
464+
let sum = v.into_iter().try_reduce(|x, y| x.checked_add(y));
465+
assert_eq!(sum, None);
466+
467+
let v: Vec<usize> = Vec::new();
468+
let sum = v.into_iter().try_reduce(|x, y| x.checked_add(y));
469+
assert_eq!(sum, Some(None));
470+
471+
let v = vec!["1", "2", "3", "4", "5"];
472+
let max = v.into_iter().try_reduce(|x, y| {
473+
if x.parse::<usize>().ok()? > y.parse::<usize>().ok()? { Some(x) } else { Some(y) }
474+
});
475+
assert_eq!(max, Some(Some("5")));
476+
477+
let v = vec!["1", "2", "3", "4", "5"];
478+
let max: Result<Option<_>, <usize as std::str::FromStr>::Err> =
479+
v.into_iter().try_reduce(|x, y| {
480+
if x.parse::<usize>()? > y.parse::<usize>()? { Ok(x) } else { Ok(y) }
481+
});
482+
assert_eq!(max, Ok(Some("5")));
483+
}
484+
457485
#[test]
458486
fn test_iterator_len() {
459487
let v: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

library/core/tests/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
#![feature(iter_intersperse)]
5757
#![feature(iter_is_partitioned)]
5858
#![feature(iter_order_by)]
59+
#![feature(iterator_try_reduce)]
5960
#![feature(const_mut_refs)]
6061
#![feature(const_pin)]
6162
#![feature(const_slice_from_raw_parts)]

src/test/ui/derives/issue-36617.rs

+13
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
11
#![derive(Copy)] //~ ERROR cannot determine resolution for the attribute macro `derive`
2+
//~^ ERROR `derive` attribute cannot be used at crate level
3+
4+
#![test]//~ ERROR cannot determine resolution for the attribute macro `test`
5+
//~^ ERROR `test` attribute cannot be used at crate level
6+
7+
#![test_case]//~ ERROR cannot determine resolution for the attribute macro `test_case`
8+
//~^ ERROR `test_case` attribute cannot be used at crate level
9+
10+
#![bench]//~ ERROR cannot determine resolution for the attribute macro `bench`
11+
//~^ ERROR `bench` attribute cannot be used at crate level
12+
13+
#![global_allocator]//~ ERROR cannot determine resolution for the attribute macro `global_allocator`
14+
//~^ ERROR `global_allocator` attribute cannot be used at crate level
215

316
fn main() {}

0 commit comments

Comments
 (0)