mirror of https://github.com/torvalds/linux.git
487 lines
16 KiB
Rust
487 lines
16 KiB
Rust
// SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
|
|
use crate::attr::Attribute;
|
|
use crate::expr::Expr;
|
|
use crate::item::Item;
|
|
use crate::mac::Macro;
|
|
use crate::pat::Pat;
|
|
use crate::token;
|
|
|
|
ast_struct! {
|
|
/// A braced block containing Rust statements.
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "full")))]
|
|
pub struct Block {
|
|
pub brace_token: token::Brace,
|
|
/// Statements in a block
|
|
pub stmts: Vec<Stmt>,
|
|
}
|
|
}
|
|
|
|
ast_enum! {
|
|
/// A statement, usually ending in a semicolon.
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "full")))]
|
|
pub enum Stmt {
|
|
/// A local (let) binding.
|
|
Local(Local),
|
|
|
|
/// An item definition.
|
|
Item(Item),
|
|
|
|
/// Expression, with or without trailing semicolon.
|
|
Expr(Expr, Option<Token![;]>),
|
|
|
|
/// A macro invocation in statement position.
|
|
///
|
|
/// Syntactically it's ambiguous which other kind of statement this
|
|
/// macro would expand to. It can be any of local variable (`let`),
|
|
/// item, or expression.
|
|
Macro(StmtMacro),
|
|
}
|
|
}
|
|
|
|
ast_struct! {
|
|
/// A local `let` binding: `let x: u64 = s.parse()?;`.
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "full")))]
|
|
pub struct Local {
|
|
pub attrs: Vec<Attribute>,
|
|
pub let_token: Token![let],
|
|
pub pat: Pat,
|
|
pub init: Option<LocalInit>,
|
|
pub semi_token: Token![;],
|
|
}
|
|
}
|
|
|
|
ast_struct! {
|
|
/// The expression assigned in a local `let` binding, including optional
|
|
/// diverging `else` block.
|
|
///
|
|
/// `LocalInit` represents `= s.parse()?` in `let x: u64 = s.parse()?` and
|
|
/// `= r else { return }` in `let Ok(x) = r else { return }`.
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "full")))]
|
|
pub struct LocalInit {
|
|
pub eq_token: Token![=],
|
|
pub expr: Box<Expr>,
|
|
pub diverge: Option<(Token![else], Box<Expr>)>,
|
|
}
|
|
}
|
|
|
|
ast_struct! {
|
|
/// A macro invocation in statement position.
|
|
///
|
|
/// Syntactically it's ambiguous which other kind of statement this macro
|
|
/// would expand to. It can be any of local variable (`let`), item, or
|
|
/// expression.
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "full")))]
|
|
pub struct StmtMacro {
|
|
pub attrs: Vec<Attribute>,
|
|
pub mac: Macro,
|
|
pub semi_token: Option<Token![;]>,
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "parsing")]
|
|
pub(crate) mod parsing {
|
|
use crate::attr::Attribute;
|
|
use crate::classify;
|
|
use crate::error::Result;
|
|
use crate::expr::{Expr, ExprBlock, ExprMacro};
|
|
use crate::ident::Ident;
|
|
use crate::item;
|
|
use crate::mac::{self, Macro};
|
|
use crate::parse::discouraged::Speculative as _;
|
|
use crate::parse::{Parse, ParseStream};
|
|
use crate::pat::{Pat, PatType};
|
|
use crate::path::Path;
|
|
use crate::stmt::{Block, Local, LocalInit, Stmt, StmtMacro};
|
|
use crate::token;
|
|
use crate::ty::Type;
|
|
use proc_macro2::TokenStream;
|
|
|
|
struct AllowNoSemi(bool);
|
|
|
|
impl Block {
|
|
/// Parse the body of a block as zero or more statements, possibly
|
|
/// including one trailing expression.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```
|
|
/// use syn::{braced, token, Attribute, Block, Ident, Result, Stmt, Token};
|
|
/// use syn::parse::{Parse, ParseStream};
|
|
///
|
|
/// // Parse a function with no generics or parameter list.
|
|
/// //
|
|
/// // fn playground {
|
|
/// // let mut x = 1;
|
|
/// // x += 1;
|
|
/// // println!("{}", x);
|
|
/// // }
|
|
/// struct MiniFunction {
|
|
/// attrs: Vec<Attribute>,
|
|
/// fn_token: Token![fn],
|
|
/// name: Ident,
|
|
/// brace_token: token::Brace,
|
|
/// stmts: Vec<Stmt>,
|
|
/// }
|
|
///
|
|
/// impl Parse for MiniFunction {
|
|
/// fn parse(input: ParseStream) -> Result<Self> {
|
|
/// let outer_attrs = input.call(Attribute::parse_outer)?;
|
|
/// let fn_token: Token![fn] = input.parse()?;
|
|
/// let name: Ident = input.parse()?;
|
|
///
|
|
/// let content;
|
|
/// let brace_token = braced!(content in input);
|
|
/// let inner_attrs = content.call(Attribute::parse_inner)?;
|
|
/// let stmts = content.call(Block::parse_within)?;
|
|
///
|
|
/// Ok(MiniFunction {
|
|
/// attrs: {
|
|
/// let mut attrs = outer_attrs;
|
|
/// attrs.extend(inner_attrs);
|
|
/// attrs
|
|
/// },
|
|
/// fn_token,
|
|
/// name,
|
|
/// brace_token,
|
|
/// stmts,
|
|
/// })
|
|
/// }
|
|
/// }
|
|
/// ```
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
|
|
pub fn parse_within(input: ParseStream) -> Result<Vec<Stmt>> {
|
|
let mut stmts = Vec::new();
|
|
loop {
|
|
while let semi @ Some(_) = input.parse()? {
|
|
stmts.push(Stmt::Expr(Expr::Verbatim(TokenStream::new()), semi));
|
|
}
|
|
if input.is_empty() {
|
|
break;
|
|
}
|
|
let stmt = parse_stmt(input, AllowNoSemi(true))?;
|
|
let requires_semicolon = match &stmt {
|
|
Stmt::Expr(stmt, None) => classify::requires_semi_to_be_stmt(stmt),
|
|
Stmt::Macro(stmt) => {
|
|
stmt.semi_token.is_none() && !stmt.mac.delimiter.is_brace()
|
|
}
|
|
Stmt::Local(_) | Stmt::Item(_) | Stmt::Expr(_, Some(_)) => false,
|
|
};
|
|
stmts.push(stmt);
|
|
if input.is_empty() {
|
|
break;
|
|
} else if requires_semicolon {
|
|
return Err(input.error("unexpected token, expected `;`"));
|
|
}
|
|
}
|
|
Ok(stmts)
|
|
}
|
|
}
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
|
|
impl Parse for Block {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
let content;
|
|
Ok(Block {
|
|
brace_token: braced!(content in input),
|
|
stmts: content.call(Block::parse_within)?,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
|
|
impl Parse for Stmt {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
let allow_nosemi = AllowNoSemi(false);
|
|
parse_stmt(input, allow_nosemi)
|
|
}
|
|
}
|
|
|
|
fn parse_stmt(input: ParseStream, allow_nosemi: AllowNoSemi) -> Result<Stmt> {
|
|
let begin = input.fork();
|
|
let attrs = input.call(Attribute::parse_outer)?;
|
|
|
|
// brace-style macros; paren and bracket macros get parsed as
|
|
// expression statements.
|
|
let ahead = input.fork();
|
|
let mut is_item_macro = false;
|
|
if let Ok(path) = ahead.call(Path::parse_mod_style) {
|
|
if ahead.peek(Token![!]) {
|
|
if ahead.peek2(Ident) || ahead.peek2(Token![try]) {
|
|
is_item_macro = true;
|
|
} else if ahead.peek2(token::Brace)
|
|
&& !(ahead.peek3(Token![.]) && !ahead.peek3(Token![..])
|
|
|| ahead.peek3(Token![?]))
|
|
{
|
|
input.advance_to(&ahead);
|
|
return stmt_mac(input, attrs, path).map(Stmt::Macro);
|
|
}
|
|
}
|
|
}
|
|
|
|
if input.peek(Token![let]) && !input.peek(token::Group) {
|
|
stmt_local(input, attrs).map(Stmt::Local)
|
|
} else if input.peek(Token![pub])
|
|
|| input.peek(Token![crate]) && !input.peek2(Token![::])
|
|
|| input.peek(Token![extern])
|
|
|| input.peek(Token![use])
|
|
|| input.peek(Token![static])
|
|
&& (input.peek2(Token![mut])
|
|
|| input.peek2(Ident)
|
|
&& !(input.peek2(Token![async])
|
|
&& (input.peek3(Token![move]) || input.peek3(Token![|]))))
|
|
|| input.peek(Token![const])
|
|
&& !(input.peek2(token::Brace)
|
|
|| input.peek2(Token![static])
|
|
|| input.peek2(Token![async])
|
|
&& !(input.peek3(Token![unsafe])
|
|
|| input.peek3(Token![extern])
|
|
|| input.peek3(Token![fn]))
|
|
|| input.peek2(Token![move])
|
|
|| input.peek2(Token![|]))
|
|
|| input.peek(Token![unsafe]) && !input.peek2(token::Brace)
|
|
|| input.peek(Token![async])
|
|
&& (input.peek2(Token![unsafe])
|
|
|| input.peek2(Token![extern])
|
|
|| input.peek2(Token![fn]))
|
|
|| input.peek(Token![fn])
|
|
|| input.peek(Token![mod])
|
|
|| input.peek(Token![type])
|
|
|| input.peek(Token![struct])
|
|
|| input.peek(Token![enum])
|
|
|| input.peek(Token![union]) && input.peek2(Ident)
|
|
|| input.peek(Token![auto]) && input.peek2(Token![trait])
|
|
|| input.peek(Token![trait])
|
|
|| input.peek(Token![default])
|
|
&& (input.peek2(Token![unsafe]) || input.peek2(Token![impl]))
|
|
|| input.peek(Token![impl])
|
|
|| input.peek(Token![macro])
|
|
|| is_item_macro
|
|
{
|
|
let item = item::parsing::parse_rest_of_item(begin, attrs, input)?;
|
|
Ok(Stmt::Item(item))
|
|
} else {
|
|
stmt_expr(input, allow_nosemi, attrs)
|
|
}
|
|
}
|
|
|
|
fn stmt_mac(input: ParseStream, attrs: Vec<Attribute>, path: Path) -> Result<StmtMacro> {
|
|
let bang_token: Token![!] = input.parse()?;
|
|
let (delimiter, tokens) = mac::parse_delimiter(input)?;
|
|
let semi_token: Option<Token![;]> = input.parse()?;
|
|
|
|
Ok(StmtMacro {
|
|
attrs,
|
|
mac: Macro {
|
|
path,
|
|
bang_token,
|
|
delimiter,
|
|
tokens,
|
|
},
|
|
semi_token,
|
|
})
|
|
}
|
|
|
|
fn stmt_local(input: ParseStream, attrs: Vec<Attribute>) -> Result<Local> {
|
|
let let_token: Token![let] = input.parse()?;
|
|
|
|
let mut pat = Pat::parse_single(input)?;
|
|
if input.peek(Token![:]) {
|
|
let colon_token: Token![:] = input.parse()?;
|
|
let ty: Type = input.parse()?;
|
|
pat = Pat::Type(PatType {
|
|
attrs: Vec::new(),
|
|
pat: Box::new(pat),
|
|
colon_token,
|
|
ty: Box::new(ty),
|
|
});
|
|
}
|
|
|
|
let init = if let Some(eq_token) = input.parse()? {
|
|
let eq_token: Token![=] = eq_token;
|
|
let expr: Expr = input.parse()?;
|
|
|
|
let diverge = if !classify::expr_trailing_brace(&expr) && input.peek(Token![else]) {
|
|
let else_token: Token![else] = input.parse()?;
|
|
let diverge = ExprBlock {
|
|
attrs: Vec::new(),
|
|
label: None,
|
|
block: input.parse()?,
|
|
};
|
|
Some((else_token, Box::new(Expr::Block(diverge))))
|
|
} else {
|
|
None
|
|
};
|
|
|
|
Some(LocalInit {
|
|
eq_token,
|
|
expr: Box::new(expr),
|
|
diverge,
|
|
})
|
|
} else {
|
|
None
|
|
};
|
|
|
|
let semi_token: Token![;] = input.parse()?;
|
|
|
|
Ok(Local {
|
|
attrs,
|
|
let_token,
|
|
pat,
|
|
init,
|
|
semi_token,
|
|
})
|
|
}
|
|
|
|
fn stmt_expr(
|
|
input: ParseStream,
|
|
allow_nosemi: AllowNoSemi,
|
|
mut attrs: Vec<Attribute>,
|
|
) -> Result<Stmt> {
|
|
let mut e = Expr::parse_with_earlier_boundary_rule(input)?;
|
|
|
|
let mut attr_target = &mut e;
|
|
loop {
|
|
attr_target = match attr_target {
|
|
Expr::Assign(e) => &mut e.left,
|
|
Expr::Binary(e) => &mut e.left,
|
|
Expr::Cast(e) => &mut e.expr,
|
|
Expr::Array(_)
|
|
| Expr::Async(_)
|
|
| Expr::Await(_)
|
|
| Expr::Block(_)
|
|
| Expr::Break(_)
|
|
| Expr::Call(_)
|
|
| Expr::Closure(_)
|
|
| Expr::Const(_)
|
|
| Expr::Continue(_)
|
|
| Expr::Field(_)
|
|
| Expr::ForLoop(_)
|
|
| Expr::Group(_)
|
|
| Expr::If(_)
|
|
| Expr::Index(_)
|
|
| Expr::Infer(_)
|
|
| Expr::Let(_)
|
|
| Expr::Lit(_)
|
|
| Expr::Loop(_)
|
|
| Expr::Macro(_)
|
|
| Expr::Match(_)
|
|
| Expr::MethodCall(_)
|
|
| Expr::Paren(_)
|
|
| Expr::Path(_)
|
|
| Expr::Range(_)
|
|
| Expr::RawAddr(_)
|
|
| Expr::Reference(_)
|
|
| Expr::Repeat(_)
|
|
| Expr::Return(_)
|
|
| Expr::Struct(_)
|
|
| Expr::Try(_)
|
|
| Expr::TryBlock(_)
|
|
| Expr::Tuple(_)
|
|
| Expr::Unary(_)
|
|
| Expr::Unsafe(_)
|
|
| Expr::While(_)
|
|
| Expr::Yield(_)
|
|
| Expr::Verbatim(_) => break,
|
|
};
|
|
}
|
|
attrs.extend(attr_target.replace_attrs(Vec::new()));
|
|
attr_target.replace_attrs(attrs);
|
|
|
|
let semi_token: Option<Token![;]> = input.parse()?;
|
|
|
|
match e {
|
|
Expr::Macro(ExprMacro { attrs, mac })
|
|
if semi_token.is_some() || mac.delimiter.is_brace() =>
|
|
{
|
|
return Ok(Stmt::Macro(StmtMacro {
|
|
attrs,
|
|
mac,
|
|
semi_token,
|
|
}));
|
|
}
|
|
_ => {}
|
|
}
|
|
|
|
if semi_token.is_some() {
|
|
Ok(Stmt::Expr(e, semi_token))
|
|
} else if allow_nosemi.0 || !classify::requires_semi_to_be_stmt(&e) {
|
|
Ok(Stmt::Expr(e, None))
|
|
} else {
|
|
Err(input.error("expected semicolon"))
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "printing")]
|
|
pub(crate) mod printing {
|
|
use crate::classify;
|
|
use crate::expr::{self, Expr};
|
|
use crate::fixup::FixupContext;
|
|
use crate::stmt::{Block, Local, Stmt, StmtMacro};
|
|
use crate::token;
|
|
use proc_macro2::TokenStream;
|
|
use quote::{ToTokens, TokenStreamExt};
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for Block {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
self.brace_token.surround(tokens, |tokens| {
|
|
tokens.append_all(&self.stmts);
|
|
});
|
|
}
|
|
}
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for Stmt {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
match self {
|
|
Stmt::Local(local) => local.to_tokens(tokens),
|
|
Stmt::Item(item) => item.to_tokens(tokens),
|
|
Stmt::Expr(expr, semi) => {
|
|
expr::printing::print_expr(expr, tokens, FixupContext::new_stmt());
|
|
semi.to_tokens(tokens);
|
|
}
|
|
Stmt::Macro(mac) => mac.to_tokens(tokens),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for Local {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
expr::printing::outer_attrs_to_tokens(&self.attrs, tokens);
|
|
self.let_token.to_tokens(tokens);
|
|
self.pat.to_tokens(tokens);
|
|
if let Some(init) = &self.init {
|
|
init.eq_token.to_tokens(tokens);
|
|
expr::printing::print_subexpression(
|
|
&init.expr,
|
|
init.diverge.is_some() && classify::expr_trailing_brace(&init.expr),
|
|
tokens,
|
|
FixupContext::NONE,
|
|
);
|
|
if let Some((else_token, diverge)) = &init.diverge {
|
|
else_token.to_tokens(tokens);
|
|
match &**diverge {
|
|
Expr::Block(diverge) => diverge.to_tokens(tokens),
|
|
_ => token::Brace::default().surround(tokens, |tokens| {
|
|
expr::printing::print_expr(diverge, tokens, FixupContext::new_stmt());
|
|
}),
|
|
}
|
|
}
|
|
}
|
|
self.semi_token.to_tokens(tokens);
|
|
}
|
|
}
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for StmtMacro {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
expr::printing::outer_attrs_to_tokens(&self.attrs, tokens);
|
|
self.mac.to_tokens(tokens);
|
|
self.semi_token.to_tokens(tokens);
|
|
}
|
|
}
|
|
}
|