1use quote::quote;
2use syn::parse::discouraged::Speculative;
3
4
5#[proc_macro]
49pub fn let_else(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
50 let Input { pattern, expr, rest, .. } = syn::parse_macro_input!(input as Input);
51
52 let match_inner = match rest {
53 Rest::Simple(SimpleRest { bound, block }) => quote! { #bound => #block, },
54 Rest::Complete(CompleteRest { arms, .. }) => quote! { #(#arms)* },
55 };
56
57 quote! {
58 #[doc(hidden)]
59 let __let_else_expr__ = #expr;
60 let #pattern = __let_else_expr__ else {
61 match __let_else_expr__ {
62 #[allow(unused, dead_code)]
63 #pattern => unsafe { std::hint::unreachable_unchecked() },
64 #match_inner
65 }
66 };
67 }.into()
68}
69
70
71struct Input {
72 pattern: syn::Pat,
73 _sym_eq: syn::Token![=],
74 expr: syn::Expr,
75 _kw_else: syn::Token![else],
76 rest: Rest,
77}
78
79impl syn::parse::Parse for Input {
80 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
81 Ok(Self {
82 pattern: input.call(syn::Pat::parse_single)?,
83 _sym_eq: input.parse()?,
84 expr: input.parse()?,
85 _kw_else: input.parse()?,
86 rest: input.parse()?,
87 })
88 }
89}
90
91
92enum Rest {
93 Simple(SimpleRest),
94 Complete(CompleteRest),
95}
96
97impl syn::parse::Parse for Rest {
98 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
99 let fork = input.fork();
100 if let Ok(simple) = fork.parse() {
101 input.advance_to(&fork);
102 return Ok(Self::Simple(simple));
103 }
104
105 let fork = input.fork();
106 if let Ok(complete) = fork.parse() {
107 input.advance_to(&fork);
108 return Ok(Self::Complete(complete));
109 }
110
111 Err(input.error("Expected `as` or `match` after `else`"))
112 }
113}
114
115
116struct SimpleRest {
117 bound: RestBound,
118 block: syn::Block,
119}
120
121impl syn::parse::Parse for SimpleRest {
122 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
123 Ok(Self {
124 bound: input.parse()?,
125 block: input.parse()?,
126 })
127 }
128}
129
130
131enum RestBound {
132 Pattern(syn::Pat),
133 Ident(syn::Ident),
134}
135
136impl syn::parse::Parse for RestBound {
137 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
138 if let Err(_) = input.parse::<syn::Token![as]>() {
139 return Ok(Self::Pattern(syn::parse_quote! { _ }));
140 }
141
142 let fork = input.fork();
143 if let Ok(pattern) = fork.call(syn::Pat::parse_multi_with_leading_vert) {
144 input.advance_to(&fork);
145 return Ok(Self::Pattern(pattern));
146 }
147
148 if let Ok(ident) = input.parse() {
149 return Ok(Self::Ident(ident));
150 }
151
152 Err(input.error("Expected a pattern or an identifier after `as`"))
153 }
154}
155
156impl quote::ToTokens for RestBound {
157 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
158 match self {
159 RestBound::Pattern(pat) => pat.to_tokens(tokens),
160 RestBound::Ident(ident) => ident.to_tokens(tokens),
161 }
162 }
163}
164
165
166struct CompleteRest {
167 _kw_match: syn::Token![match],
168 arms: Vec<syn::Arm>,
169}
170
171impl syn::parse::Parse for CompleteRest {
172 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
173 Ok(Self {
174 _kw_match: input.parse()?,
175 arms: {
176 let content;
177 syn::braced!(content in input);
178
179 let mut arms = Vec::new();
180 while !content.is_empty() {
181 arms.push(content.parse()?);
182 }
183
184 arms
185 },
186 })
187 }
188}