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