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