Skip to content

Commit

Permalink
Add a fallback for parsing DeriveInput discriminants to Verbatim in n…
Browse files Browse the repository at this point in the history
…on-full mode
  • Loading branch information
dtolnay committed Jan 1, 2024
1 parent d341612 commit 1348178
Showing 1 changed file with 98 additions and 0 deletions.
98 changes: 98 additions & 0 deletions src/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ ast_struct! {
pub(crate) mod parsing {
use super::*;
use crate::ext::IdentExt as _;
#[cfg(not(feature = "full"))]
use crate::parse::discouraged::Speculative as _;
use crate::parse::{Parse, ParseStream, Result};

#[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
Expand All @@ -174,7 +176,26 @@ pub(crate) mod parsing {
};
let discriminant = if input.peek(Token![=]) {
let eq_token: Token![=] = input.parse()?;
#[cfg(feature = "full")]
let discriminant: Expr = input.parse()?;
#[cfg(not(feature = "full"))]
let discriminant = {
let ahead = input.fork();
match ahead.parse() {
Ok(expr) => {
input.advance_to(&ahead);
expr
}
Err(discriminant_error) => {
let begin = input.fork();
if scan_lenient_discriminant(input).is_ok() {
Expr::Verbatim(verbatim::between(&begin, input))
} else {
return Err(discriminant_error);
}
}
}
};
Some((eq_token, discriminant))
} else {
None
Expand All @@ -188,6 +209,83 @@ pub(crate) mod parsing {
}
}

#[cfg(not(feature = "full"))]
pub(crate) fn scan_lenient_discriminant(input: ParseStream) -> Result<()> {
use proc_macro2::Delimiter::{self, Brace, Bracket, Parenthesis};

let consume = |delimiter: Delimiter| {
Result::unwrap(input.step(|cursor| match cursor.group(delimiter) {
Some((_inside, _span, rest)) => Ok((true, rest)),
None => Ok((false, *cursor)),
}))
};

macro_rules! consume {
[$token:tt] => {
input.parse::<Option<Token![$token]>>().unwrap().is_some()
};
}

let mut initial = true;
let mut depth = 0usize;
loop {
if initial {
if consume![&] {
input.parse::<Option<Token![mut]>>()?;
} else if consume![let] {
while !consume![=] {
if !((consume![|] || consume![ref] || consume![mut] || consume![@])
|| (consume![!] || input.parse::<Option<Lit>>()?.is_some())
|| (consume![..=] || consume![..] || consume![&] || consume![_])
|| (consume(Brace) || consume(Bracket) || consume(Parenthesis)))
{
path::parsing::qpath(input, true)?;
}
}
} else if input.parse::<Option<Lifetime>>()?.is_some() && !consume![:] {
break;
} else if consume![if] || consume![match] || consume![while] {
depth += 1;
} else if (consume(Brace) || consume(Bracket) || consume(Parenthesis))
|| input.parse::<Option<Lit>>()?.is_some()
{
initial = false;
} else if consume![async] || consume![const] || consume![loop] || consume![unsafe] {
if !consume(Brace) {
break;
}
initial = false;
} else if input.parse::<UnOp>().is_err() {
path::parsing::qpath(input, true)?;
initial = consume![!] || depth == 0 && input.peek(token::Brace);
}
} else if input.is_empty() || input.peek(Token![,]) {
return Ok(());
} else if depth > 0 && consume(Brace) {
if consume![else] && !consume(Brace) {
input.parse::<Token![if]>()?;
initial = true;
} else {
depth -= 1;
}
} else if input.parse::<BinOp>().is_ok() || (consume![..] | consume![=]) {
initial = true;
} else if consume![.] {
if input.parse::<Option<LitFloat>>()?.is_none()
&& (input.parse::<Member>()?.is_named() && consume![::])
{
AngleBracketedGenericArguments::do_parse(None, input)?;
}
} else if consume![as] {
input.parse::<Type>()?;
} else if !(consume(Brace) || consume(Bracket) || consume(Parenthesis)) {
break;
}
}

Err(input.error("unsupported expression"))
}

#[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
impl Parse for FieldsNamed {
fn parse(input: ParseStream) -> Result<Self> {
Expand Down

0 comments on commit 1348178

Please sign in to comment.