rust/compiler/rustc_hir/src/pat_util.rs

164 lines
5.0 KiB
Rust
Raw Normal View History

use crate::def::{CtorOf, DefKind, Res};
use crate::def_id::DefId;
2019-02-05 17:20:45 +00:00
use crate::hir::{self, HirId, PatKind};
2020-04-19 11:00:18 +00:00
use rustc_span::symbol::Ident;
use rustc_span::Span;
use std::iter::{Enumerate, ExactSizeIterator};
2015-11-04 06:02:22 +00:00
pub struct EnumerateAndAdjust<I> {
enumerate: Enumerate<I>,
gap_pos: usize,
gap_len: usize,
}
2019-12-22 22:42:04 +00:00
impl<I> Iterator for EnumerateAndAdjust<I>
where
I: Iterator,
{
type Item = (usize, <I as Iterator>::Item);
fn next(&mut self) -> Option<(usize, <I as Iterator>::Item)> {
2019-12-22 22:42:04 +00:00
self.enumerate
.next()
.map(|(i, elem)| (if i < self.gap_pos { i } else { i + self.gap_len }, elem))
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.enumerate.size_hint()
}
}
pub trait EnumerateAndAdjustIterator {
2019-12-22 22:42:04 +00:00
fn enumerate_and_adjust(
self,
expected_len: usize,
gap_pos: Option<usize>,
) -> EnumerateAndAdjust<Self>
where
Self: Sized;
}
impl<T: ExactSizeIterator> EnumerateAndAdjustIterator for T {
2019-12-22 22:42:04 +00:00
fn enumerate_and_adjust(
self,
expected_len: usize,
gap_pos: Option<usize>,
) -> EnumerateAndAdjust<Self>
where
Self: Sized,
{
let actual_len = self.len();
EnumerateAndAdjust {
enumerate: self.enumerate(),
2018-04-02 23:45:06 +00:00
gap_pos: gap_pos.unwrap_or(expected_len),
gap_len: expected_len - actual_len,
}
}
}
2019-11-29 12:43:03 +00:00
impl hir::Pat<'_> {
/// Call `f` on every "binding" in a pattern, e.g., on `a` in
/// `match foo() { Some(a) => (), None => () }`
2020-04-19 11:00:18 +00:00
pub fn each_binding(&self, mut f: impl FnMut(hir::BindingAnnotation, HirId, Span, Ident)) {
2019-12-14 22:43:21 +00:00
self.walk_always(|p| {
2019-09-26 15:18:31 +00:00
if let PatKind::Binding(binding_mode, _, ident, _) = p.kind {
f(binding_mode, p.hir_id, p.span, ident);
}
});
}
/// Call `f` on every "binding" in a pattern, e.g., on `a` in
/// `match foo() { Some(a) => (), None => () }`.
///
/// When encountering an or-pattern `p_0 | ... | p_n` only `p_0` will be visited.
pub fn each_binding_or_first(
&self,
2020-04-19 11:00:18 +00:00
f: &mut impl FnMut(hir::BindingAnnotation, HirId, Span, Ident),
) {
2019-09-26 15:18:31 +00:00
self.walk(|p| match &p.kind {
PatKind::Or(ps) => {
ps[0].each_binding_or_first(f);
false
2019-12-22 22:42:04 +00:00
}
PatKind::Binding(bm, _, ident, _) => {
f(*bm, p.hir_id, p.span, *ident);
true
}
_ => true,
})
}
/// Checks if the pattern contains any patterns that bind something to
/// an ident, e.g., `foo`, or `Foo(foo)` or `foo @ Bar(..)`.
pub fn contains_bindings(&self) -> bool {
2020-10-27 01:02:48 +00:00
self.satisfies(|p| matches!(p.kind, PatKind::Binding(..)))
}
When matching against a pattern (either via `match` or `let`) that contains ref-bindings, do not permit any upcasting from the type of the value being matched. Similarly, do not permit coercion in a `let`. This is a [breaking-change] in that it closes a type hole that previously existed, and in that coercion is not performed. You should be able to work around the latter by converting: ```rust let ref mut x: T = expr; ``` into ```rust let x: T = expr; let ref mut x = x; ``` Restricting coercion not to apply in the case of `let ref` or `let ref mut` is sort of unexciting to me, but seems the best solution: 1. Mixing coercion and `let ref` or `let ref mut` is a bit odd, because you are taking the address of a (coerced) temporary, but only sometimes. It's not syntactically evident, in other words, what's going on. When you're doing a coercion, you're kind of 2. Put another way, I would like to preserve the relationship that `equality <= subtyping <= coercion <= as-coercion`, where this is an indication of the number of `(T1,T2)` pairs that are accepted by the various relations. Trying to mix `let ref mut` and coercion would create another kind of relation that is like coercion, but acts differently in the case where a precise match is needed. 3. In any case, this is strictly more conservative than what we had before and we can undo it in the future if we find a way to make coercion mix with type equality. The change to match I feel ok about but similarly unthrilled. There is some subtle text already concerning whether to use eqtype or subtype for identifier bindings. The best fix I think would be to always have match use strict equality but use subtyping on identifier bindings, but the comment `(*)` explains why that's not working at the moment. As above, I think we can change this as we clean up the code there.
2015-03-06 15:14:12 +00:00
/// Checks if the pattern satisfies the given predicate on some sub-pattern.
2019-11-29 13:08:03 +00:00
fn satisfies(&self, pred: impl Fn(&hir::Pat<'_>) -> bool) -> bool {
let mut satisfies = false;
self.walk_short(|p| {
if pred(p) {
satisfies = true;
false // Found one, can short circuit now.
} else {
true
}
});
satisfies
}
2020-04-19 11:00:18 +00:00
pub fn simple_ident(&self) -> Option<Ident> {
2019-09-26 15:18:31 +00:00
match self.kind {
PatKind::Binding(
hir::BindingAnnotation::Unannotated | hir::BindingAnnotation::Mutable,
_,
ident,
None,
) => Some(ident),
2018-05-11 14:24:04 +00:00
_ => None,
}
}
2019-02-08 13:53:55 +00:00
/// Returns variants that are necessary to exist for the pattern to match.
pub fn necessary_variants(&self) -> Vec<DefId> {
let mut variants = vec![];
2019-09-26 15:18:31 +00:00
self.walk(|p| match &p.kind {
PatKind::Or(_) => false,
2019-12-22 22:42:04 +00:00
PatKind::Path(hir::QPath::Resolved(_, path))
| PatKind::TupleStruct(hir::QPath::Resolved(_, path), ..)
| PatKind::Struct(hir::QPath::Resolved(_, path), ..) => {
if let Res::Def(DefKind::Variant | DefKind::Ctor(CtorOf::Variant, ..), id) =
path.res
{
variants.push(id);
}
true
}
_ => true,
});
variants.sort();
variants.dedup();
variants
}
/// Checks if the pattern contains any `ref` or `ref mut` bindings, and if
/// yes whether it contains mutable or just immutables ones.
2019-02-08 13:53:55 +00:00
//
// FIXME(tschottdorf): this is problematic as the HIR is being scraped, but
// ref bindings are be implicit after #42640 (default match binding modes). See issue #44848.
pub fn contains_explicit_ref_binding(&self) -> Option<hir::Mutability> {
let mut result = None;
2019-12-22 22:42:04 +00:00
self.each_binding(|annotation, _, _, _| match annotation {
hir::BindingAnnotation::Ref => match result {
None | Some(hir::Mutability::Not) => result = Some(hir::Mutability::Not),
_ => {}
2019-12-22 22:42:04 +00:00
},
hir::BindingAnnotation::RefMut => result = Some(hir::Mutability::Mut),
_ => {}
});
result
}
}