mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-15 09:27:27 +00:00
Support bool literal patterns
This commit is contained in:
parent
5a8a0b6269
commit
3a85e47f6a
2 changed files with 182 additions and 9 deletions
|
@ -118,6 +118,32 @@ fn main(v: E) {
|
|||
match v { }
|
||||
//^ Missing match arm
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn boolean() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
fn main() {
|
||||
match true {
|
||||
true => {}
|
||||
false => {}
|
||||
}
|
||||
match true {
|
||||
true | false => {}
|
||||
}
|
||||
match true {
|
||||
true => {}
|
||||
_ => {}
|
||||
}
|
||||
match true {}
|
||||
//^^^^ Missing match arm
|
||||
match true { true => {} }
|
||||
//^^^^ Missing match arm
|
||||
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,12 +1,17 @@
|
|||
use std::{
|
||||
cmp::{max, min},
|
||||
iter::once,
|
||||
ops::RangeInclusive,
|
||||
};
|
||||
|
||||
use hir_def::{
|
||||
expr::{Pat, PatId, RecordFieldPat},
|
||||
expr::{Expr, Literal, Pat, PatId, RecordFieldPat},
|
||||
find_path::find_path,
|
||||
item_scope::ItemInNs,
|
||||
path::Path,
|
||||
type_ref::Mutability,
|
||||
AttrDefId, EnumVariantId, HasModule, VariantId,
|
||||
};
|
||||
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
|
||||
use crate::{AdtId, Interner, Scalar, Ty, TyExt, TyKind};
|
||||
|
@ -20,7 +25,7 @@ pub(super) enum ToDo {}
|
|||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub(super) struct IntRange {
|
||||
range: ToDo,
|
||||
range: RangeInclusive<u128>,
|
||||
}
|
||||
|
||||
impl IntRange {
|
||||
|
@ -36,12 +41,147 @@ impl IntRange {
|
|||
}
|
||||
|
||||
fn is_singleton(&self) -> bool {
|
||||
self.range.start() == self.range.end()
|
||||
}
|
||||
|
||||
fn boundaries(&self) -> (u128, u128) {
|
||||
(*self.range.start(), *self.range.end())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_bool(value: bool) -> IntRange {
|
||||
let val = value as u128;
|
||||
IntRange { range: val..=val }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_range(cx: &MatchCheckCtx<'_>, lo: u128, hi: u128, scalar_ty: Scalar) -> IntRange {
|
||||
if let Scalar::Bool = scalar_ty {
|
||||
IntRange { range: lo..=hi }
|
||||
} else {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
fn is_subrange(&self, other: &Self) -> bool {
|
||||
other.range.start() <= self.range.start() && self.range.end() <= other.range.end()
|
||||
}
|
||||
|
||||
fn intersection(&self, other: &Self) -> Option<Self> {
|
||||
let (lo, hi) = self.boundaries();
|
||||
let (other_lo, other_hi) = other.boundaries();
|
||||
if lo <= other_hi && other_lo <= hi {
|
||||
Some(IntRange { range: max(lo, other_lo)..=min(hi, other_hi) })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// See `Constructor::is_covered_by`
|
||||
fn is_covered_by(&self, other: &Self) -> bool {
|
||||
todo!()
|
||||
if self.intersection(other).is_some() {
|
||||
// Constructor splitting should ensure that all intersections we encounter are actually
|
||||
// inclusions.
|
||||
assert!(self.is_subrange(other));
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a border between 2 integers. Because the intervals spanning borders must be able to
|
||||
/// cover every integer, we need to be able to represent 2^128 + 1 such borders.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
enum IntBorder {
|
||||
JustBefore(u128),
|
||||
AfterMax,
|
||||
}
|
||||
|
||||
/// A range of integers that is partitioned into disjoint subranges. This does constructor
|
||||
/// splitting for integer ranges as explained at the top of the file.
|
||||
///
|
||||
/// This is fed multiple ranges, and returns an output that covers the input, but is split so that
|
||||
/// the only intersections between an output range and a seen range are inclusions. No output range
|
||||
/// straddles the boundary of one of the inputs.
|
||||
///
|
||||
/// The following input:
|
||||
/// ```
|
||||
/// |-------------------------| // `self`
|
||||
/// |------| |----------| |----|
|
||||
/// |-------| |-------|
|
||||
/// ```
|
||||
/// would be iterated over as follows:
|
||||
/// ```
|
||||
/// ||---|--||-|---|---|---|--|
|
||||
/// ```
|
||||
#[derive(Debug, Clone)]
|
||||
struct SplitIntRange {
|
||||
/// The range we are splitting
|
||||
range: IntRange,
|
||||
/// The borders of ranges we have seen. They are all contained within `range`. This is kept
|
||||
/// sorted.
|
||||
borders: Vec<IntBorder>,
|
||||
}
|
||||
|
||||
impl SplitIntRange {
|
||||
fn new(range: IntRange) -> Self {
|
||||
SplitIntRange { range, borders: Vec::new() }
|
||||
}
|
||||
|
||||
/// Internal use
|
||||
fn to_borders(r: IntRange) -> [IntBorder; 2] {
|
||||
use IntBorder::*;
|
||||
let (lo, hi) = r.boundaries();
|
||||
let lo = JustBefore(lo);
|
||||
let hi = match hi.checked_add(1) {
|
||||
Some(m) => JustBefore(m),
|
||||
None => AfterMax,
|
||||
};
|
||||
[lo, hi]
|
||||
}
|
||||
|
||||
/// Add ranges relative to which we split.
|
||||
fn split(&mut self, ranges: impl Iterator<Item = IntRange>) {
|
||||
let this_range = &self.range;
|
||||
let included_ranges = ranges.filter_map(|r| this_range.intersection(&r));
|
||||
let included_borders = included_ranges.flat_map(|r| {
|
||||
let borders = Self::to_borders(r);
|
||||
once(borders[0]).chain(once(borders[1]))
|
||||
});
|
||||
self.borders.extend(included_borders);
|
||||
self.borders.sort_unstable();
|
||||
}
|
||||
|
||||
/// Iterate over the contained ranges.
|
||||
fn iter(&self) -> impl Iterator<Item = IntRange> + '_ {
|
||||
use IntBorder::*;
|
||||
|
||||
let self_range = Self::to_borders(self.range.clone());
|
||||
// Start with the start of the range.
|
||||
let mut prev_border = self_range[0];
|
||||
self.borders
|
||||
.iter()
|
||||
.copied()
|
||||
// End with the end of the range.
|
||||
.chain(once(self_range[1]))
|
||||
// List pairs of adjacent borders.
|
||||
.map(move |border| {
|
||||
let ret = (prev_border, border);
|
||||
prev_border = border;
|
||||
ret
|
||||
})
|
||||
// Skip duplicates.
|
||||
.filter(|(prev_border, border)| prev_border != border)
|
||||
// Finally, convert to ranges.
|
||||
.map(|(prev_border, border)| {
|
||||
let range = match (prev_border, border) {
|
||||
(JustBefore(n), JustBefore(m)) if n < m => n..=(m - 1),
|
||||
(JustBefore(n), AfterMax) => n..=u128::MAX,
|
||||
_ => unreachable!(), // Ruled out by the sorting and filtering we did
|
||||
};
|
||||
IntRange { range }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -143,13 +283,16 @@ impl Constructor {
|
|||
VariantId::StructId(_) | VariantId::UnionId(_) => Single,
|
||||
}
|
||||
}
|
||||
&Pat::Lit(expr_id) => match cx.body[expr_id] {
|
||||
Expr::Literal(Literal::Bool(val)) => IntRange(IntRange::from_bool(val)),
|
||||
_ => todo!(),
|
||||
},
|
||||
|
||||
Pat::Or(..) => panic!("bug: Or-pattern should have been expanded earlier on."),
|
||||
Pat::Missing => todo!("Fail gracefully when there is an error in a pattern"),
|
||||
pat => todo!("Constructor::from_pat {:?}", pat),
|
||||
// Pat::Range { start, end } => {}
|
||||
// Pat::Slice { prefix, slice, suffix } => {}
|
||||
// Pat::Lit(_) => {}
|
||||
// Pat::ConstBlock(_) => {}
|
||||
}
|
||||
}
|
||||
|
@ -181,7 +324,10 @@ impl Constructor {
|
|||
// Fast-track if the range is trivial. In particular, we don't do the overlapping
|
||||
// ranges check.
|
||||
IntRange(ctor_range) if !ctor_range.is_singleton() => {
|
||||
todo!("Constructor::split IntRange")
|
||||
let mut split_range = SplitIntRange::new(ctor_range.clone());
|
||||
let int_ranges = ctors.filter_map(|ctor| ctor.as_int_range());
|
||||
split_range.split(int_ranges.cloned());
|
||||
split_range.iter().map(IntRange).collect()
|
||||
}
|
||||
Slice(_) => todo!("Constructor::split Slice"),
|
||||
// Any other constructor can be used unchanged.
|
||||
|
@ -282,7 +428,8 @@ pub(super) struct SplitWildcard {
|
|||
impl SplitWildcard {
|
||||
pub(super) fn new(pcx: PatCtxt<'_>) -> Self {
|
||||
let cx = pcx.cx;
|
||||
// let make_range = |start, end| IntRange(todo!());
|
||||
let make_range =
|
||||
|start, end, scalar| IntRange(IntRange::from_range(cx, start, end, scalar));
|
||||
|
||||
// This determines the set of all possible constructors for the type `pcx.ty`. For numbers,
|
||||
// arrays and slices we use ranges and variable-length slices when appropriate.
|
||||
|
@ -293,7 +440,7 @@ impl SplitWildcard {
|
|||
// Invariant: this is empty if and only if the type is uninhabited (as determined by
|
||||
// `cx.is_uninhabited()`).
|
||||
let all_ctors = match pcx.ty.kind(&Interner) {
|
||||
TyKind::Scalar(Scalar::Bool) => todo!(),
|
||||
TyKind::Scalar(Scalar::Bool) => smallvec![make_range(0, 1, Scalar::Bool)],
|
||||
// TyKind::Array(..) if ... => todo!(),
|
||||
TyKind::Array(..) | TyKind::Slice(..) => todo!(),
|
||||
&TyKind::Adt(AdtId(hir_def::AdtId::EnumId(enum_id)), ref _substs) => {
|
||||
|
|
Loading…
Reference in a new issue