1use std::cmp::Ordering;
2
3use serde::{Deserialize, Serialize};
4
5use crate::{ast::*, formatter::IntoFormatter, pretty::FmtWithCtx};
6
7mod parser;
8
9pub use Pattern as NamePattern;
10
11#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
12pub struct Pattern {
13 pub elems: Vec<PatElem>,
14}
15
16#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
17pub enum PatElem {
18 Ident {
20 name: String,
21 generics: Vec<PatTy>,
22 is_trait: bool,
24 },
25 Impl(Box<Pattern>),
28 Glob,
30}
31
32#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
33pub enum PatTy {
34 Pat(Pattern),
36 Ref(RefKind, Box<Self>),
38}
39
40impl Pattern {
41 pub fn parse(i: &str) -> Result<Self, nom::error::Error<String>> {
42 use std::str::FromStr;
43 Self::from_str(i)
44 }
45 pub fn impl_for(trait_pat: Self) -> Self {
47 Pattern {
48 elems: vec![PatElem::Impl(Box::new(trait_pat))],
49 }
50 }
51
52 fn len(&self) -> usize {
53 self.elems.len()
54 }
55
56 pub fn matches(&self, ctx: &TranslatedCrate, name: &Name) -> bool {
57 self.matches_with_generics(ctx, name, None)
58 }
59
60 pub fn matches_item(&self, ctx: &TranslatedCrate, item: ItemRef<'_>) -> bool {
61 let generics = item.identity_args();
62 let name = &item.item_meta().name;
63 self.matches_with_generics(ctx, name, Some(&generics))
64 }
65
66 pub fn matches_with_generics(
67 &self,
68 ctx: &TranslatedCrate,
69 name: &Name,
70 args: Option<&GenericArgs>,
71 ) -> bool {
72 let mut scrutinee_elems = name.name.as_slice();
73 let instantiated_args: Option<GenericArgs>;
74 let mut args: Option<&GenericArgs> = args;
75 if let [prefix @ .., PathElem::Instantiated(instantiation)] = scrutinee_elems {
76 instantiated_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.iter().cloned());
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 args = instantiated_args.as_ref();
108 scrutinee_elems = prefix;
109 };
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 if self.elems.len() > scrutinee_elems.len() {
124 return false;
125 }
126 let same_length = self.elems.len() == scrutinee_elems.len();
129 for (i, pat) in self.elems.iter().enumerate() {
130 let is_last = same_length && i + 1 == self.elems.len();
131 let args = if is_last { args } else { None };
132 if !pat.matches_with_generics(ctx, &scrutinee_elems[i], args) {
133 return false;
134 }
135 }
136 true
137 }
138
139 pub fn matches_ty(&self, ctx: &TranslatedCrate, ty: &Ty) -> bool {
140 if let [PatElem::Glob] = self.elems.as_slice() {
141 return true;
142 }
143 match ty.kind() {
144 TyKind::Adt(tref) => {
145 let type_name = ctx.item_name(tref.id);
146 self.matches_with_generics(ctx, type_name, Some(&tref.generics))
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::Pattern(ty, _) => self.matches_ty(ctx, ty),
169 TyKind::Scalar(ty) => matches!(
170 self.elems.as_slice(),
171 [PatElem::Ident { name, generics, .. }]
172 if generics.is_empty() && name == &ty.to_string()
173 ),
174 TyKind::TypeVar(..)
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
215fn generic_args_match_params(params: &GenericParams, args: &GenericArgs) -> bool {
216 params.regions.len() == args.regions.len()
217 && params.types.len() == args.types.len()
218 && params.const_generics.len() == args.const_generics.len()
219 && params.trait_clauses.len() == args.trait_refs.len()
220}
221
222impl Ord for Pattern {
225 fn cmp(&self, other: &Self) -> Ordering {
226 self.compare(other)
227 }
228}
229impl PartialOrd for Pattern {
230 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
231 Some(self.cmp(other))
232 }
233}
234
235impl PatElem {
236 fn matches_with_generics(
237 &self,
238 ctx: &TranslatedCrate,
239 elem: &PathElem,
240 args: Option<&GenericArgs>,
241 ) -> bool {
242 match (self, elem) {
243 (PatElem::Glob, _) => true,
244 (
245 PatElem::Ident {
246 name: pat_ident,
247 generics,
248 ..
249 },
250 PathElem::Ident(ident, _),
251 ) => {
252 let same_ident =
254 pat_ident == ident || (pat_ident == "crate" && ident == &ctx.crate_name);
255 same_ident && PatTy::matches_generics(ctx, generics, args)
256 }
257 (
258 PatElem::Ident {
259 name: pat_ident,
260 generics,
261 ..
262 },
263 PathElem::Builtin(builtin, _),
264 ) => {
265 !builtin.is_tuple()
266 && pat_ident == builtin.ident()
267 && PatTy::matches_generics(ctx, generics, args)
268 }
269 (PatElem::Impl(_pat), PathElem::Impl(ImplElem::Ty(..))) => {
270 false
272 }
273 (PatElem::Impl(pat), PathElem::Impl(ImplElem::Trait(impl_id))) => {
274 let Some(timpl) = ctx.trait_impls.get(*impl_id) else {
275 return false;
276 };
277 let trait_name = ctx.item_name(timpl.impl_trait.id);
278 pat.matches_with_generics(ctx, trait_name, Some(&timpl.impl_trait.generics))
279 }
280 _ => false,
281 }
282 }
283}
284
285impl PatTy {
286 pub fn matches_generics(
287 ctx: &TranslatedCrate,
288 pats: &[Self],
289 generics: Option<&GenericArgs>,
290 ) -> bool {
291 let Some(generics) = generics else {
292 return true;
294 };
295 if pats.is_empty() {
296 return true;
298 }
299 if pats.len() != generics.types.len() + generics.const_generics.len() {
301 return false;
302 }
303 let (type_pats, const_pats) = pats.split_at(generics.types.len());
304 let types_match = generics
305 .types
306 .iter()
307 .zip(type_pats)
308 .all(|(ty, pat)| pat.matches_ty(ctx, ty));
309 let consts_match = generics
310 .const_generics
311 .iter()
312 .zip(const_pats)
313 .all(|(c, pat)| pat.matches_const(ctx, c));
314 types_match && consts_match
315 }
316
317 pub fn matches_ty(&self, ctx: &TranslatedCrate, ty: &Ty) -> bool {
318 match (self, ty.kind()) {
319 (PatTy::Pat(p), _) => p.matches_ty(ctx, ty),
320 (PatTy::Ref(pat_mtbl, p_ty), TyKind::Ref(_, ty, ty_mtbl)) => {
321 pat_mtbl == ty_mtbl && p_ty.matches_ty(ctx, ty)
322 }
323 _ => false,
324 }
325 }
326
327 pub fn matches_const(&self, ctx: &TranslatedCrate, c: &ConstantExpr) -> bool {
328 match self {
329 PatTy::Pat(p) => p.matches_const(ctx, c),
330 PatTy::Ref(..) => false,
331 }
332 }
333}
334
335#[test]
336fn test_compare() {
337 use Ordering::*;
338 let tests = [
339 ("_", Less, "crate"),
340 ("crate::_", Less, "crate::foo"),
341 ("crate::foo", Less, "crate::foo::_"),
342 ];
343 for (x, o, y) in tests {
344 let x = Pattern::parse(x).unwrap();
345 let y = Pattern::parse(y).unwrap();
346 assert_eq!(x.compare(&y), o);
347 }
348}