Skip to content

Commit

Permalink
Auto merge of #91517 - matthiaskrgr:rollup-3fmp4go, r=matthiaskrgr
Browse files Browse the repository at this point in the history
Rollup of 3 pull requests

Successful merges:

 - #87054 (Add a `try_reduce` method to the Iterator trait)
 - #89701 (Updated error message for accidental uses of derive attribute as a crate attribute)
 - #90519 (Keep spans for generics in `#[derive(_)]` desugaring)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
  • Loading branch information
bors committed Dec 4, 2021
2 parents 887999d + 0311cfa commit efec545
Show file tree
Hide file tree
Showing 29 changed files with 731 additions and 148 deletions.
154 changes: 76 additions & 78 deletions compiler/rustc_builtin_macros/src/deriving/generic/mod.rs

Large diffs are not rendered by default.

16 changes: 6 additions & 10 deletions compiler/rustc_builtin_macros/src/deriving/generic/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,14 +211,6 @@ fn mk_ty_param(
cx.typaram(span, Ident::new(name, span), attrs.to_owned(), bounds, None)
}

fn mk_generics(params: Vec<ast::GenericParam>, span: Span) -> Generics {
Generics {
params,
where_clause: ast::WhereClause { has_where_token: false, predicates: Vec::new(), span },
span,
}
}

/// Bounds on type parameters.
#[derive(Clone)]
pub struct Bounds {
Expand All @@ -236,7 +228,7 @@ impl Bounds {
self_ty: Ident,
self_generics: &Generics,
) -> Generics {
let generic_params = self
let params = self
.bounds
.iter()
.map(|t| {
Expand All @@ -245,7 +237,11 @@ impl Bounds {
})
.collect();

mk_generics(generic_params, span)
Generics {
params,
where_clause: ast::WhereClause { has_where_token: false, predicates: Vec::new(), span },
span,
}
}
}

Expand Down
23 changes: 15 additions & 8 deletions compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ use rustc_index::vec::IndexVec;
use rustc_macros::HashStable_Generic;
use rustc_span::source_map::Spanned;
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::{def_id::LocalDefId, BytePos};
use rustc_span::{MultiSpan, Span, DUMMY_SP};
use rustc_span::{def_id::LocalDefId, BytePos, MultiSpan, Span, DUMMY_SP};
use rustc_target::asm::InlineAsmRegOrRegClass;
use rustc_target::spec::abi::Abi;

Expand Down Expand Up @@ -526,12 +525,20 @@ pub struct GenericParam<'hir> {
}

