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 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(mono_args)] = scrutinee_elems {
76 assert!(
78 args.is_none()
79 || args.as_ref().unwrap().len() == args.as_ref().unwrap().regions.len(),
80 "In pattern \"{}\" matching against name \"{}\": we have both monomorphized generics {} and regular generics {}",
81 self,
82 name.with_ctx(&ctx.into_fmt()),
83 mono_args.skip_binder.with_ctx(&ctx.into_fmt()),
84 args.unwrap().with_ctx(&ctx.into_fmt())
85 );
86 let mut mono_args = mono_args.skip_binder.clone();
90 if let Some(args) = args {
91 mono_args.regions.extend(args.regions);
93 }
94 scrutinee_elems = prefix;
95 args = Some(mono_args);
96 };
97 let args = args.as_ref();
98 if let Some(PatElem::Impl(_)) = self.elems.first()
102 && let Some((i, _)) = scrutinee_elems
103 .iter()
104 .enumerate()
105 .rfind(|(_, elem)| elem.is_impl())
106 {
107 scrutinee_elems = &scrutinee_elems[i..];
108 }
109
110 let zipped = self.elems.iter().zip_longest(scrutinee_elems).collect_vec();
111 let zipped_len = zipped.len();
112 for (i, x) in zipped.into_iter().enumerate() {
113 let is_last = i + 1 == zipped_len;
114 match x {
115 EitherOrBoth::Both(pat, elem) => {
116 let args = if is_last { args } else { None };
117 if !pat.matches_with_generics(ctx, elem, args) {
118 return false;
119 }
120 }
121 EitherOrBoth::Right(_) => return true,
124 EitherOrBoth::Left(_) => return false,
126 }
127 }
128 true
130 }
131
132 pub fn matches_ty(&self, ctx: &TranslatedCrate, ty: &Ty) -> bool {
133 if let [PatElem::Glob] = self.elems.as_slice() {
134 return true;
135 }
136 match ty.kind() {
137 TyKind::Adt(tref) => {
138 let args = &tref.generics;
139 match tref.id {
140 TypeId::Adt(type_id) => {
141 let Some(type_name) = ctx.item_name(type_id) else {
142 return false;
143 };
144 self.matches_with_generics(ctx, type_name, Some(args))
145 }
146 TypeId::Builtin(builtin_ty) => {
147 let name = builtin_ty.get_name();
148 self.matches_with_generics(ctx, &name, Some(args))
149 }
150 TypeId::Tuple => false,
151 }
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::TypeVar(..)
174 | TyKind::Literal(..)
175 | TyKind::Never
176 | TyKind::Ref(..)
177 | TyKind::RawPtr(..)
178 | TyKind::TraitType(..)
179 | TyKind::DynTrait(..)
180 | TyKind::FnPtr(..)
181 | TyKind::FnDef(..)
182 | TyKind::PtrMetadata(..)
183 | TyKind::Error(..) => false,
184 }
185 }
186
187 pub fn matches_const(&self, _ctx: &TranslatedCrate, _c: &ConstantExpr) -> bool {
188 if let [PatElem::Glob] = self.elems.as_slice() {
189 return true;
190 }
191 todo!("non-trivial const generics patterns aren't implemented")
192 }
193
194 pub fn compare(&self, other: &Self) -> Ordering {
198 use Ordering::*;
199 use PatElem::*;
200 match self.len().cmp(&other.len()) {
201 o @ (Less | Greater) => return o,
202 _ if self.len() == 0 => return Equal,
203 Equal => {}
204 }
205 match (self.elems.last().unwrap(), other.elems.last().unwrap()) {
206 (Glob, Glob) => Equal,
207 (Glob, _) => Less,
208 (_, Glob) => Greater,
209 _ => Equal,
211 }
212 }
213}
214
215impl Ord for Pattern {
218 fn cmp(&self, other: &Self) -> Ordering {
219 self.compare(other)
220 }
221}
222impl PartialOrd for Pattern {
223 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
224 Some(self.cmp(other))
225 }
226}
227
228impl PatElem {
229 fn matches_with_generics(
230 &self,
231 ctx: &TranslatedCrate,
232 elem: &PathElem,
233 args: Option<&GenericArgs>,
234 ) -> bool {
235 match (self, elem) {
236 (PatElem::Glob, _) => true,
237 (
238 PatElem::Ident {
239 name: pat_ident,
240 generics,
241 ..
242 },
243 PathElem::Ident(ident, _),
244 ) => {
245 let same_ident =
247 pat_ident == ident || (pat_ident == "crate" && ident == &ctx.crate_name);
248 same_ident && PatTy::matches_generics(ctx, generics, args)
249 }
250 (PatElem::Impl(_pat), PathElem::Impl(ImplElem::Ty(..))) => {
251 false
253 }
254 (PatElem::Impl(pat), PathElem::Impl(ImplElem::Trait(impl_id))) => {
255 let Some(timpl) = ctx.trait_impls.get(*impl_id) else {
256 return false;
257 };
258 let Some(trait_name) = ctx.item_name(timpl.impl_trait.id) else {
259 return false;
260 };
261 pat.matches_with_generics(ctx, trait_name, Some(&timpl.impl_trait.generics))
262 }
263 _ => false,
264 }
265 }
266}
267
268impl PatTy {
269 pub fn matches_generics(
270 ctx: &TranslatedCrate,
271 pats: &[Self],
272 generics: Option<&GenericArgs>,
273 ) -> bool {
274 let Some(generics) = generics else {
275 return true;
277 };
278 if pats.is_empty() {
279 return true;
281 }
282 if pats.len() != generics.types.len() + generics.const_generics.len() {
284 return false;
285 }
286 let (type_pats, const_pats) = pats.split_at(generics.types.len());
287 let types_match = generics
288 .types
289 .iter()
290 .zip(type_pats)
291 .all(|(ty, pat)| pat.matches_ty(ctx, ty));
292 let consts_match = generics
293 .const_generics
294 .iter()
295 .zip(const_pats)
296 .all(|(c, pat)| pat.matches_const(ctx, c));
297 types_match && consts_match
298 }
299
300 pub fn matches_ty(&self, ctx: &TranslatedCrate, ty: &Ty) -> bool {
301 match (self, ty.kind()) {
302 (PatTy::Pat(p), _) => p.matches_ty(ctx, ty),
303 (PatTy::Ref(pat_mtbl, p_ty), TyKind::Ref(_, ty, ty_mtbl)) => {
304 pat_mtbl == ty_mtbl && p_ty.matches_ty(ctx, ty)
305 }
306 _ => false,
307 }
308 }
309
310 pub fn matches_const(&self, ctx: &TranslatedCrate, c: &ConstantExpr) -> bool {
311 match self {
312 PatTy::Pat(p) => p.matches_const(ctx, c),
313 PatTy::Ref(..) => false,
314 }
315 }
316}
317
318#[test]
319fn test_compare() {
320 use Ordering::*;
321 let tests = [
322 ("_", Less, "crate"),
323 ("crate::_", Less, "crate::foo"),
324 ("crate::foo", Less, "crate::foo::_"),
325 ];
326 for (x, o, y) in tests {
327 let x = Pattern::parse(x).unwrap();
328 let y = Pattern::parse(y).unwrap();
329 assert_eq!(x.compare(&y), o);
330 }
331}