rustc_const_eval: build Pattern instead of hir::Pat for pretty-printing.
This commit is contained in:
parent
e227433dc3
commit
c001b0940c
@ -23,18 +23,12 @@
|
||||
use pattern::{FieldPattern, Pattern, PatternKind};
|
||||
use pattern::{PatternFoldable, PatternFolder};
|
||||
|
||||
use rustc::hir::def::Def;
|
||||
use rustc::hir::def_id::DefId;
|
||||
use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
|
||||
|
||||
use rustc::hir;
|
||||
use rustc::hir::def::CtorKind;
|
||||
use rustc::hir::{Pat, PatKind};
|
||||
use rustc::mir::Field;
|
||||
use rustc::util::common::ErrorReported;
|
||||
|
||||
use syntax::ast::{self, DUMMY_NODE_ID};
|
||||
use syntax::codemap::Spanned;
|
||||
use syntax::ptr::P;
|
||||
use syntax_pos::{Span, DUMMY_SP};
|
||||
|
||||
use arena::TypedArena;
|
||||
@ -74,12 +68,6 @@ fn fold_pattern(&mut self, pat: &Pattern<'tcx>) -> Pattern<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
pub const DUMMY_WILD_PAT: &'static Pat = &Pat {
|
||||
id: DUMMY_NODE_ID,
|
||||
node: PatKind::Wild,
|
||||
span: DUMMY_SP
|
||||
};
|
||||
|
||||
impl<'tcx> Pattern<'tcx> {
|
||||
fn is_wildcard(&self) -> bool {
|
||||
match *self.kind {
|
||||
@ -224,25 +212,34 @@ pub enum Constructor {
|
||||
}
|
||||
|
||||
impl<'tcx> Constructor {
|
||||
fn variant_for_adt(&self, adt: &'tcx ty::AdtDef) -> &'tcx ty::VariantDef {
|
||||
fn variant_index_for_adt(&self, adt: &'tcx ty::AdtDef) -> usize {
|
||||
match self {
|
||||
&Variant(vid) => adt.variant_with_id(vid),
|
||||
&Variant(vid) => adt.variant_index_with_id(vid),
|
||||
&Single => {
|
||||
assert_eq!(adt.variants.len(), 1);
|
||||
&adt.variants[0]
|
||||
0
|
||||
}
|
||||
_ => bug!("bad constructor {:?} for adt {:?}", self, adt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub enum Usefulness {
|
||||
#[derive(Clone)]
|
||||
pub enum Usefulness<'tcx> {
|
||||
Useful,
|
||||
UsefulWithWitness(Vec<Witness>),
|
||||
UsefulWithWitness(Vec<Witness<'tcx>>),
|
||||
NotUseful
|
||||
}
|
||||
|
||||
impl<'tcx> Usefulness<'tcx> {
|
||||
fn is_useful(&self) -> bool {
|
||||
match *self {
|
||||
NotUseful => false,
|
||||
_ => true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum WitnessPreference {
|
||||
ConstructWitness,
|
||||
@ -255,31 +252,17 @@ struct PatternContext<'tcx> {
|
||||
max_slice_length: usize,
|
||||
}
|
||||
|
||||
|
||||
fn const_val_to_expr(value: &ConstVal) -> P<hir::Expr> {
|
||||
let node = match value {
|
||||
&ConstVal::Bool(b) => ast::LitKind::Bool(b),
|
||||
_ => bug!()
|
||||
};
|
||||
P(hir::Expr {
|
||||
id: DUMMY_NODE_ID,
|
||||
node: hir::ExprLit(P(Spanned { node: node, span: DUMMY_SP })),
|
||||
span: DUMMY_SP,
|
||||
attrs: ast::ThinVec::new(),
|
||||
})
|
||||
}
|
||||
|
||||
/// A stack of patterns in reverse order of construction
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub struct Witness(Vec<P<Pat>>);
|
||||
#[derive(Clone)]
|
||||
pub struct Witness<'tcx>(Vec<Pattern<'tcx>>);
|
||||
|
||||
impl Witness {
|
||||
pub fn single_pattern(&self) -> &Pat {
|
||||
impl<'tcx> Witness<'tcx> {
|
||||
pub fn single_pattern(&self) -> &Pattern<'tcx> {
|
||||
assert_eq!(self.0.len(), 1);
|
||||
&self.0[0]
|
||||
}
|
||||
|
||||
fn push_wild_constructor<'a, 'tcx>(
|
||||
fn push_wild_constructor<'a>(
|
||||
mut self,
|
||||
cx: &MatchCheckCtxt<'a, 'tcx>,
|
||||
ctor: &Constructor,
|
||||
@ -287,7 +270,7 @@ fn push_wild_constructor<'a, 'tcx>(
|
||||
-> Self
|
||||
{
|
||||
let arity = constructor_arity(cx, ctor, ty);
|
||||
self.0.extend(repeat(DUMMY_WILD_PAT).take(arity).map(|p| P(p.clone())));
|
||||
self.0.extend(repeat(cx.wild_pattern).take(arity).cloned());
|
||||
self.apply_constructor(cx, ctor, ty)
|
||||
}
|
||||
|
||||
@ -305,7 +288,7 @@ fn push_wild_constructor<'a, 'tcx>(
|
||||
///
|
||||
/// left_ty: struct X { a: (bool, &'static str), b: usize}
|
||||
/// pats: [(false, "foo"), 42] => X { a: (false, "foo"), b: 42 }
|
||||
fn apply_constructor<'a, 'tcx>(
|
||||
fn apply_constructor<'a>(
|
||||
mut self,
|
||||
cx: &MatchCheckCtxt<'a,'tcx>,
|
||||
ctor: &Constructor,
|
||||
@ -318,60 +301,56 @@ fn apply_constructor<'a, 'tcx>(
|
||||
let mut pats = self.0.drain(len-arity..).rev();
|
||||
|
||||
match ty.sty {
|
||||
ty::TyTuple(..) => PatKind::Tuple(pats.collect(), None),
|
||||
ty::TyAdt(..) |
|
||||
ty::TyTuple(..) => {
|
||||
let pats = pats.enumerate().map(|(i, p)| {
|
||||
FieldPattern {
|
||||
field: Field::new(i),
|
||||
pattern: p
|
||||
}
|
||||
}).collect();
|
||||
|
||||
ty::TyAdt(adt, _) => {
|
||||
let v = ctor.variant_for_adt(adt);
|
||||
let qpath = hir::QPath::Resolved(None, P(hir::Path {
|
||||
span: DUMMY_SP,
|
||||
def: Def::Err,
|
||||
segments: vec![hir::PathSegment::from_name(v.name)].into(),
|
||||
}));
|
||||
match v.ctor_kind {
|
||||
CtorKind::Fictive => {
|
||||
let field_pats: hir::HirVec<_> = v.fields.iter()
|
||||
.zip(pats)
|
||||
.filter(|&(_, ref pat)| pat.node != PatKind::Wild)
|
||||
.map(|(field, pat)| Spanned {
|
||||
span: DUMMY_SP,
|
||||
node: hir::FieldPat {
|
||||
name: field.name,
|
||||
pat: pat,
|
||||
is_shorthand: false,
|
||||
}
|
||||
}).collect();
|
||||
let has_more_fields = field_pats.len() < arity;
|
||||
PatKind::Struct(qpath, field_pats, has_more_fields)
|
||||
if let ty::TyAdt(adt, _) = ty.sty {
|
||||
if adt.variants.len() > 1 {
|
||||
PatternKind::Variant {
|
||||
adt_def: adt,
|
||||
variant_index: ctor.variant_index_for_adt(adt),
|
||||
subpatterns: pats
|
||||
}
|
||||
} else {
|
||||
PatternKind::Leaf { subpatterns: pats }
|
||||
}
|
||||
CtorKind::Fn => {
|
||||
PatKind::TupleStruct(qpath, pats.collect(), None)
|
||||
}
|
||||
CtorKind::Const => PatKind::Path(qpath)
|
||||
} else {
|
||||
PatternKind::Leaf { subpatterns: pats }
|
||||
}
|
||||
}
|
||||
|
||||
ty::TyRef(_, ty::TypeAndMut { mutbl, .. }) => {
|
||||
PatKind::Ref(pats.nth(0).unwrap(), mutbl)
|
||||
ty::TyRef(..) => {
|
||||
PatternKind::Deref { subpattern: pats.nth(0).unwrap() }
|
||||
}
|
||||
|
||||
ty::TySlice(_) | ty::TyArray(..) => {
|
||||
PatKind::Slice(pats.collect(), None, hir::HirVec::new())
|
||||
PatternKind::Slice {
|
||||
prefix: pats.collect(),
|
||||
slice: None,
|
||||
suffix: vec![]
|
||||
}
|
||||
}
|
||||
|
||||
_ => {
|
||||
match *ctor {
|
||||
ConstantValue(ref v) => PatKind::Lit(const_val_to_expr(v)),
|
||||
_ => PatKind::Wild,
|
||||
ConstantValue(ref v) => PatternKind::Constant { value: v.clone() },
|
||||
_ => PatternKind::Wild,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
self.0.push(P(hir::Pat {
|
||||
id: DUMMY_NODE_ID,
|
||||
node: pat,
|
||||
span: DUMMY_SP
|
||||
}));
|
||||
self.0.push(Pattern {
|
||||
ty: ty,
|
||||
span: DUMMY_SP,
|
||||
kind: Box::new(pat),
|
||||
});
|
||||
|
||||
self
|
||||
}
|
||||
@ -528,13 +507,13 @@ pub fn is_useful<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
|
||||
matrix: &Matrix<'a, 'tcx>,
|
||||
v: &[&'a Pattern<'tcx>],
|
||||
witness: WitnessPreference)
|
||||
-> Usefulness {
|
||||
-> Usefulness<'tcx> {
|
||||
let &Matrix(ref rows) = matrix;
|
||||
debug!("is_useful({:?}, {:?})", matrix, v);
|
||||
if rows.is_empty() {
|
||||
return match witness {
|
||||
ConstructWitness => UsefulWithWitness(vec![Witness(
|
||||
repeat(DUMMY_WILD_PAT).take(v.len()).map(|p| P(p.clone())).collect()
|
||||
repeat(cx.wild_pattern).take(v.len()).cloned().collect()
|
||||
)]),
|
||||
LeaveOutWitness => Useful
|
||||
};
|
||||
@ -559,7 +538,7 @@ pub fn is_useful<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
|
||||
debug!("is_useful - expanding constructors: {:?}", constructors);
|
||||
constructors.into_iter().map(|c|
|
||||
is_useful_specialized(cx, matrix, v, c.clone(), pcx.ty, witness)
|
||||
).find(|result| result != &NotUseful).unwrap_or(NotUseful)
|
||||
).find(|result| result.is_useful()).unwrap_or(NotUseful)
|
||||
} else {
|
||||
debug!("is_useful - expanding wildcard");
|
||||
let constructors = missing_constructors(cx, matrix, pcx);
|
||||
@ -567,7 +546,7 @@ pub fn is_useful<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
|
||||
if constructors.is_empty() {
|
||||
all_constructors(cx, pcx).into_iter().map(|c| {
|
||||
is_useful_specialized(cx, matrix, v, c.clone(), pcx.ty, witness)
|
||||
}).find(|result| result != &NotUseful).unwrap_or(NotUseful)
|
||||
}).find(|result| result.is_useful()).unwrap_or(NotUseful)
|
||||
} else {
|
||||
let matrix = rows.iter().filter_map(|r| {
|
||||
if r[0].is_wildcard() {
|
||||
@ -597,7 +576,7 @@ fn is_useful_specialized<'a, 'tcx>(
|
||||
v: &[&'a Pattern<'tcx>],
|
||||
ctor: Constructor,
|
||||
lty: Ty<'tcx>,
|
||||
witness: WitnessPreference) -> Usefulness
|
||||
witness: WitnessPreference) -> Usefulness<'tcx>
|
||||
{
|
||||
let arity = constructor_arity(cx, &ctor, lty);
|
||||
let matrix = Matrix(m.iter().flat_map(|r| {
|
||||
@ -672,7 +651,7 @@ fn constructor_arity(_cx: &MatchCheckCtxt, ctor: &Constructor, ty: Ty) -> usize
|
||||
},
|
||||
ty::TyRef(..) => 1,
|
||||
ty::TyAdt(adt, _) => {
|
||||
ctor.variant_for_adt(adt).fields.len()
|
||||
adt.variants[ctor.variant_index_for_adt(adt)].fields.len()
|
||||
}
|
||||
_ => 0
|
||||
}
|
||||
|
@ -9,11 +9,10 @@
|
||||
// except according to those terms.
|
||||
|
||||
use _match::{MatchCheckCtxt, Matrix, expand_pattern, is_useful};
|
||||
use _match::{DUMMY_WILD_PAT};
|
||||
use _match::Usefulness::*;
|
||||
use _match::WitnessPreference::*;
|
||||
|
||||
use pattern::{Pattern, PatternContext, PatternError};
|
||||
use pattern::{Pattern, PatternContext, PatternError, PatternKind};
|
||||
|
||||
use eval::report_const_eval_err;
|
||||
|
||||
@ -230,9 +229,7 @@ fn check_irrefutable(&self, pat: &Pat, is_fn_arg: bool) {
|
||||
Useful => bug!()
|
||||
};
|
||||
|
||||
let pattern_string = hir::print::to_string(&self.tcx.map, |s| {
|
||||
s.print_pat(witness[0].single_pattern())
|
||||
});
|
||||
let pattern_string = witness[0].single_pattern().to_string();
|
||||
let mut diag = struct_span_err!(
|
||||
self.tcx.sess, pat.span, E0005,
|
||||
"refutable pattern in {}: `{}` not covered",
|
||||
@ -369,23 +366,21 @@ fn check_exhaustive<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
|
||||
match is_useful(cx, matrix, &[cx.wild_pattern], ConstructWitness) {
|
||||
UsefulWithWitness(pats) => {
|
||||
let witnesses = if pats.is_empty() {
|
||||
vec![DUMMY_WILD_PAT]
|
||||
vec![cx.wild_pattern]
|
||||
} else {
|
||||
pats.iter().map(|w| w.single_pattern()).collect()
|
||||
};
|
||||
match source {
|
||||
hir::MatchSource::ForLoopDesugar => {
|
||||
// `witnesses[0]` has the form `Some(<head>)`, peel off the `Some`
|
||||
let witness = match witnesses[0].node {
|
||||
PatKind::TupleStruct(_, ref pats, _) => match &pats[..] {
|
||||
&[ref pat] => &**pat,
|
||||
let witness = match *witnesses[0].kind {
|
||||
PatternKind::Variant { ref subpatterns, .. } => match &subpatterns[..] {
|
||||
&[ref pat] => &pat.pattern,
|
||||
_ => bug!(),
|
||||
},
|
||||
_ => bug!(),
|
||||
};
|
||||
let pattern_string = hir::print::to_string(&cx.tcx.map, |s| {
|
||||
s.print_pat(witness)
|
||||
});
|
||||
let pattern_string = witness.to_string();
|
||||
struct_span_err!(cx.tcx.sess, sp, E0297,
|
||||
"refutable pattern in `for` loop binding: \
|
||||
`{}` not covered",
|
||||
@ -394,24 +389,23 @@ fn check_exhaustive<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
|
||||
.emit();
|
||||
},
|
||||
_ => {
|
||||
let pattern_strings: Vec<_> = witnesses.iter().map(|w| {
|
||||
hir::print::to_string(&cx.tcx.map, |s| s.print_pat(w))
|
||||
}).collect();
|
||||
const LIMIT: usize = 3;
|
||||
let joined_patterns = match pattern_strings.len() {
|
||||
let joined_patterns = match witnesses.len() {
|
||||
0 => bug!(),
|
||||
1 => format!("`{}`", pattern_strings[0]),
|
||||
1 => format!("`{}`", witnesses[0]),
|
||||
2...LIMIT => {
|
||||
let (tail, head) = pattern_strings.split_last().unwrap();
|
||||
format!("`{}`", head.join("`, `") + "` and `" + tail)
|
||||
let (tail, head) = witnesses.split_last().unwrap();
|
||||
let head: Vec<_> = head.iter().map(|w| w.to_string()).collect();
|
||||
format!("`{}` and `{}`", head.join("`, `"), tail)
|
||||
},
|
||||
_ => {
|
||||
let (head, tail) = pattern_strings.split_at(LIMIT);
|
||||
let (head, tail) = witnesses.split_at(LIMIT);
|
||||
let head: Vec<_> = head.iter().map(|w| w.to_string()).collect();
|
||||
format!("`{}` and {} more", head.join("`, `"), tail.len())
|
||||
}
|
||||
};
|
||||
|
||||
let label_text = match pattern_strings.len(){
|
||||
let label_text = match witnesses.len() {
|
||||
1 => format!("pattern {} not covered", joined_patterns),
|
||||
_ => format!("patterns {} not covered", joined_patterns)
|
||||
};
|
||||
|
@ -14,12 +14,13 @@
|
||||
use rustc::mir::{Field, BorrowKind, Mutability};
|
||||
use rustc::ty::{self, TyCtxt, AdtDef, Ty, Region};
|
||||
use rustc::hir::{self, PatKind};
|
||||
use rustc::hir::def::Def;
|
||||
use rustc::hir::def::{Def, CtorKind};
|
||||
use rustc::hir::def_id::DefId;
|
||||
use rustc::hir::pat_util::EnumerateAndAdjustIterator;
|
||||
|
||||
use rustc_data_structures::indexed_vec::Idx;
|
||||
|
||||
use std::fmt;
|
||||
use syntax::ast;
|
||||
use syntax::ptr::P;
|
||||
use syntax_pos::Span;
|
||||
@ -105,6 +106,158 @@ pub enum PatternKind<'tcx> {
|
||||
},
|
||||
}
|
||||
|
||||
fn print_const_val(value: &ConstVal, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *value {
|
||||
ConstVal::Float(ref x) => write!(f, "{}", x),
|
||||
ConstVal::Integral(ref i) => write!(f, "{}", i),
|
||||
ConstVal::Str(ref s) => write!(f, "{:?}", &s[..]),
|
||||
ConstVal::ByteStr(ref b) => write!(f, "{:?}", &b[..]),
|
||||
ConstVal::Bool(b) => write!(f, "{:?}", b),
|
||||
ConstVal::Char(c) => write!(f, "{:?}", c),
|
||||
ConstVal::Struct(_) |
|
||||
ConstVal::Tuple(_) |
|
||||
ConstVal::Function(_) |
|
||||
ConstVal::Array(..) |
|
||||
ConstVal::Repeat(..) |
|
||||
ConstVal::Dummy => bug!("{:?} not printable in a pattern", value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> fmt::Display for Pattern<'tcx> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self.kind {
|
||||
PatternKind::Wild => write!(f, "_"),
|
||||
PatternKind::Binding { mutability, name, mode, ref subpattern, .. } => {
|
||||
let is_mut = match mode {
|
||||
BindingMode::ByValue => mutability == Mutability::Mut,
|
||||
BindingMode::ByRef(_, bk) => {
|
||||
write!(f, "ref ")?;
|
||||
bk == BorrowKind::Mut
|
||||
}
|
||||
};
|
||||
if is_mut {
|
||||
write!(f, "mut ")?;
|
||||
}
|
||||
write!(f, "{}", name)?;
|
||||
if let Some(ref subpattern) = *subpattern {
|
||||
write!(f, " @ {}", subpattern)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
PatternKind::Variant { ref subpatterns, .. } |
|
||||
PatternKind::Leaf { ref subpatterns } => {
|
||||
let variant = match *self.kind {
|
||||
PatternKind::Variant { adt_def, variant_index, .. } => {
|
||||
Some(&adt_def.variants[variant_index])
|
||||
}
|
||||
_ => if let ty::TyAdt(adt, _) = self.ty.sty {
|
||||
Some(adt.struct_variant())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
let mut first = true;
|
||||
let mut start_or_continue = || if first { first = false; "" } else { ", " };
|
||||
|
||||
if let Some(variant) = variant {
|
||||
write!(f, "{}", variant.name)?;
|
||||
|
||||
// Only for TyAdt we can have `S {...}`,
|
||||
// which we handle separately here.
|
||||
if variant.ctor_kind == CtorKind::Fictive {
|
||||
write!(f, " {{ ")?;
|
||||
|
||||
let mut printed = 0;
|
||||
for p in subpatterns {
|
||||
if let PatternKind::Wild = *p.pattern.kind {
|
||||
continue;
|
||||
}
|
||||
let name = variant.fields[p.field.index()].name;
|
||||
write!(f, "{}{}: {}", start_or_continue(), name, p.pattern)?;
|
||||
printed += 1;
|
||||
}
|
||||
|
||||
if printed < variant.fields.len() {
|
||||
write!(f, "{}..", start_or_continue())?;
|
||||
}
|
||||
|
||||
return write!(f, " }}");
|
||||
}
|
||||
}
|
||||
|
||||
let num_fields = variant.map_or(subpatterns.len(), |v| v.fields.len());
|
||||
if num_fields != 0 || variant.is_none() {
|
||||
write!(f, "(")?;
|
||||
for i in 0..num_fields {
|
||||
write!(f, "{}", start_or_continue())?;
|
||||
|
||||
// Common case: the field is where we expect it.
|
||||
if let Some(p) = subpatterns.get(i) {
|
||||
if p.field.index() == i {
|
||||
write!(f, "{}", p.pattern)?;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, we have to go looking for it.
|
||||
if let Some(p) = subpatterns.iter().find(|p| p.field.index() == i) {
|
||||
write!(f, "{}", p.pattern)?;
|
||||
} else {
|
||||
write!(f, "_")?;
|
||||
}
|
||||
}
|
||||
write!(f, ")")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
PatternKind::Deref { ref subpattern } => {
|
||||
match self.ty.sty {
|
||||
ty::TyBox(_) => write!(f, "box ")?,
|
||||
ty::TyRef(_, mt) => {
|
||||
write!(f, "&")?;
|
||||
if mt.mutbl == hir::MutMutable {
|
||||
write!(f, "mut ")?;
|
||||
}
|
||||
}
|
||||
_ => bug!("{} is a bad Deref pattern type", self.ty)
|
||||
}
|
||||
write!(f, "{}", subpattern)
|
||||
}
|
||||
PatternKind::Constant { ref value } => {
|
||||
print_const_val(value, f)
|
||||
}
|
||||
PatternKind::Range { ref lo, ref hi } => {
|
||||
print_const_val(lo, f)?;
|
||||
write!(f, "...")?;
|
||||
print_const_val(hi, f)
|
||||
}
|
||||
PatternKind::Slice { ref prefix, ref slice, ref suffix } |
|
||||
PatternKind::Array { ref prefix, ref slice, ref suffix } => {
|
||||
let mut first = true;
|
||||
let mut start_or_continue = || if first { first = false; "" } else { ", " };
|
||||
write!(f, "[")?;
|
||||
for p in prefix {
|
||||
write!(f, "{}{}", start_or_continue(), p)?;
|
||||
}
|
||||
if let Some(ref slice) = *slice {
|
||||
write!(f, "{}", start_or_continue())?;
|
||||
match *slice.kind {
|
||||
PatternKind::Wild => {}
|
||||
_ => write!(f, "{}", slice)?
|
||||
}
|
||||
write!(f, "..")?;
|
||||
}
|
||||
for p in suffix {
|
||||
write!(f, "{}{}", start_or_continue(), p)?;
|
||||
}
|
||||
write!(f, "]")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PatternContext<'a, 'gcx: 'tcx, 'tcx: 'a> {
|
||||
pub tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||
pub errors: Vec<PatternError>,
|
||||
|
Loading…
Reference in New Issue
Block a user