impl GenericParam<'hir> {
pub fn bounds_span(&self) -> Option<Span> {
self.bounds.iter().fold(None, |span, bound| {
let span = span.map(|s| s.to(bound.span())).unwrap_or_else(|| bound.span());

Some(span)
})
pub fn bounds_span_for_suggestions(&self) -> Option<Span> {
self.bounds
.iter()
.fold(None, |span: Option<Span>, bound| {
// We include bounds that come from a `#[derive(_)]` but point at the user's code,
// as we use this method to get a span appropriate for suggestions.
if !bound.span().can_be_used_for_suggestions() {
None
} else {
let span = span.map(|s| s.to(bound.span())).unwrap_or_else(|| bound.span());
Some(span)
}
})
.map(|sp| sp.shrink_to_hi())
}
}

Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_middle/src/ty/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ pub fn suggest_constraining_type_param(
// `where` clause instead of `trait Base<T: Copy = String>: Super<T>`.
&& !matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. })
{
if let Some(bounds_span) = param.bounds_span() {
if let Some(span) = param.bounds_span_for_suggestions() {
// If user has provided some bounds, suggest restricting them:
//
// fn foo<T: Foo>(t: T) { ... }
Expand All @@ -284,7 +284,7 @@ pub fn suggest_constraining_type_param(
// --
// |
// replace with: `T: Bar +`
suggest_restrict(bounds_span.shrink_to_hi());
suggest_restrict(span);
} else {
// If user hasn't provided any bounds, suggest adding a new one:
//
Expand Down
36 changes: 30 additions & 6 deletions compiler/rustc_passes/src/check_attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1953,28 +1953,52 @@ fn is_c_like_enum(item: &Item<'_>) -> bool {
}
}

// FIXME: Fix "Cannot determine resolution" error and remove built-in macros
// from this check.
fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
// Check for builtin attributes at the crate level
// which were unsuccessfully resolved due to cannot determine
// resolution for the attribute macro error.
const ATTRS_TO_CHECK: &[Symbol] = &[
sym::macro_export,
sym::repr,
sym::path,
sym::automatically_derived,
sym::start,
sym::rustc_main,
sym::derive,
sym::test,
sym::test_case,
sym::global_allocator,
sym::bench,
];

for attr in attrs {
for attr_to_check in ATTRS_TO_CHECK {
if attr.has_name(*attr_to_check) {
tcx.sess
.struct_span_err(
// This function should only be called with crate attributes
// which are inner attributes always but lets check to make sure
if attr.style == AttrStyle::Inner {
for attr_to_check in ATTRS_TO_CHECK {
if attr.has_name(*attr_to_check) {
let mut err = tcx.sess.struct_span_err(
attr.span,
&format!(
"`{}` attribute cannot be used at crate level",
attr_to_check.to_ident_string()
),
)
.emit();
);
// Only emit an error with a suggestion if we can create a
// string out of the attribute span
if let Ok(src) = tcx.sess.source_map().span_to_snippet(attr.span) {
let replacement = src.replace("#!", "#");
err.span_suggestion_verbose(
attr.span,
"perhaps you meant to use an outer attribute",
replacement,
rustc_errors::Applicability::MachineApplicable,
);
}
err.emit()
}
}
}
}
Expand Down
20 changes: 7 additions & 13 deletions compiler/rustc_resolve/src/late/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1735,7 +1735,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
(generics.span, format!("<{}>", ident))
};
// Do not suggest if this is coming from macro expansion.
if !span.from_expansion() {
if span.can_be_used_for_suggestions() {
return Some((
span.shrink_to_hi(),
msg,
Expand Down Expand Up @@ -1803,7 +1803,7 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
);
err.span_label(lifetime_ref.span, "undeclared lifetime");
let mut suggests_in_band = false;
let mut suggest_note = true;
let mut suggested_spans = vec![];
for missing in &self.missing_named_lifetime_spots {
match missing {
MissingLifetimeSpot::Generics(generics) => {
Expand All @@ -1821,23 +1821,17 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
suggests_in_band = true;
(generics.span, format!("<{}>", lifetime_ref))
};
if !span.from_expansion() {
if suggested_spans.contains(&span) {
continue;
}
suggested_spans.push(span);
if span.can_be_used_for_suggestions() {
err.span_suggestion(
span,
&format!("consider introducing lifetime `{}` here", lifetime_ref),
sugg,
Applicability::MaybeIncorrect,
);
} else if suggest_note {
suggest_note = false; // Avoid displaying the same help multiple times.
err.span_label(
span,
&format!(
"lifetime `{}` is missing in item created through this procedural \
macro",
lifetime_ref,
),
);
}
}
MissingLifetimeSpot::HigherRanked { span, span_type } => {
Expand Down
10 changes: 10 additions & 0 deletions compiler/rustc_span/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,16 @@ impl Span {
matches!(self.ctxt().outer_expn_data().kind, ExpnKind::Macro(MacroKind::Derive, _))
}

/// Gate suggestions that would not be appropriate in a context the user didn't write.
pub fn can_be_used_for_suggestions(self) -> bool {
!self.from_expansion()
// FIXME: If this span comes from a `derive` macro but it points at code the user wrote,
// the callsite span and the span will be pointing at different places. It also means that
// we can safely provide suggestions on this span.
|| (matches!(self.ctxt().outer_expn_data().kind, ExpnKind::Macro(MacroKind::Derive, _))
&& self.parent_callsite().map(|p| (p.lo(), p.hi())) != Some((self.lo(), self.hi())))
}

#[inline]
pub fn with_root_ctxt(lo: BytePos, hi: BytePos) -> Span {
Span::new(lo, hi, SyntaxContext::root(), None)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,16 +262,16 @@ fn suggest_restriction(
match generics
.params
.iter()
.map(|p| p.bounds_span().unwrap_or(p.span))
.filter(|&span| generics.span.contains(span) && span.desugaring_kind().is_none())
.map(|p| p.bounds_span_for_suggestions().unwrap_or(p.span.shrink_to_hi()))
.filter(|&span| generics.span.contains(span) && span.can_be_used_for_suggestions())
.max_by_key(|span| span.hi())
{
// `fn foo(t: impl Trait)`
// ^ suggest `<T: Trait>` here
None => (generics.span, format!("<{}>", type_param)),
// `fn foo<A>(t: impl Trait)`
// ^^^ suggest `<A, T: Trait>` here
Some(span) => (span.shrink_to_hi(), format!(", {}", type_param)),
Some(span) => (span, format!(", {}", type_param)),
},
// `fn foo(t: impl Trait)`
// ^ suggest `where <T as Trait>::A: Bound`
Expand Down
8 changes: 3 additions & 5 deletions compiler/rustc_typeck/src/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,11 +177,9 @@ crate fn placeholder_type_error(
sugg.push((arg.span, (*type_name).to_string()));
} else {
let last = generics.iter().last().unwrap();
sugg.push((
// Account for bounds, we want `fn foo<T: E, K>(_: K)` not `fn foo<T, K: E>(_: K)`.
last.bounds_span().unwrap_or(last.span).shrink_to_hi(),
format!(", {}", type_name),
));
// Account for bounds, we want `fn foo<T: E, K>(_: K)` not `fn foo<T, K: E>(_: K)`.
let span = last.bounds_span_for_suggestions().unwrap_or(last.span.shrink_to_hi());
sugg.push((span, format!(", {}", type_name)));
}

let mut err = bad_placeholder_type(tcx, placeholder_types, kind);
Expand Down
80 changes: 80 additions & 0 deletions library/core/src/iter/traits/iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2216,6 +2216,86 @@ pub trait Iterator {
Some(self.fold(first, f))
}

/// Reduces the elements to a single one by repeatedly applying a reducing operation. If the
/// closure returns a failure, the failure is propagated back to the caller immediately.
///
/// The return type of this method depends on the return type of the closure. If the closure
/// returns `Result<Self::Item, E>`, then this function will return `Result<Option<Self::Item>,
/// E>`. If the closure returns `Option<Self::Item>`, then this function will return
/// `Option<Option<Self::Item>>`.
///
/// When called on an empty iterator, this function will return either `Some(None)` or
/// `Ok(None)` depending on the type of the provided closure.
///
/// For iterators with at least one element, this is essentially the same as calling
/// [`try_fold()`] with the first element of the iterator as the initial accumulator value.
///
/// [`try_fold()`]: Iterator::try_fold
///
/// # Examples
///
/// Safely calculate the sum of a series of numbers:
///
/// ```
/// #![feature(iterator_try_reduce)]
///
/// let numbers: Vec<usize> = vec![10, 20, 5, 23, 0];
/// let sum = numbers.into_iter().try_reduce(|x, y| x.checked_add(y));
/// assert_eq!(sum, Some(Some(58)));
/// ```
///
/// Determine when a reduction short circuited:
///
/// ```
/// #![feature(iterator_try_reduce)]
///
/// let numbers = vec![1, 2, 3, usize::MAX, 4, 5];
/// let sum = numbers.into_iter().try_reduce(|x, y| x.checked_add(y));
/// assert_eq!(sum, None);
/// ```
///
/// Determine when a reduction was not performed because there are no elements:
///
/// ```
/// #![feature(iterator_try_reduce)]
///
/// let numbers: Vec<usize> = Vec::new();
/// let sum = numbers.into_iter().try_reduce(|x, y| x.checked_add(y));
/// assert_eq!(sum, Some(None));
/// ```
///
/// Use a [`Result`] instead of an [`Option`]:
///
/// ```
/// #![feature(iterator_try_reduce)]
///
/// let numbers = vec!["1", "2", "3", "4", "5"];
/// let max: Result<Option<_>, <usize as std::str::FromStr>::Err> =
/// numbers.into_iter().try_reduce(|x, y| {
/// if x.parse::<usize>()? > y.parse::<usize>()? { Ok(x) } else { Ok(y) }
/// });
/// assert_eq!(max, Ok(Some("5")));
/// ```
#[inline]
#[unstable(feature = "iterator_try_reduce", reason = "new API", issue = "87053")]
fn try_reduce<F, R>(&mut self, f: F) -> ChangeOutputType<R, Option<R::Output>>
where
Self: Sized,
F: FnMut(Self::Item, Self::Item) -> R,
R: Try<Output = Self::Item>,
R::Residual: Residual<Option<Self::Item>>,
{
let first = match self.next() {
Some(i) => i,
None => return Try::from_output(None),
};

match self.try_fold(first, f).branch() {
ControlFlow::Break(r) => FromResidual::from_residual(r),
ControlFlow::Continue(i) => Try::from_output(Some(i)),
}
}

/// Tests if every element of the iterator matches a predicate.
///
/// `all()` takes a closure that returns `true` or `false`. It applies
Expand Down
28 changes: 28 additions & 0 deletions library/core/tests/iter/traits/iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,34 @@ fn test_find_map() {
}
}

#[test]
fn test_try_reduce() {
let v: Vec<usize> = vec![1, 2, 3, 4, 5];
let sum = v.into_iter().try_reduce(|x, y| x.checked_add(y));
assert_eq!(sum, Some(Some(15)));

let v: Vec<usize> = vec![1, 2, 3, 4, 5, usize::MAX];
let sum = v.into_iter().try_reduce(|x, y| x.checked_add(y));
assert_eq!(sum, None);

let v: Vec<usize> = Vec::new();
let sum = v.into_iter().try_reduce(|x, y| x.checked_add(y));
assert_eq!(sum, Some(None));

let v = vec!["1", "2", "3", "4", "5"];
let max = v.into_iter().try_reduce(|x, y| {
if x.parse::<usize>().ok()? > y.parse::<usize>().ok()? { Some(x) } else { Some(y) }
});
assert_eq!(max, Some(Some("5")));

let v = vec!["1", "2", "3", "4", "5"];
let max: Result<Option<_>, <usize as std::str::FromStr>::Err> =
v.into_iter().try_reduce(|x, y| {
if x.parse::<usize>()? > y.parse::<usize>()? { Ok(x) } else { Ok(y) }
});
assert_eq!(max, Ok(Some("5")));
}

#[test]
fn test_iterator_len() {
let v: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
Expand Down
1 change: 1 addition & 0 deletions library/core/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
#![feature(iter_intersperse)]
#![feature(iter_is_partitioned)]
#![feature(iter_order_by)]
#![feature(iterator_try_reduce)]
#![feature(const_mut_refs)]
#![feature(const_pin)]
#![feature(const_slice_from_raw_parts)]
Expand Down
13 changes: 13 additions & 0 deletions src/test/ui/derives/issue-36617.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
#![derive(Copy)] //~ ERROR cannot determine resolution for the attribute macro `derive`
//~^ ERROR `derive` attribute cannot be used at crate level

#![test]//~ ERROR cannot determine resolution for the attribute macro `test`
//~^ ERROR `test` attribute cannot be used at crate level

#![test_case]//~ ERROR cannot determine resolution for the attribute macro `test_case`
//~^ ERROR `test_case` attribute cannot be used at crate level

#![bench]//~ ERROR cannot determine resolution for the attribute macro `bench`
//~^ ERROR `bench` attribute cannot be used at crate level

#![global_allocator]//~ ERROR cannot determine resolution for the attribute macro `global_allocator`
//~^ ERROR `global_allocator` attribute cannot be used at crate level

fn main() {}
Loading

0 comments on commit efec545

Please sign in to comment.