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