1use std::cmp::Ordering;
2
3use itertools::{EitherOrBoth, Itertools};
4use serde::{Deserialize, Serialize};
5
6use crate::{ast::*, formatter::IntoFormatter, pretty::FmtWithCtx};
7
8mod parser;
9
10pub use Pattern as NamePattern;
11
12#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
13pub struct Pattern {
14 pub elems: Vec<PatElem>,
15}
16
17#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
18pub enum PatElem {
19 Ident {
21 name: String,
22 generics: Vec<PatTy>,
23 is_trait: bool,
25 },
26 Impl(Box<Pattern>),
29 Glob,
31}
32
33#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
34pub enum PatTy {
35 Pat(Pattern),
37 Ref(RefKind, Box<Self>),
39}
40
41impl Pattern {
42 pub fn parse(i: &str) -> Result<Self, nom::error::Error<String>> {
43 use std::str::FromStr;
44 Self::from_str(i)
45 }
46 pub fn impl_for(trait_pat: Self) -> Self {
48 Pattern {
49 elems: vec![PatElem::Impl(Box::new(trait_pat))],
50 }
51 }
52
53 fn len(&self) -> usize {
54 self.elems.len()
55 }
56
57 pub fn matches(&self, ctx: &TranslatedCrate, name: &Name) -> bool {
58 self.matches_with_generics(ctx, name, None)
59 }
60
61 pub fn matches_item(&self, ctx: &TranslatedCrate, item: ItemRef<'_>) -> bool {
62 let generics = item.identity_args();
63 let name = &item.item_meta().name;
64 self.matches_with_generics(ctx, name, Some(&generics))
65 }
66
67 pub fn matches_with_generics(
68 &self,
69 ctx: &TranslatedCrate,
70 name: &Name,
71 args: Option<&GenericArgs>,
72 ) -> bool {
73 let mut scrutinee_elems = name.name.as_slice();
74 let mut args: Option<GenericArgs> = args.cloned();
75 if let [prefix @ .., PathElem::Instantiated(instantiation)] = scrutinee_elems {
76 args = match args {
79 None => None,
80 Some(args) if instantiation.params.is_empty() => {
81 assert!(
84 args.len() == args.regions.len(),
85 "In pattern \"{}\" matching against name \"{}\": we have both monomorphized generics {} and regular generics {}",
86 self,
87 name.with_ctx(&ctx.into_fmt()),
88 instantiation.skip_binder.with_ctx(&ctx.into_fmt()),
89 args.with_ctx(&ctx.into_fmt())
90 );
91 let mut mono_args = instantiation.skip_binder.clone();
93 mono_args.regions.extend(args.regions);
94 Some(mono_args)
95 }
96 Some(args) => {
97 assert!(
98 generic_args_match_params(&instantiation.params, &args),
99 "In pattern \"{}\" matching against name \"{}\": the instantiated item generics {} do not match the item parameters",
100 self,
101 name.with_ctx(&ctx.into_fmt()),
102 args.with_ctx(&ctx.into_fmt())
103 );
104 Some(instantiation.as_ref().clone().apply(&args))
105 }
106 };
107 scrutinee_elems = prefix;
108 };
109 let args = args.as_ref();
110 if let Some(PatElem::Impl(_)) = self.elems.first()
114 && let Some((i, _)) = scrutinee_elems
115 .iter()
116 .enumerate()
117 .rfind(|(_, elem)| elem.is_impl())
118 {
119 scrutinee_elems = &scrutinee_elems[i..];
120 }
121
122 let zipped = self.elems.iter().zip_longest(scrutinee_elems).collect_vec();
123 let zipped_len = zipped.len();
124 for (i, x) in zipped.into_iter().enumerate() {
125 let is_last = i + 1 == zipped_len;
126 match x {
127 EitherOrBoth::Both(pat, elem) => {
128 let args = if is_last { args } else { None };
129 if !pat.matches_with_generics(ctx, elem, args) {
130 return false;
131 }
132 }
133 EitherOrBoth::Right(_) => return true,
136 EitherOrBoth::Left(_) => return false,
138 }
139 }
140 true
142 }
143
144 pub fn matches_ty(&self, ctx: &TranslatedCrate, ty: &Ty) -> bool {
145 if let [PatElem::Glob] = self.elems.as_slice() {
146 return true;
147 }
148 match ty.kind() {
149 TyKind::Adt(tref) => {
150 let type_name = ctx.item_name(tref.id);
151 self.matches_with_generics(ctx, type_name, Some(&tref.generics))
152 }
153 TyKind::Array(ty, len) => {
154 let type_name = Name::from_path(&["Array"]);
155 let args = GenericArgs {
156 regions: [].into(),
157 types: [ty.clone()].into(),
158 const_generics: [len.clone()].into(),
159 trait_refs: [].into(),
160 };
161 self.matches_with_generics(ctx, &type_name, Some(&args))
162 }
163 TyKind::Slice(ty) => {
164 let type_name = Name::from_path(&["Slice"]);
165 let args = GenericArgs {
166 regions: [].into(),
167 types: [ty.clone()].into(),
168 const_generics: [].into(),
169 trait_refs: [].into(),
170 };
171 self.matches_with_generics(ctx, &type_name, Some(&args))
172 }
173 TyKind::Pattern(ty, _) => self.matches_ty(ctx, ty),
174 TyKind::Literal(ty) => matches!(
175 self.elems.as_slice(),
176 [PatElem::Ident { name, generics, .. }]
177 if generics.is_empty() && name == &ty.to_string()
178 ),
179 TyKind::TypeVar(..)
180 | TyKind::Never
181 | TyKind::Ref(..)
182 | TyKind::RawPtr(..)
183 | TyKind::TraitType(..)
184 | TyKind::DynTrait(..)
185 | TyKind::FnPtr(..)
186 | TyKind::FnDef(..)
187 | TyKind::PtrMetadata(..)
188 | TyKind::Error(..) => false,
189 }
190 }
191
192 pub fn matches_const(&self, _ctx: &TranslatedCrate, _c: &ConstantExpr) -> bool {
193 if let [PatElem::Glob] = self.elems.as_slice() {
194 return true;
195 }
196 todo!("non-trivial const generics patterns aren't implemented")
197 }
198
199 pub fn compare(&self, other: &Self) -> Ordering {
203 use Ordering::*;
204 use PatElem::*;
205 match self.len().cmp(&other.len()) {
206 o @ (Less | Greater) => return o,
207 _ if self.len() == 0 => return Equal,
208 Equal => {}
209 }
210 match (self.elems.last().unwrap(), other.elems.last().unwrap()) {
211 (Glob, Glob) => Equal,
212 (Glob, _) => Less,
213 (_, Glob) => Greater,
214 _ => Equal,
216 }
217 }
218}
219
220fn generic_args_match_params(params: &GenericParams, args: &GenericArgs) -> bool {
221 params.regions.len() == args.regions.len()
222 && params.types.len() == args.types.len()
223 && params.const_generics.len() == args.const_generics.len()
224 && params.trait_clauses.len() == args.trait_refs.len()
225}
226
227impl Ord for Pattern {
230 fn cmp(&self, other: &Self) -> Ordering {
231 self.compare(other)
232 }
233}
234impl PartialOrd for Pattern {
235 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
236 Some(self.cmp(other))
237 }
238}
239
240impl PatElem {
241 fn matches_with_generics(
242 &self,
243 ctx: &TranslatedCrate,
244 elem: &PathElem,
245 args: Option<&GenericArgs>,
246 ) -> bool {
247 match (self, elem) {
248 (PatElem::Glob, _) => true,
249 (
250 PatElem::Ident {
251 name: pat_ident,
252 generics,
253 ..
254 },
255 PathElem::Ident(ident, _),
256 ) => {
257 let same_ident =
259 pat_ident == ident || (pat_ident == "crate" && ident == &ctx.crate_name);
260 same_ident && PatTy::matches_generics(ctx, generics, args)
261 }
262 (
263 PatElem::Ident {
264 name: pat_ident,
265 generics,
266 ..
267 },
268 PathElem::Builtin(BuiltinPathElem::Str),
269 ) => pat_ident == "str" && PatTy::matches_generics(ctx, generics, args),
270 (PatElem::Impl(_pat), PathElem::Impl(ImplElem::Ty(..))) => {
271 false
273 }
274 (PatElem::Impl(pat), PathElem::Impl(ImplElem::Trait(impl_id))) => {
275 let Some(timpl) = ctx.trait_impls.get(*impl_id) else {
276 return false;
277 };
278 let trait_name = ctx.item_name(timpl.impl_trait.id);
279 pat.matches_with_generics(ctx, trait_name, Some(&timpl.impl_trait.generics))
280 }
281 _ => false,
282 }
283 }
284}
285
286impl PatTy {
287 pub fn matches_generics(
288 ctx: &TranslatedCrate,
289 pats: &[Self],
290 generics: Option<&GenericArgs>,
291 ) -> bool {
292 let Some(generics) = generics else {
293 return true;
295 };
296 if pats.is_empty() {
297 return true;
299 }
300 if pats.len() != generics.types.len() + generics.const_generics.len() {
302 return false;
303 }
304 let (type_pats, const_pats) = pats.split_at(generics.types.len());
305 let types_match = generics
306 .types
307 .iter()
308 .zip(type_pats)
309 .all(|(ty, pat)| pat.matches_ty(ctx, ty));
310 let consts_match = generics
311 .const_generics
312 .iter()
313 .zip(const_pats)
314 .all(|(c, pat)| pat.matches_const(ctx, c));
315 types_match && consts_match
316 }
317
318 pub fn matches_ty(&self, ctx: &TranslatedCrate, ty: &Ty) -> bool {
319 match (self, ty.kind()) {
320 (PatTy::Pat(p), _) => p.matches_ty(ctx, ty),
321 (PatTy::Ref(pat_mtbl, p_ty), TyKind::Ref(_, ty, ty_mtbl)) => {
322 pat_mtbl == ty_mtbl && p_ty.matches_ty(ctx, ty)
323 }
324 _ => false,
325 }
326 }
327
328 pub fn matches_const(&self, ctx: &TranslatedCrate, c: &ConstantExpr) -> bool {
329 match self {
330 PatTy::Pat(p) => p.matches_const(ctx, c),
331 PatTy::Ref(..) => false,
332 }
333 }
334}
335
336#[test]
337fn test_compare() {
338 use Ordering::*;
339 let tests = [
340 ("_", Less, "crate"),
341 ("crate::_", Less, "crate::foo"),
342 ("crate::foo", Less, "crate::foo::_"),
343 ];
344 for (x, o, y) in tests {
345 let x = Pattern::parse(x).unwrap();
346 let y = Pattern::parse(y).unwrap();
347 assert_eq!(x.compare(&y), o);
348 }
349}