| 1 | |
| 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 | |
| 12 | #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
|
| 13 | pub struct Block {
|
| 14 | pub brace_token: token::Brace,
|
| 15 | |
| 16 | pub stmts: Vec<Stmt>,
|
| 17 | }
|
| 18 | }
|
| 19 |
|
| 20 | ast_enum! {
|
| 21 | |
| 22 | #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
|
| 23 | pub enum Stmt {
|
| 24 | |
| 25 | Local(Local),
|
| 26 |
|
| 27 | |
| 28 | Item(Item),
|
| 29 |
|
| 30 | |
| 31 | Expr(Expr, Option<Token![;]>),
|
| 32 |
|
| 33 | |
| 34 | |
| 35 | |
| 36 | |
| 37 | |
| 38 | Macro(StmtMacro),
|
| 39 | }
|
| 40 | }
|
| 41 |
|
| 42 | ast_struct! {
|
| 43 | |
| 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 | |
| 56 | |
| 57 | |
| 58 | |
| 59 | |
| 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 | |
| 70 | |
| 71 | |
| 72 | |
| 73 | |
| 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 | |
| 104 | |
| 105 | |
| 106 | |
| 107 | |
| 108 | |
| 109 | |
| 110 | |
| 111 | |
| 112 | |
| 113 | |
| 114 | |
| 115 | |
| 116 | |
| 117 | |
| 118 | |
| 119 | |
| 120 | |
| 121 | |
| 122 | |
| 123 | |
| 124 | |
| 125 | |
| 126 | |
| 127 | |
| 128 | |
| 129 | |
| 130 | |
| 131 | |
| 132 | |
| 133 | |
| 134 | |
| 135 | |
| 136 | |
| 137 | |
| 138 | |
| 139 | |
| 140 | |
| 141 | |
| 142 | |
| 143 | |
| 144 | |
| 145 | |
| 146 | |
| 147 | |
| 148 | |
| 149 | |
| 150 | |
| 151 | |
| 152 | #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
|
| 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 {
|
| 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 {
|
| 194 | fn parse(input: ParseStream) -> Result<Self> {
|
| 195 | let allow_nosemi = AllowNoSemi(false);
|
| 196 | parse_stmt(input, allow_nosemi)
|
| 197 | }
|
| 198 | }
|
| 199 |
|
| 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 | |
| 205 | |
| 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 |
|
| 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 |
|
| 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 |
|
| 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 {
|
| 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 {
|
| 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 {
|
| 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 {
|
| 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 | }
|