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_supreme::error::ErrorTree<String>> {
43 use std::str::FromStr;
44 Self::from_str(i)
45 }
46
47 fn len(&self) -> usize {
48 self.elems.len()
49 }
50
51 pub fn matches(&self, ctx: &TranslatedCrate, name: &Name) -> bool {
52 self.matches_with_generics(ctx, name, None)
53 }
54
55 pub fn matches_item(&self, ctx: &TranslatedCrate, item: ItemRef<'_>) -> bool {
56 let generics = item.identity_args();
57 let name = &item.item_meta().name;
58 self.matches_with_generics(ctx, name, Some(&generics))
59 }
60
61 pub fn matches_with_generics(
62 &self,
63 ctx: &TranslatedCrate,
64 name: &Name,
65 args: Option<&GenericArgs>,
66 ) -> bool {
67 let mut scrutinee_elems = name.name.as_slice();
68 let mut args: Option<GenericArgs> = args.cloned();
69 if let [prefix @ .., PathElem::Instantiated(mono_args)] = scrutinee_elems {
70 assert!(
72 args.is_none()
73 || args.as_ref().unwrap().len() == args.as_ref().unwrap().regions.elem_count(),
74 "In pattern \"{}\" matching against name \"{}\": we have both monomorphized generics {} and regular generics {}",
75 self,
76 name.with_ctx(&ctx.into_fmt()),
77 mono_args.skip_binder.with_ctx(&ctx.into_fmt()),
78 args.unwrap().with_ctx(&ctx.into_fmt())
79 );
80 let mut mono_args = mono_args.skip_binder.clone();
84 if let Some(args) = args {
85 mono_args.regions.extend(args.regions.into_iter());
87 }
88 scrutinee_elems = prefix;
89 args = Some(mono_args);
90 };
91 let args = args.as_ref();
92 if let Some(PatElem::Impl(_)) = self.elems.first() {
96 if let Some((i, _)) = scrutinee_elems
97 .iter()
98 .enumerate()
99 .rfind(|(_, elem)| elem.is_impl())
100 {
101 scrutinee_elems = &scrutinee_elems[i..];
102 }
103 }
104
105 let zipped = self.elems.iter().zip_longest(scrutinee_elems).collect_vec();
106 let zipped_len = zipped.len();
107 for (i, x) in zipped.into_iter().enumerate() {
108 let is_last = i + 1 == zipped_len;
109 match x {
110 EitherOrBoth::Both(pat, elem) => {
111 let args = if is_last { args } else { None };
112 if !pat.matches_with_generics(ctx, elem, args) {
113 return false;
114 }
115 }
116 EitherOrBoth::Right(_) => return true,
119 EitherOrBoth::Left(_) => return false,
121 }
122 }
123 true
125 }
126
127 pub fn matches_ty(&self, ctx: &TranslatedCrate, ty: &Ty) -> bool {
128 if let [PatElem::Glob] = self.elems.as_slice() {
129 return true;
130 }
131 match ty.kind() {
132 TyKind::Adt(tref) => {
133 let args = &tref.generics;
134 match tref.id {
135 TypeId::Adt(type_id) => {
136 let Some(type_name) = ctx.item_name(type_id) else {
137 return false;
138 };
139 self.matches_with_generics(ctx, type_name, Some(args))
140 }
141 TypeId::Builtin(builtin_ty) => {
142 let name = builtin_ty.get_name();
143 self.matches_with_generics(ctx, &name, Some(args))
144 }
145 TypeId::Tuple => false,
146 }
147 }
148 TyKind::Array(ty, len) => {
149 let type_name = Name::from_path(&["Array"]);
150 let args = GenericArgs {
151 regions: [].into(),
152 types: [ty.clone()].into(),
153 const_generics: [len.clone()].into(),
154 trait_refs: [].into(),
155 };
156 self.matches_with_generics(ctx, &type_name, Some(&args))
157 }
158 TyKind::Slice(ty) => {
159 let type_name = Name::from_path(&["Slice"]);
160 let args = GenericArgs {
161 regions: [].into(),
162 types: [ty.clone()].into(),
163 const_generics: [].into(),
164 trait_refs: [].into(),
165 };
166 self.matches_with_generics(ctx, &type_name, Some(&args))
167 }
168 TyKind::TypeVar(..)
169 | TyKind::Literal(..)
170 | TyKind::Never
171 | TyKind::Ref(..)
172 | TyKind::RawPtr(..)
173 | TyKind::TraitType(..)
174 | TyKind::DynTrait(..)
175 | TyKind::FnPtr(..)
176 | TyKind::FnDef(..)
177 | TyKind::PtrMetadata(..)
178 | TyKind::Error(..) => false,
179 }
180 }
181
182 pub fn matches_const(&self, _ctx: &TranslatedCrate, _c: &ConstGeneric) -> bool {
183 if let [PatElem::Glob] = self.elems.as_slice() {
184 return true;
185 }
186 todo!("non-trivial const generics patterns aren't implemented")
187 }
188
189 pub fn compare(&self, other: &Self) -> Ordering {
193 use Ordering::*;
194 use PatElem::*;
195 match self.len().cmp(&other.len()) {
196 o @ (Less | Greater) => return o,
197 _ if self.len() == 0 => return Equal,
198 Equal => {}
199 }
200 match (self.elems.last().unwrap(), other.elems.last().unwrap()) {
201 (Glob, Glob) => Equal,
202 (Glob, _) => Less,
203 (_, Glob) => Greater,
204 _ => Equal,
206 }
207 }
208}
209
210impl Ord for Pattern {
213 fn cmp(&self, other: &Self) -> Ordering {
214 self.compare(other)
215 }
216}
217impl PartialOrd for Pattern {
218 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
219 Some(self.compare(other))
220 }
221}
222
223impl PatElem {
224 fn matches_with_generics(
225 &self,
226 ctx: &TranslatedCrate,
227 elem: &PathElem,
228 args: Option<&GenericArgs>,
229 ) -> bool {
230 match (self, elem) {
231 (PatElem::Glob, _) => true,
232 (
233 PatElem::Ident {
234 name: pat_ident,
235 generics,
236 ..
237 },
238 PathElem::Ident(ident, _),
239 ) => {
240 let same_ident =
242 pat_ident == ident || (pat_ident == "crate" && ident == &ctx.crate_name);
243 same_ident && PatTy::matches_generics(ctx, generics, args)
244 }
245 (PatElem::Impl(_pat), PathElem::Impl(ImplElem::Ty(..))) => {
246 false
248 }
249 (PatElem::Impl(pat), PathElem::Impl(ImplElem::Trait(impl_id))) => {
250 let Some(timpl) = ctx.trait_impls.get(*impl_id) else {
251 return false;
252 };
253 let Some(trait_name) = ctx.item_name(timpl.impl_trait.id) else {
254 return false;
255 };
256 pat.matches_with_generics(ctx, trait_name, Some(&timpl.impl_trait.generics))
257 }
258 _ => false,
259 }
260 }
261}
262
263impl PatTy {
264 pub fn matches_generics(
265 ctx: &TranslatedCrate,
266 pats: &[Self],
267 generics: Option<&GenericArgs>,
268 ) -> bool {
269 let Some(generics) = generics else {
270 return true;
272 };
273 if pats.is_empty() {
274 return true;
276 }
277 if pats.len() != generics.types.elem_count() + generics.const_generics.elem_count() {
279 return false;
280 }
281 let (type_pats, const_pats) = pats.split_at(generics.types.elem_count());
282 let types_match = generics
283 .types
284 .iter()
285 .zip(type_pats)
286 .all(|(ty, pat)| pat.matches_ty(ctx, ty));
287 let consts_match = generics
288 .const_generics
289 .iter()
290 .zip(const_pats)
291 .all(|(c, pat)| pat.matches_const(ctx, c));
292 types_match && consts_match
293 }
294
295 pub fn matches_ty(&self, ctx: &TranslatedCrate, ty: &Ty) -> bool {
296 match (self, ty.kind()) {
297 (PatTy::Pat(p), _) => p.matches_ty(ctx, ty),
298 (PatTy::Ref(pat_mtbl, p_ty), TyKind::Ref(_, ty, ty_mtbl)) => {
299 pat_mtbl == ty_mtbl && p_ty.matches_ty(ctx, ty)
300 }
301 _ => false,
302 }
303 }
304
305 pub fn matches_const(&self, ctx: &TranslatedCrate, c: &ConstGeneric) -> bool {
306 match self {
307 PatTy::Pat(p) => p.matches_const(ctx, c),
308 PatTy::Ref(..) => false,
309 }
310 }
311}
312
313#[test]
314fn test_compare() {
315 use Ordering::*;
316 let tests = [
317 ("_", Less, "crate"),
318 ("crate::_", Less, "crate::foo"),
319 ("crate::foo", Less, "crate::foo::_"),
320 ];
321 for (x, o, y) in tests {
322 let x = Pattern::parse(x).unwrap();
323 let y = Pattern::parse(y).unwrap();
324 assert_eq!(x.compare(&y), o);
325 }
326}