Skip to content

Commit 3f51582

Browse files
refactor(macros): drop proc-macro-error2 in favor of syn::Error
1 parent 97966e1 commit 3f51582

3 files changed

Lines changed: 113 additions & 91 deletions

File tree

macros/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ proc-macro = true
1717
syn = { version = "2", features = ["full"] }
1818
quote = "1"
1919
proc-macro2 = { version = "1.0.86", features = ["span-locations"] }
20-
proc-macro-error2 = "2"
2120

2221
[dev-dependencies]
2322
cmd_lib = { path = ".." }

macros/src/lexer.rs

Lines changed: 66 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use crate::parser::{ParseArg, Parser};
2-
use proc_macro_error2::abort;
32
use proc_macro2::{Delimiter, Ident, Literal, Span, TokenStream, TokenTree, token_stream};
43
use quote::quote;
54
use std::iter::Peekable;
@@ -12,12 +11,12 @@ use std::str::Chars;
1211
/// - to escape '$' itself, use "$$"
1312
/// - support normal rust character escapes:
1413
/// https://doc.rust-lang.org/reference/tokens.html#ascii-escapes
15-
pub fn scan_str_lit(lit: &Literal) -> TokenStream {
14+
pub fn scan_str_lit(lit: &Literal) -> syn::Result<TokenStream> {
1615
let s = lit.to_string();
1716

1817
// If the literal is not a string (e.g., a number literal), treat it as a direct CmdString.
1918
if !s.starts_with('\"') {
20-
return quote!(::cmd_lib::CmdString::from(#lit));
19+
return Ok(quote!(::cmd_lib::CmdString::from(#lit)));
2120
}
2221

2322
// Extract the inner string by trimming the surrounding quotes.
@@ -77,7 +76,7 @@ pub fn scan_str_lit(lit: &Literal) -> TokenStream {
7776

7877
// Expect '}' to close the braced interpolation
7978
if chars.next() != Some('}') {
80-
abort!(lit.span(), "bad substitution: expected '}'");
79+
bail!(lit.span(), "bad substitution: expected '}}'");
8180
}
8281
}
8382

@@ -110,7 +109,7 @@ pub fn scan_str_lit(lit: &Literal) -> TokenStream {
110109

111110
// Append any remaining literal part after the loop finishes.
112111
seal_current_literal_part(&mut output, &mut current_literal_part);
113-
output
112+
Ok(output)
114113
}
115114

116115
/// Parses a variable name from the character iterator.
@@ -167,14 +166,14 @@ impl Lexer {
167166
}
168167
}
169168

170-
pub fn scan(mut self) -> Parser<impl Iterator<Item = ParseArg>> {
169+
pub fn scan(mut self) -> syn::Result<Parser<impl Iterator<Item = ParseArg>>> {
171170
while let Some(item) = self.iter.next() {
172171
match item {
173172
TokenTree::Group(_) => {
174-
abort!(self.iter.span(), "grouping is only allowed for variables");
173+
bail!(self.iter.span(), "grouping is only allowed for variables");
175174
}
176175
TokenTree::Literal(lit) => {
177-
self.scan_literal(lit);
176+
self.scan_literal(lit)?;
178177
}
179178
TokenTree::Ident(ident) => {
180179
let s = ident.to_string();
@@ -183,17 +182,17 @@ impl Lexer {
183182
TokenTree::Punct(punct) => {
184183
let ch = punct.as_char();
185184
if ch == ';' {
186-
self.add_arg_with_token(SepToken::SemiColon, self.iter.span());
185+
self.add_arg_with_token(SepToken::SemiColon, self.iter.span())?;
187186
} else if ch == '|' {
188-
self.scan_pipe();
187+
self.scan_pipe()?;
189188
} else if ch == '<' {
190-
self.set_redirect(self.iter.span(), RedirectFd::Stdin);
189+
self.set_redirect(self.iter.span(), RedirectFd::Stdin)?;
191190
} else if ch == '>' {
192-
self.scan_redirect_out(1);
191+
self.scan_redirect_out(1)?;
193192
} else if ch == '&' {
194-
self.scan_ampersand();
193+
self.scan_ampersand()?;
195194
} else if ch == '$' {
196-
self.scan_dollar();
195+
self.scan_dollar()?;
197196
} else {
198197
let s = ch.to_string();
199198
self.extend_last_arg(quote!(#s));
@@ -202,18 +201,18 @@ impl Lexer {
202201
}
203202

204203
if self.iter.peek_no_gap().is_none() && !self.last_arg_str.is_empty() {
205-
self.add_arg_with_token(SepToken::Space, self.iter.span());
204+
self.add_arg_with_token(SepToken::Space, self.iter.span())?;
206205
}
207206
}
208-
self.add_arg_with_token(SepToken::Space, self.iter.span());
209-
Parser::from(self.args.into_iter().peekable())
207+
self.add_arg_with_token(SepToken::Space, self.iter.span())?;
208+
Ok(Parser::from(self.args.into_iter().peekable()))
210209
}
211210

212-
fn add_arg_with_token(&mut self, token: SepToken, token_span: Span) {
211+
fn add_arg_with_token(&mut self, token: SepToken, token_span: Span) -> syn::Result<()> {
213212
let last_arg_str = &self.last_arg_str;
214213
if let Some((redirect, span)) = self.last_redirect.take() {
215214
if last_arg_str.is_empty() {
216-
abort!(span, "wrong redirection format: missing target");
215+
bail!(span, "wrong redirection format: missing target");
217216
}
218217

219218
let mut stdouterr = false;
@@ -239,13 +238,14 @@ impl Lexer {
239238
SepToken::Space => new_redirect = self.seen_redirect,
240239
SepToken::SemiColon => self.args.push(ParseArg::Semicolon),
241240
SepToken::Pipe => {
242-
Self::check_set_redirect(&mut self.seen_redirect.1, "stdout", token_span);
241+
Self::check_set_redirect(&mut self.seen_redirect.1, "stdout", token_span)?;
243242
self.args.push(ParseArg::Pipe);
244243
new_redirect.0 = true;
245244
}
246245
}
247246
self.seen_redirect = new_redirect;
248247
self.last_arg_str = TokenStream::new();
248+
Ok(())
249249
}
250250

251251
fn extend_last_arg(&mut self, stream: TokenStream) {
@@ -255,38 +255,42 @@ impl Lexer {
255255
self.last_arg_str.extend(quote!(.append(#stream)));
256256
}
257257

258-
fn check_set_redirect(redirect: &mut bool, name: &str, span: Span) {
258+
fn check_set_redirect(redirect: &mut bool, name: &str, span: Span) -> syn::Result<()> {
259259
if *redirect {
260-
abort!(span, "already set {} redirection", name);
260+
bail!(span, "already set {} redirection", name);
261261
}
262262
*redirect = true;
263+
Ok(())
263264
}
264265

265-
fn set_redirect(&mut self, span: Span, fd: RedirectFd) {
266+
fn set_redirect(&mut self, span: Span, fd: RedirectFd) -> syn::Result<()> {
266267
if self.last_redirect.is_some() {
267-
abort!(span, "wrong double redirection format");
268+
bail!(span, "wrong double redirection format");
268269
}
269270
match fd {
270-
RedirectFd::Stdin => Self::check_set_redirect(&mut self.seen_redirect.0, "stdin", span),
271+
RedirectFd::Stdin => {
272+
Self::check_set_redirect(&mut self.seen_redirect.0, "stdin", span)?
273+
}
271274
RedirectFd::Stdout { append: _ } => {
272-
Self::check_set_redirect(&mut self.seen_redirect.1, "stdout", span)
275+
Self::check_set_redirect(&mut self.seen_redirect.1, "stdout", span)?
273276
}
274277
RedirectFd::Stderr { append: _ } => {
275-
Self::check_set_redirect(&mut self.seen_redirect.2, "stderr", span)
278+
Self::check_set_redirect(&mut self.seen_redirect.2, "stderr", span)?
276279
}
277280
RedirectFd::StdoutErr { append: _ } => {
278-
Self::check_set_redirect(&mut self.seen_redirect.1, "stdout", span);
279-
Self::check_set_redirect(&mut self.seen_redirect.2, "stderr", span);
281+
Self::check_set_redirect(&mut self.seen_redirect.1, "stdout", span)?;
282+
Self::check_set_redirect(&mut self.seen_redirect.2, "stderr", span)?;
280283
}
281284
}
282285
self.last_redirect = Some((fd, span));
286+
Ok(())
283287
}
284288

285-
fn scan_literal(&mut self, lit: Literal) {
289+
fn scan_literal(&mut self, lit: Literal) -> syn::Result<()> {
286290
let s = lit.to_string();
287291
if s.starts_with('\"') || s.starts_with('r') {
288292
// string literal
289-
let ss = scan_str_lit(&lit);
293+
let ss = scan_str_lit(&lit)?;
290294
self.extend_last_arg(quote!(#ss.into_os_string()));
291295
} else {
292296
let mut is_redirect = false;
@@ -295,23 +299,24 @@ impl Lexer {
295299
&& p.as_char() == '>'
296300
{
297301
self.iter.next();
298-
self.scan_redirect_out(if s == "1" { 1 } else { 2 });
302+
self.scan_redirect_out(if s == "1" { 1 } else { 2 })?;
299303
is_redirect = true;
300304
}
301305
if !is_redirect {
302306
self.extend_last_arg(quote!(#s));
303307
}
304308
}
309+
Ok(())
305310
}
306311

307-
fn scan_pipe(&mut self) {
312+
fn scan_pipe(&mut self) -> syn::Result<()> {
308313
if let Some(TokenTree::Punct(p)) = self.iter.peek_no_gap()
309314
&& p.as_char() == '&'
310315
{
311316
if let Some(ref redirect) = self.last_redirect {
312-
abort!(redirect.1, "invalid '&': found previous redirect");
317+
bail!(redirect.1, "invalid '&': found previous redirect");
313318
}
314-
Self::check_set_redirect(&mut self.seen_redirect.2, "stderr", p.span());
319+
Self::check_set_redirect(&mut self.seen_redirect.2, "stderr", p.span())?;
315320
self.args.push(ParseArg::RedirectFd(2, 1));
316321
self.iter.next();
317322
}
@@ -320,18 +325,19 @@ impl Lexer {
320325
match self.iter.peek() {
321326
Some(TokenTree::Punct(np)) => {
322327
if np.as_char() == '|' || np.as_char() == ';' {
323-
abort!(np.span(), "expect new command after '|'");
328+
bail!(np.span(), "expect new command after '|'");
324329
}
325330
}
326331
None => {
327-
abort!(self.iter.span(), "expect new command after '|'");
332+
bail!(self.iter.span(), "expect new command after '|'");
328333
}
329334
_ => {}
330335
}
331-
self.add_arg_with_token(SepToken::Pipe, self.iter.span());
336+
self.add_arg_with_token(SepToken::Pipe, self.iter.span())?;
337+
Ok(())
332338
}
333339

334-
fn scan_redirect_out(&mut self, fd: i32) {
340+
fn scan_redirect_out(&mut self, fd: i32) -> syn::Result<()> {
335341
let append = self.check_append();
336342
self.set_redirect(
337343
self.iter.span(),
@@ -340,68 +346,70 @@ impl Lexer {
340346
} else {
341347
RedirectFd::Stderr { append }
342348
},
343-
);
349+
)?;
344350
if let Some(TokenTree::Punct(p)) = self.iter.peek_no_gap()
345351
&& p.as_char() == '&'
346352
{
347353
if append {
348-
abort!(p.span(), "raw fd not allowed for append redirection");
354+
bail!(p.span(), "raw fd not allowed for append redirection");
349355
}
350356
self.iter.next();
351357
if let Some(TokenTree::Literal(lit)) = self.iter.peek_no_gap() {
352358
let s = lit.to_string();
353359
if s.starts_with('\"') || s.starts_with('r') {
354-
abort!(lit.span(), "invalid literal string after &");
360+
bail!(lit.span(), "invalid literal string after &");
355361
}
356362
if &s == "1" {
357363
self.args.push(ParseArg::RedirectFd(fd, 1));
358364
} else if &s == "2" {
359365
self.args.push(ParseArg::RedirectFd(fd, 2));
360366
} else {
361-
abort!(lit.span(), "Only &1 or &2 is supported");
367+
bail!(lit.span(), "Only &1 or &2 is supported");
362368
}
363369
self.last_redirect = None;
364370
self.iter.next();
365371
} else {
366-
abort!(self.iter.span(), "expect &1 or &2");
372+
bail!(self.iter.span(), "expect &1 or &2");
367373
}
368374
}
375+
Ok(())
369376
}
370377

371-
fn scan_ampersand(&mut self) {
378+
fn scan_ampersand(&mut self) -> syn::Result<()> {
372379
if let Some(tt) = self.iter.peek_no_gap() {
373380
if let TokenTree::Punct(p) = tt {
374381
let span = p.span();
375382
if p.as_char() == '>' {
376383
self.iter.next();
377384
let append = self.check_append();
378-
self.set_redirect(span, RedirectFd::StdoutErr { append });
385+
self.set_redirect(span, RedirectFd::StdoutErr { append })?;
379386
} else {
380-
abort!(span, "invalid punctuation");
387+
bail!(span, "invalid punctuation");
381388
}
382389
} else {
383-
abort!(tt.span(), "invalid format after '&'");
390+
bail!(tt.span(), "invalid format after '&'");
384391
}
385392
} else if self.last_redirect.is_some() {
386-
abort!(
393+
bail!(
387394
self.iter.span(),
388395
"wrong redirection format: no spacing permitted before '&'"
389396
);
390397
} else if self.iter.peek().is_some() {
391-
abort!(self.iter.span(), "invalid spacing after '&'");
398+
bail!(self.iter.span(), "invalid spacing after '&'");
392399
} else {
393-
abort!(self.iter.span(), "invalid '&' at the end");
400+
bail!(self.iter.span(), "invalid '&' at the end");
394401
}
402+
Ok(())
395403
}
396404

397-
fn scan_dollar(&mut self) {
405+
fn scan_dollar(&mut self) -> syn::Result<()> {
398406
let peek_no_gap = self.iter.peek_no_gap().map(|tt| tt.to_owned());
399407
// let peek_no_gap = None;
400408
if let Some(TokenTree::Ident(var)) = peek_no_gap {
401409
self.extend_last_arg(quote!(#var.as_os_str()));
402410
} else if let Some(TokenTree::Group(g)) = peek_no_gap {
403411
if g.delimiter() != Delimiter::Brace && g.delimiter() != Delimiter::Bracket {
404-
abort!(
412+
bail!(
405413
g.span(),
406414
"invalid grouping: found {:?}, only \"brace/bracket\" is allowed",
407415
format!("{:?}", g.delimiter()).to_lowercase()
@@ -412,13 +420,13 @@ impl Lexer {
412420
let span = tt.span();
413421
if let TokenTree::Ident(ref var) = tt {
414422
if found_var {
415-
abort!(span, "more than one variable in grouping");
423+
bail!(span, "more than one variable in grouping");
416424
}
417425
if g.delimiter() == Delimiter::Brace {
418426
self.extend_last_arg(quote!(#var.as_os_str()));
419427
} else {
420428
if !self.last_arg_str.is_empty() {
421-
abort!(
429+
bail!(
422430
span,
423431
"vector variable cannot have content immediately before it"
424432
);
@@ -427,13 +435,14 @@ impl Lexer {
427435
}
428436
found_var = true;
429437
} else {
430-
abort!(span, "invalid grouping: extra tokens");
438+
bail!(span, "invalid grouping: extra tokens");
431439
}
432440
}
433441
} else {
434-
abort!(self.iter.span(), "invalid token after $");
442+
bail!(self.iter.span(), "invalid token after $");
435443
}
436444
self.iter.next();
445+
Ok(())
437446
}
438447

439448
fn check_append(&mut self) -> bool {

0 commit comments

Comments
 (0)