Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: Apache-2.0 OR MIT
2
3use crate::attr::Attribute;
4use crate::expr::Expr;
5use crate::item::Item;
6use crate::mac::Macro;
7use crate::pat::Pat;
8use crate::token;
9
10ast_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
20ast_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
42ast_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
54ast_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
68ast_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")]
83pub(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")))]
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 // 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
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")]
417pub(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}