1 // SPDX-License-Identifier: Apache-2.0 OR MIT 2 3 use crate::attr::Attribute; 4 use crate::expr::Expr; 5 use crate::item::Item; 6 use crate::mac::Macro; 7 use crate::pat::Pat; 8 use crate::token; 9 10 ast_struct! { 11 /// A braced block containing Rust statements. 12 #[cfg_attr(docsrs, doc(cfg(feature = "full")))] 13 pub struct Block { 14 pub brace_token: token::Brace, 15 /// Statements in a block 16 pub stmts: Vec<Stmt>, 17 } 18 } 19 20 ast_enum! { 21 /// A statement, usually ending in a semicolon. 22 #[cfg_attr(docsrs, doc(cfg(feature = "full")))] 23 pub enum Stmt { 24 /// A local (let) binding. 25 Local(Local), 26 27 /// An item definition. 28 Item(Item), 29 30 /// Expression, with or without trailing semicolon. 31 Expr(Expr, Option<Token![;]>), 32 33 /// A macro invocation in statement position. 34 /// 35 /// Syntactically it's ambiguous which other kind of statement this 36 /// macro would expand to. It can be any of local variable (`let`), 37 /// item, or expression. 38 Macro(StmtMacro), 39 } 40 } 41 42 ast_struct! { 43 /// A local `let` binding: `let x: u64 = s.parse()?;`. 44 #[cfg_attr(docsrs, doc(cfg(feature = "full")))] 45 pub struct Local { 46 pub attrs: Vec<Attribute>, 47 pub let_token: Token![let], 48 pub pat: Pat, 49 pub init: Option<LocalInit>, 50 pub semi_token: Token![;], 51 } 52 } 53 54 ast_struct! { 55 /// The expression assigned in a local `let` binding, including optional 56 /// diverging `else` block. 57 /// 58 /// `LocalInit` represents `= s.parse()?` in `let x: u64 = s.parse()?` and 59 /// `= r else { return }` in `let Ok(x) = r else { return }`. 60 #[cfg_attr(docsrs, doc(cfg(feature = "full")))] 61 pub struct LocalInit { 62 pub eq_token: Token![=], 63 pub expr: Box<Expr>, 64 pub diverge: Option<(Token![else], Box<Expr>)>, 65 } 66 } 67 68 ast_struct! { 69 /// A macro invocation in statement position. 70 /// 71 /// Syntactically it's ambiguous which other kind of statement this macro 72 /// would expand to. It can be any of local variable (`let`), item, or 73 /// expression. 74 #[cfg_attr(docsrs, doc(cfg(feature = "full")))] 75 pub struct StmtMacro { 76 pub attrs: Vec<Attribute>, 77 pub mac: Macro, 78 pub semi_token: Option<Token![;]>, 79 } 80 } 81 82 #[cfg(feature = "parsing")] 83 pub(crate) mod parsing { 84 use crate::attr::Attribute; 85 use crate::classify; 86 use crate::error::Result; 87 use crate::expr::{Expr, ExprBlock, ExprMacro}; 88 use crate::ident::Ident; 89 use crate::item; 90 use crate::mac::{self, Macro}; 91 use crate::parse::discouraged::Speculative as _; 92 use crate::parse::{Parse, ParseStream}; 93 use crate::pat::{Pat, PatType}; 94 use crate::path::Path; 95 use crate::stmt::{Block, Local, LocalInit, Stmt, StmtMacro}; 96 use crate::token; 97 use crate::ty::Type; 98 use proc_macro2::TokenStream; 99 100 struct AllowNoSemi(bool); 101 102 impl Block { 103 /// Parse the body of a block as zero or more statements, possibly 104 /// including one trailing expression. 105 /// 106 /// # Example 107 /// 108 /// ``` 109 /// use syn::{braced, token, Attribute, Block, Ident, Result, Stmt, Token}; 110 /// use syn::parse::{Parse, ParseStream}; 111 /// 112 /// // Parse a function with no generics or parameter list. 113 /// // 114 /// // fn playground { 115 /// // let mut x = 1; 116 /// // x += 1; 117 /// // println!("{}", x); 118 /// // } 119 /// struct MiniFunction { 120 /// attrs: Vec<Attribute>, 121 /// fn_token: Token![fn], 122 /// name: Ident, 123 /// brace_token: token::Brace, 124 /// stmts: Vec<Stmt>, 125 /// } 126 /// 127 /// impl Parse for MiniFunction { 128 /// fn parse(input: ParseStream) -> Result<Self> { 129 /// let outer_attrs = input.call(Attribute::parse_outer)?; 130 /// let fn_token: Token![fn] = input.parse()?; 131 /// let name: Ident = input.parse()?; 132 /// 133 /// let content; 134 /// let brace_token = braced!(content in input); 135 /// let inner_attrs = content.call(Attribute::parse_inner)?; 136 /// let stmts = content.call(Block::parse_within)?; 137 /// 138 /// Ok(MiniFunction { 139 /// attrs: { 140 /// let mut attrs = outer_attrs; 141 /// attrs.extend(inner_attrs); 142 /// attrs 143 /// }, 144 /// fn_token, 145 /// name, 146 /// brace_token, 147 /// stmts, 148 /// }) 149 /// } 150 /// } 151 /// ``` 152 #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] parse_within(input: ParseStream) -> Result<Vec<Stmt>>153 pub fn parse_within(input: ParseStream) -> Result<Vec<Stmt>> { 154 let mut stmts = Vec::new(); 155 loop { 156 while let semi @ Some(_) = input.parse()? { 157 stmts.push(Stmt::Expr(Expr::Verbatim(TokenStream::new()), semi)); 158 } 159 if input.is_empty() { 160 break; 161 } 162 let stmt = parse_stmt(input, AllowNoSemi(true))?; 163 let requires_semicolon = match &stmt { 164 Stmt::Expr(stmt, None) => classify::requires_semi_to_be_stmt(stmt), 165 Stmt::Macro(stmt) => { 166 stmt.semi_token.is_none() && !stmt.mac.delimiter.is_brace() 167 } 168 Stmt::Local(_) | Stmt::Item(_) | Stmt::Expr(_, Some(_)) => false, 169 }; 170 stmts.push(stmt); 171 if input.is_empty() { 172 break; 173 } else if requires_semicolon { 174 return Err(input.error("unexpected token, expected `;`")); 175 } 176 } 177 Ok(stmts) 178 } 179 } 180 181 #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] 182 impl Parse for Block { parse(input: ParseStream) -> Result<Self>183 fn parse(input: ParseStream) -> Result<Self> { 184 let content; 185 Ok(Block { 186 brace_token: braced!(content in input), 187 stmts: content.call(Block::parse_within)?, 188 }) 189 } 190 } 191 192 #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] 193 impl Parse for Stmt { parse(input: ParseStream) -> Result<Self>194 fn parse(input: ParseStream) -> Result<Self> { 195 let allow_nosemi = AllowNoSemi(false); 196 parse_stmt(input, allow_nosemi) 197 } 198 } 199 parse_stmt(input: ParseStream, allow_nosemi: AllowNoSemi) -> Result<Stmt>200 fn parse_stmt(input: ParseStream, allow_nosemi: AllowNoSemi) -> Result<Stmt> { 201 let begin = input.fork(); 202 let attrs = input.call(Attribute::parse_outer)?; 203 204 // brace-style macros; paren and bracket macros get parsed as 205 // expression statements. 206 let ahead = input.fork(); 207 let mut is_item_macro = false; 208 if let Ok(path) = ahead.call(Path::parse_mod_style) { 209 if ahead.peek(Token![!]) { 210 if ahead.peek2(Ident) || ahead.peek2(Token![try]) { 211 is_item_macro = true; 212 } else if ahead.peek2(token::Brace) 213 && !(ahead.peek3(Token![.]) && !ahead.peek3(Token![..]) 214 || ahead.peek3(Token![?])) 215 { 216 input.advance_to(&ahead); 217 return stmt_mac(input, attrs, path).map(Stmt::Macro); 218 } 219 } 220 } 221 222 if input.peek(Token![let]) && !input.peek(token::Group) { 223 stmt_local(input, attrs).map(Stmt::Local) 224 } else if input.peek(Token![pub]) 225 || input.peek(Token![crate]) && !input.peek2(Token![::]) 226 || input.peek(Token![extern]) 227 || input.peek(Token![use]) 228 || input.peek(Token![static]) 229 && (input.peek2(Token![mut]) 230 || input.peek2(Ident) 231 && !(input.peek2(Token![async]) 232 && (input.peek3(Token![move]) || input.peek3(Token![|])))) 233 || input.peek(Token![const]) 234 && !(input.peek2(token::Brace) 235 || input.peek2(Token![static]) 236 || input.peek2(Token![async]) 237 && !(input.peek3(Token![unsafe]) 238 || input.peek3(Token![extern]) 239 || input.peek3(Token![fn])) 240 || input.peek2(Token![move]) 241 || input.peek2(Token![|])) 242 || input.peek(Token![unsafe]) && !input.peek2(token::Brace) 243 || input.peek(Token![async]) 244 && (input.peek2(Token![unsafe]) 245 || input.peek2(Token![extern]) 246 || input.peek2(Token![fn])) 247 || input.peek(Token![fn]) 248 || input.peek(Token![mod]) 249 || input.peek(Token![type]) 250 || input.peek(Token![struct]) 251 || input.peek(Token![enum]) 252 || input.peek(Token![union]) && input.peek2(Ident) 253 || input.peek(Token![auto]) && input.peek2(Token![trait]) 254 || input.peek(Token![trait]) 255 || input.peek(Token![default]) 256 && (input.peek2(Token![unsafe]) || input.peek2(Token![impl])) 257 || input.peek(Token![impl]) 258 || input.peek(Token![macro]) 259 || is_item_macro 260 { 261 let item = item::parsing::parse_rest_of_item(begin, attrs, input)?; 262 Ok(Stmt::Item(item)) 263 } else { 264 stmt_expr(input, allow_nosemi, attrs) 265 } 266 } 267 stmt_mac(input: ParseStream, attrs: Vec<Attribute>, path: Path) -> Result<StmtMacro>268 fn stmt_mac(input: ParseStream, attrs: Vec<Attribute>, path: Path) -> Result<StmtMacro> { 269 let bang_token: Token![!] = input.parse()?; 270 let (delimiter, tokens) = mac::parse_delimiter(input)?; 271 let semi_token: Option<Token![;]> = input.parse()?; 272 273 Ok(StmtMacro { 274 attrs, 275 mac: Macro { 276 path, 277 bang_token, 278 delimiter, 279 tokens, 280 }, 281 semi_token, 282 }) 283 } 284 stmt_local(input: ParseStream, attrs: Vec<Attribute>) -> Result<Local>285 fn stmt_local(input: ParseStream, attrs: Vec<Attribute>) -> Result<Local> { 286 let let_token: Token![let] = input.parse()?; 287 288 let mut pat = Pat::parse_single(input)?; 289 if input.peek(Token![:]) { 290 let colon_token: Token![:] = input.parse()?; 291 let ty: Type = input.parse()?; 292 pat = Pat::Type(PatType { 293 attrs: Vec::new(), 294 pat: Box::new(pat), 295 colon_token, 296 ty: Box::new(ty), 297 }); 298 } 299 300 let init = if let Some(eq_token) = input.parse()? { 301 let eq_token: Token![=] = eq_token; 302 let expr: Expr = input.parse()?; 303 304 let diverge = if !classify::expr_trailing_brace(&expr) && input.peek(Token![else]) { 305 let else_token: Token![else] = input.parse()?; 306 let diverge = ExprBlock { 307 attrs: Vec::new(), 308 label: None, 309 block: input.parse()?, 310 }; 311 Some((else_token, Box::new(Expr::Block(diverge)))) 312 } else { 313 None 314 }; 315 316 Some(LocalInit { 317 eq_token, 318 expr: Box::new(expr), 319 diverge, 320 }) 321 } else { 322 None 323 }; 324 325 let semi_token: Token![;] = input.parse()?; 326 327 Ok(Local { 328 attrs, 329 let_token, 330 pat, 331 init, 332 semi_token, 333 }) 334 } 335 stmt_expr( input: ParseStream, allow_nosemi: AllowNoSemi, mut attrs: Vec<Attribute>, ) -> Result<Stmt>336 fn stmt_expr( 337 input: ParseStream, 338 allow_nosemi: AllowNoSemi, 339 mut attrs: Vec<Attribute>, 340 ) -> Result<Stmt> { 341 let mut e = Expr::parse_with_earlier_boundary_rule(input)?; 342 343 let mut attr_target = &mut e; 344 loop { 345 attr_target = match attr_target { 346 Expr::Assign(e) => &mut e.left, 347 Expr::Binary(e) => &mut e.left, 348 Expr::Cast(e) => &mut e.expr, 349 Expr::Array(_) 350 | Expr::Async(_) 351 | Expr::Await(_) 352 | Expr::Block(_) 353 | Expr::Break(_) 354 | Expr::Call(_) 355 | Expr::Closure(_) 356 | Expr::Const(_) 357 | Expr::Continue(_) 358 | Expr::Field(_) 359 | Expr::ForLoop(_) 360 | Expr::Group(_) 361 | Expr::If(_) 362 | Expr::Index(_) 363 | Expr::Infer(_) 364 | Expr::Let(_) 365 | Expr::Lit(_) 366 | Expr::Loop(_) 367 | Expr::Macro(_) 368 | Expr::Match(_) 369 | Expr::MethodCall(_) 370 | Expr::Paren(_) 371 | Expr::Path(_) 372 | Expr::Range(_) 373 | Expr::RawAddr(_) 374 | Expr::Reference(_) 375 | Expr::Repeat(_) 376 | Expr::Return(_) 377 | Expr::Struct(_) 378 | Expr::Try(_) 379 | Expr::TryBlock(_) 380 | Expr::Tuple(_) 381 | Expr::Unary(_) 382 | Expr::Unsafe(_) 383 | Expr::While(_) 384 | Expr::Yield(_) 385 | Expr::Verbatim(_) => break, 386 }; 387 } 388 attrs.extend(attr_target.replace_attrs(Vec::new())); 389 attr_target.replace_attrs(attrs); 390 391 let semi_token: Option<Token![;]> = input.parse()?; 392 393 match e { 394 Expr::Macro(ExprMacro { attrs, mac }) 395 if semi_token.is_some() || mac.delimiter.is_brace() => 396 { 397 return Ok(Stmt::Macro(StmtMacro { 398 attrs, 399 mac, 400 semi_token, 401 })); 402 } 403 _ => {} 404 } 405 406 if semi_token.is_some() { 407 Ok(Stmt::Expr(e, semi_token)) 408 } else if allow_nosemi.0 || !classify::requires_semi_to_be_stmt(&e) { 409 Ok(Stmt::Expr(e, None)) 410 } else { 411 Err(input.error("expected semicolon")) 412 } 413 } 414 } 415 416 #[cfg(feature = "printing")] 417 pub(crate) mod printing { 418 use crate::classify; 419 use crate::expr::{self, Expr}; 420 use crate::fixup::FixupContext; 421 use crate::stmt::{Block, Local, Stmt, StmtMacro}; 422 use crate::token; 423 use proc_macro2::TokenStream; 424 use quote::{ToTokens, TokenStreamExt}; 425 426 #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] 427 impl ToTokens for Block { to_tokens(&self, tokens: &mut TokenStream)428 fn to_tokens(&self, tokens: &mut TokenStream) { 429 self.brace_token.surround(tokens, |tokens| { 430 tokens.append_all(&self.stmts); 431 }); 432 } 433 } 434 435 #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] 436 impl ToTokens for Stmt { to_tokens(&self, tokens: &mut TokenStream)437 fn to_tokens(&self, tokens: &mut TokenStream) { 438 match self { 439 Stmt::Local(local) => local.to_tokens(tokens), 440 Stmt::Item(item) => item.to_tokens(tokens), 441 Stmt::Expr(expr, semi) => { 442 expr::printing::print_expr(expr, tokens, FixupContext::new_stmt()); 443 semi.to_tokens(tokens); 444 } 445 Stmt::Macro(mac) => mac.to_tokens(tokens), 446 } 447 } 448 } 449 450 #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] 451 impl ToTokens for Local { to_tokens(&self, tokens: &mut TokenStream)452 fn to_tokens(&self, tokens: &mut TokenStream) { 453 expr::printing::outer_attrs_to_tokens(&self.attrs, tokens); 454 self.let_token.to_tokens(tokens); 455 self.pat.to_tokens(tokens); 456 if let Some(init) = &self.init { 457 init.eq_token.to_tokens(tokens); 458 expr::printing::print_subexpression( 459 &init.expr, 460 init.diverge.is_some() && classify::expr_trailing_brace(&init.expr), 461 tokens, 462 FixupContext::NONE, 463 ); 464 if let Some((else_token, diverge)) = &init.diverge { 465 else_token.to_tokens(tokens); 466 match &**diverge { 467 Expr::Block(diverge) => diverge.to_tokens(tokens), 468 _ => token::Brace::default().surround(tokens, |tokens| { 469 expr::printing::print_expr(diverge, tokens, FixupContext::new_stmt()); 470 }), 471 } 472 } 473 } 474 self.semi_token.to_tokens(tokens); 475 } 476 } 477 478 #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] 479 impl ToTokens for StmtMacro { to_tokens(&self, tokens: &mut TokenStream)480 fn to_tokens(&self, tokens: &mut TokenStream) { 481 expr::printing::outer_attrs_to_tokens(&self.attrs, tokens); 482 self.mac.to_tokens(tokens); 483 self.semi_token.to_tokens(tokens); 484 } 485 } 486 } 487