]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_ast/src/token.rs
Auto merge of #96080 - nikic:ranlib, r=Mark-Simulacrum
[rust.git] / compiler / rustc_ast / src / token.rs
1 pub use BinOpToken::*;
2 pub use DelimToken::*;
3 pub use LitKind::*;
4 pub use Nonterminal::*;
5 pub use TokenKind::*;
6
7 use crate::ast;
8 use crate::ptr::P;
9
10 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
11 use rustc_data_structures::sync::Lrc;
12 use rustc_macros::HashStable_Generic;
13 use rustc_span::symbol::{kw, sym};
14 use rustc_span::symbol::{Ident, Symbol};
15 use rustc_span::{self, edition::Edition, Span, DUMMY_SP};
16 use std::borrow::Cow;
17 use std::{fmt, mem};
18
19 #[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
20 pub enum CommentKind {
21     Line,
22     Block,
23 }
24
25 #[derive(Clone, PartialEq, Encodable, Decodable, Hash, Debug, Copy)]
26 #[derive(HashStable_Generic)]
27 pub enum BinOpToken {
28     Plus,
29     Minus,
30     Star,
31     Slash,
32     Percent,
33     Caret,
34     And,
35     Or,
36     Shl,
37     Shr,
38 }
39
40 /// A delimiter token.
41 #[derive(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Debug, Copy)]
42 #[derive(HashStable_Generic)]
43 pub enum DelimToken {
44     /// A round parenthesis (i.e., `(` or `)`).
45     Paren,
46     /// A square bracket (i.e., `[` or `]`).
47     Bracket,
48     /// A curly brace (i.e., `{` or `}`).
49     Brace,
50     /// An empty delimiter.
51     NoDelim,
52 }
53
54 #[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
55 pub enum LitKind {
56     Bool, // AST only, must never appear in a `Token`
57     Byte,
58     Char,
59     Integer,
60     Float,
61     Str,
62     StrRaw(u8), // raw string delimited by `n` hash symbols
63     ByteStr,
64     ByteStrRaw(u8), // raw byte string delimited by `n` hash symbols
65     Err,
66 }
67
68 /// A literal token.
69 #[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
70 pub struct Lit {
71     pub kind: LitKind,
72     pub symbol: Symbol,
73     pub suffix: Option<Symbol>,
74 }
75
76 impl fmt::Display for Lit {
77     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78         let Lit { kind, symbol, suffix } = *self;
79         match kind {
80             Byte => write!(f, "b'{}'", symbol)?,
81             Char => write!(f, "'{}'", symbol)?,
82             Str => write!(f, "\"{}\"", symbol)?,
83             StrRaw(n) => write!(
84                 f,
85                 "r{delim}\"{string}\"{delim}",
86                 delim = "#".repeat(n as usize),
87                 string = symbol
88             )?,
89             ByteStr => write!(f, "b\"{}\"", symbol)?,
90             ByteStrRaw(n) => write!(
91                 f,
92                 "br{delim}\"{string}\"{delim}",
93                 delim = "#".repeat(n as usize),
94                 string = symbol
95             )?,
96             Integer | Float | Bool | Err => write!(f, "{}", symbol)?,
97         }
98
99         if let Some(suffix) = suffix {
100             write!(f, "{}", suffix)?;
101         }
102
103         Ok(())
104     }
105 }
106
107 impl LitKind {
108     /// An English article for the literal token kind.
109     pub fn article(self) -> &'static str {
110         match self {
111             Integer | Err => "an",
112             _ => "a",
113         }
114     }
115
116     pub fn descr(self) -> &'static str {
117         match self {
118             Bool => panic!("literal token contains `Lit::Bool`"),
119             Byte => "byte",
120             Char => "char",
121             Integer => "integer",
122             Float => "float",
123             Str | StrRaw(..) => "string",
124             ByteStr | ByteStrRaw(..) => "byte string",
125             Err => "error",
126         }
127     }
128
129     crate fn may_have_suffix(self) -> bool {
130         matches!(self, Integer | Float | Err)
131     }
132 }
133
134 impl Lit {
135     pub fn new(kind: LitKind, symbol: Symbol, suffix: Option<Symbol>) -> Lit {
136         Lit { kind, symbol, suffix }
137     }
138 }
139
140 pub fn ident_can_begin_expr(name: Symbol, span: Span, is_raw: bool) -> bool {
141     let ident_token = Token::new(Ident(name, is_raw), span);
142
143     !ident_token.is_reserved_ident()
144         || ident_token.is_path_segment_keyword()
145         || [
146             kw::Async,
147             kw::Do,
148             kw::Box,
149             kw::Break,
150             kw::Const,
151             kw::Continue,
152             kw::False,
153             kw::For,
154             kw::If,
155             kw::Let,
156             kw::Loop,
157             kw::Match,
158             kw::Move,
159             kw::Return,
160             kw::True,
161             kw::Try,
162             kw::Unsafe,
163             kw::While,
164             kw::Yield,
165             kw::Static,
166         ]
167         .contains(&name)
168 }
169
170 fn ident_can_begin_type(name: Symbol, span: Span, is_raw: bool) -> bool {
171     let ident_token = Token::new(Ident(name, is_raw), span);
172
173     !ident_token.is_reserved_ident()
174         || ident_token.is_path_segment_keyword()
175         || [kw::Underscore, kw::For, kw::Impl, kw::Fn, kw::Unsafe, kw::Extern, kw::Typeof, kw::Dyn]
176             .contains(&name)
177 }
178
179 #[derive(Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
180 pub enum TokenKind {
181     /* Expression-operator symbols. */
182     Eq,
183     Lt,
184     Le,
185     EqEq,
186     Ne,
187     Ge,
188     Gt,
189     AndAnd,
190     OrOr,
191     Not,
192     Tilde,
193     BinOp(BinOpToken),
194     BinOpEq(BinOpToken),
195
196     /* Structural symbols */
197     At,
198     Dot,
199     DotDot,
200     DotDotDot,
201     DotDotEq,
202     Comma,
203     Semi,
204     Colon,
205     ModSep,
206     RArrow,
207     LArrow,
208     FatArrow,
209     Pound,
210     Dollar,
211     Question,
212     /// Used by proc macros for representing lifetimes, not generated by lexer right now.
213     SingleQuote,
214     /// An opening delimiter (e.g., `{`).
215     OpenDelim(DelimToken),
216     /// A closing delimiter (e.g., `}`).
217     CloseDelim(DelimToken),
218
219     /* Literals */
220     Literal(Lit),
221
222     /// Identifier token.
223     /// Do not forget about `NtIdent` when you want to match on identifiers.
224     /// It's recommended to use `Token::(ident,uninterpolate,uninterpolated_span)` to
225     /// treat regular and interpolated identifiers in the same way.
226     Ident(Symbol, /* is_raw */ bool),
227     /// Lifetime identifier token.
228     /// Do not forget about `NtLifetime` when you want to match on lifetime identifiers.
229     /// It's recommended to use `Token::(lifetime,uninterpolate,uninterpolated_span)` to
230     /// treat regular and interpolated lifetime identifiers in the same way.
231     Lifetime(Symbol),
232
233     Interpolated(Lrc<Nonterminal>),
234
235     /// A doc comment token.
236     /// `Symbol` is the doc comment's data excluding its "quotes" (`///`, `/**`, etc)
237     /// similarly to symbols in string literal tokens.
238     DocComment(CommentKind, ast::AttrStyle, Symbol),
239
240     Eof,
241 }
242
243 // `TokenKind` is used a lot. Make sure it doesn't unintentionally get bigger.
244 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
245 rustc_data_structures::static_assert_size!(TokenKind, 16);
246
247 #[derive(Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
248 pub struct Token {
249     pub kind: TokenKind,
250     pub span: Span,
251 }
252
253 impl TokenKind {
254     pub fn lit(kind: LitKind, symbol: Symbol, suffix: Option<Symbol>) -> TokenKind {
255         Literal(Lit::new(kind, symbol, suffix))
256     }
257
258     // An approximation to proc-macro-style single-character operators used by rustc parser.
259     // If the operator token can be broken into two tokens, the first of which is single-character,
260     // then this function performs that operation, otherwise it returns `None`.
261     pub fn break_two_token_op(&self) -> Option<(TokenKind, TokenKind)> {
262         Some(match *self {
263             Le => (Lt, Eq),
264             EqEq => (Eq, Eq),
265             Ne => (Not, Eq),
266             Ge => (Gt, Eq),
267             AndAnd => (BinOp(And), BinOp(And)),
268             OrOr => (BinOp(Or), BinOp(Or)),
269             BinOp(Shl) => (Lt, Lt),
270             BinOp(Shr) => (Gt, Gt),
271             BinOpEq(Plus) => (BinOp(Plus), Eq),
272             BinOpEq(Minus) => (BinOp(Minus), Eq),
273             BinOpEq(Star) => (BinOp(Star), Eq),
274             BinOpEq(Slash) => (BinOp(Slash), Eq),
275             BinOpEq(Percent) => (BinOp(Percent), Eq),
276             BinOpEq(Caret) => (BinOp(Caret), Eq),
277             BinOpEq(And) => (BinOp(And), Eq),
278             BinOpEq(Or) => (BinOp(Or), Eq),
279             BinOpEq(Shl) => (Lt, Le),
280             BinOpEq(Shr) => (Gt, Ge),
281             DotDot => (Dot, Dot),
282             DotDotDot => (Dot, DotDot),
283             ModSep => (Colon, Colon),
284             RArrow => (BinOp(Minus), Gt),
285             LArrow => (Lt, BinOp(Minus)),
286             FatArrow => (Eq, Gt),
287             _ => return None,
288         })
289     }
290
291     /// Returns tokens that are likely to be typed accidentally instead of the current token.
292     /// Enables better error recovery when the wrong token is found.
293     pub fn similar_tokens(&self) -> Option<Vec<TokenKind>> {
294         match *self {
295             Comma => Some(vec![Dot, Lt, Semi]),
296             Semi => Some(vec![Colon, Comma]),
297             FatArrow => Some(vec![Eq, RArrow]),
298             _ => None,
299         }
300     }
301
302     pub fn should_end_const_arg(&self) -> bool {
303         matches!(self, Gt | Ge | BinOp(Shr) | BinOpEq(Shr))
304     }
305 }
306
307 impl Token {
308     pub fn new(kind: TokenKind, span: Span) -> Self {
309         Token { kind, span }
310     }
311
312     /// Some token that will be thrown away later.
313     pub fn dummy() -> Self {
314         Token::new(TokenKind::Question, DUMMY_SP)
315     }
316
317     /// Recovers a `Token` from an `Ident`. This creates a raw identifier if necessary.
318     pub fn from_ast_ident(ident: Ident) -> Self {
319         Token::new(Ident(ident.name, ident.is_raw_guess()), ident.span)
320     }
321
322     /// Return this token by value and leave a dummy token in its place.
323     pub fn take(&mut self) -> Self {
324         mem::replace(self, Token::dummy())
325     }
326
327     /// For interpolated tokens, returns a span of the fragment to which the interpolated
328     /// token refers. For all other tokens this is just a regular span.
329     /// It is particularly important to use this for identifiers and lifetimes
330     /// for which spans affect name resolution and edition checks.
331     /// Note that keywords are also identifiers, so they should use this
332     /// if they keep spans or perform edition checks.
333     pub fn uninterpolated_span(&self) -> Span {
334         match &self.kind {
335             Interpolated(nt) => nt.span(),
336             _ => self.span,
337         }
338     }
339
340     pub fn is_op(&self) -> bool {
341         !matches!(
342             self.kind,
343             OpenDelim(..)
344                 | CloseDelim(..)
345                 | Literal(..)
346                 | DocComment(..)
347                 | Ident(..)
348                 | Lifetime(..)
349                 | Interpolated(..)
350                 | Eof
351         )
352     }
353
354     pub fn is_like_plus(&self) -> bool {
355         matches!(self.kind, BinOp(Plus) | BinOpEq(Plus))
356     }
357
358     /// Returns `true` if the token can appear at the start of an expression.
359     pub fn can_begin_expr(&self) -> bool {
360         match self.uninterpolate().kind {
361             Ident(name, is_raw)              =>
362                 ident_can_begin_expr(name, self.span, is_raw), // value name or keyword
363             OpenDelim(..)                     | // tuple, array or block
364             Literal(..)                       | // literal
365             Not                               | // operator not
366             BinOp(Minus)                      | // unary minus
367             BinOp(Star)                       | // dereference
368             BinOp(Or) | OrOr                  | // closure
369             BinOp(And)                        | // reference
370             AndAnd                            | // double reference
371             // DotDotDot is no longer supported, but we need some way to display the error
372             DotDot | DotDotDot | DotDotEq     | // range notation
373             Lt | BinOp(Shl)                   | // associated path
374             ModSep                            | // global path
375             Lifetime(..)                      | // labeled loop
376             Pound                             => true, // expression attributes
377             Interpolated(ref nt) => matches!(**nt, NtLiteral(..) |
378                 NtExpr(..)    |
379                 NtBlock(..)   |
380                 NtPath(..)),
381             _ => false,
382         }
383     }
384
385     /// Returns `true` if the token can appear at the start of a type.
386     pub fn can_begin_type(&self) -> bool {
387         match self.uninterpolate().kind {
388             Ident(name, is_raw)        =>
389                 ident_can_begin_type(name, self.span, is_raw), // type name or keyword
390             OpenDelim(Paren)            | // tuple
391             OpenDelim(Bracket)          | // array
392             Not                         | // never
393             BinOp(Star)                 | // raw pointer
394             BinOp(And)                  | // reference
395             AndAnd                      | // double reference
396             Question                    | // maybe bound in trait object
397             Lifetime(..)                | // lifetime bound in trait object
398             Lt | BinOp(Shl)             | // associated path
399             ModSep                      => true, // global path
400             Interpolated(ref nt) => matches!(**nt, NtTy(..) | NtPath(..)),
401             _ => false,
402         }
403     }
404
405     /// Returns `true` if the token can appear at the start of a const param.
406     pub fn can_begin_const_arg(&self) -> bool {
407         match self.kind {
408             OpenDelim(Brace) => true,
409             Interpolated(ref nt) => matches!(**nt, NtExpr(..) | NtBlock(..) | NtLiteral(..)),
410             _ => self.can_begin_literal_maybe_minus(),
411         }
412     }
413
414     /// Returns `true` if the token can appear at the start of a generic bound.
415     pub fn can_begin_bound(&self) -> bool {
416         self.is_path_start()
417             || self.is_lifetime()
418             || self.is_keyword(kw::For)
419             || self == &Question
420             || self == &OpenDelim(Paren)
421     }
422
423     /// Returns `true` if the token is any literal.
424     pub fn is_lit(&self) -> bool {
425         matches!(self.kind, Literal(..))
426     }
427
428     /// Returns `true` if the token is any literal, a minus (which can prefix a literal,
429     /// for example a '-42', or one of the boolean idents).
430     ///
431     /// In other words, would this token be a valid start of `parse_literal_maybe_minus`?
432     ///
433     /// Keep this in sync with and `Lit::from_token`, excluding unary negation.
434     pub fn can_begin_literal_maybe_minus(&self) -> bool {
435         match self.uninterpolate().kind {
436             Literal(..) | BinOp(Minus) => true,
437             Ident(name, false) if name.is_bool_lit() => true,
438             Interpolated(ref nt) => match &**nt {
439                 NtLiteral(_) => true,
440                 NtExpr(e) => match &e.kind {
441                     ast::ExprKind::Lit(_) => true,
442                     ast::ExprKind::Unary(ast::UnOp::Neg, e) => {
443                         matches!(&e.kind, ast::ExprKind::Lit(_))
444                     }
445                     _ => false,
446                 },
447                 _ => false,
448             },
449             _ => false,
450         }
451     }
452
453     // A convenience function for matching on identifiers during parsing.
454     // Turns interpolated identifier (`$i: ident`) or lifetime (`$l: lifetime`) token
455     // into the regular identifier or lifetime token it refers to,
456     // otherwise returns the original token.
457     pub fn uninterpolate(&self) -> Cow<'_, Token> {
458         match &self.kind {
459             Interpolated(nt) => match **nt {
460                 NtIdent(ident, is_raw) => {
461                     Cow::Owned(Token::new(Ident(ident.name, is_raw), ident.span))
462                 }
463                 NtLifetime(ident) => Cow::Owned(Token::new(Lifetime(ident.name), ident.span)),
464                 _ => Cow::Borrowed(self),
465             },
466             _ => Cow::Borrowed(self),
467         }
468     }
469
470     /// Returns an identifier if this token is an identifier.
471     pub fn ident(&self) -> Option<(Ident, /* is_raw */ bool)> {
472         let token = self.uninterpolate();
473         match token.kind {
474             Ident(name, is_raw) => Some((Ident::new(name, token.span), is_raw)),
475             _ => None,
476         }
477     }
478
479     /// Returns a lifetime identifier if this token is a lifetime.
480     pub fn lifetime(&self) -> Option<Ident> {
481         let token = self.uninterpolate();
482         match token.kind {
483             Lifetime(name) => Some(Ident::new(name, token.span)),
484             _ => None,
485         }
486     }
487
488     /// Returns `true` if the token is an identifier.
489     pub fn is_ident(&self) -> bool {
490         self.ident().is_some()
491     }
492
493     /// Returns `true` if the token is a lifetime.
494     pub fn is_lifetime(&self) -> bool {
495         self.lifetime().is_some()
496     }
497
498     /// Returns `true` if the token is an identifier whose name is the given
499     /// string slice.
500     pub fn is_ident_named(&self, name: Symbol) -> bool {
501         self.ident().map_or(false, |(ident, _)| ident.name == name)
502     }
503
504     /// Returns `true` if the token is an interpolated path.
505     fn is_path(&self) -> bool {
506         if let Interpolated(ref nt) = self.kind && let NtPath(..) = **nt {
507             return true;
508         }
509         false
510     }
511
512     /// Would `maybe_whole_expr` in `parser.rs` return `Ok(..)`?
513     /// That is, is this a pre-parsed expression dropped into the token stream
514     /// (which happens while parsing the result of macro expansion)?
515     pub fn is_whole_expr(&self) -> bool {
516         if let Interpolated(ref nt) = self.kind
517             && let NtExpr(_) | NtLiteral(_) | NtPath(_) | NtIdent(..) | NtBlock(_) = **nt
518         {
519             return true;
520         }
521
522         false
523     }
524
525     // Is the token an interpolated block (`$b:block`)?
526     pub fn is_whole_block(&self) -> bool {
527         if let Interpolated(ref nt) = self.kind && let NtBlock(..) = **nt {
528             return true;
529         }
530         false
531     }
532
533     /// Returns `true` if the token is either the `mut` or `const` keyword.
534     pub fn is_mutability(&self) -> bool {
535         self.is_keyword(kw::Mut) || self.is_keyword(kw::Const)
536     }
537
538     pub fn is_qpath_start(&self) -> bool {
539         self == &Lt || self == &BinOp(Shl)
540     }
541
542     pub fn is_path_start(&self) -> bool {
543         self == &ModSep
544             || self.is_qpath_start()
545             || self.is_path()
546             || self.is_path_segment_keyword()
547             || self.is_ident() && !self.is_reserved_ident()
548     }
549
550     /// Returns `true` if the token is a given keyword, `kw`.
551     pub fn is_keyword(&self, kw: Symbol) -> bool {
552         self.is_non_raw_ident_where(|id| id.name == kw)
553     }
554
555     pub fn is_path_segment_keyword(&self) -> bool {
556         self.is_non_raw_ident_where(Ident::is_path_segment_keyword)
557     }
558
559     // Returns true for reserved identifiers used internally for elided lifetimes,
560     // unnamed method parameters, crate root module, error recovery etc.
561     pub fn is_special_ident(&self) -> bool {
562         self.is_non_raw_ident_where(Ident::is_special)
563     }
564
565     /// Returns `true` if the token is a keyword used in the language.
566     pub fn is_used_keyword(&self) -> bool {
567         self.is_non_raw_ident_where(Ident::is_used_keyword)
568     }
569
570     /// Returns `true` if the token is a keyword reserved for possible future use.
571     pub fn is_unused_keyword(&self) -> bool {
572         self.is_non_raw_ident_where(Ident::is_unused_keyword)
573     }
574
575     /// Returns `true` if the token is either a special identifier or a keyword.
576     pub fn is_reserved_ident(&self) -> bool {
577         self.is_non_raw_ident_where(Ident::is_reserved)
578     }
579
580     /// Returns `true` if the token is the identifier `true` or `false`.
581     pub fn is_bool_lit(&self) -> bool {
582         self.is_non_raw_ident_where(|id| id.name.is_bool_lit())
583     }
584
585     pub fn is_numeric_lit(&self) -> bool {
586         matches!(
587             self.kind,
588             Literal(Lit { kind: LitKind::Integer, .. }) | Literal(Lit { kind: LitKind::Float, .. })
589         )
590     }
591
592     /// Returns `true` if the token is a non-raw identifier for which `pred` holds.
593     pub fn is_non_raw_ident_where(&self, pred: impl FnOnce(Ident) -> bool) -> bool {
594         match self.ident() {
595             Some((id, false)) => pred(id),
596             _ => false,
597         }
598     }
599
600     pub fn glue(&self, joint: &Token) -> Option<Token> {
601         let kind = match self.kind {
602             Eq => match joint.kind {
603                 Eq => EqEq,
604                 Gt => FatArrow,
605                 _ => return None,
606             },
607             Lt => match joint.kind {
608                 Eq => Le,
609                 Lt => BinOp(Shl),
610                 Le => BinOpEq(Shl),
611                 BinOp(Minus) => LArrow,
612                 _ => return None,
613             },
614             Gt => match joint.kind {
615                 Eq => Ge,
616                 Gt => BinOp(Shr),
617                 Ge => BinOpEq(Shr),
618                 _ => return None,
619             },
620             Not => match joint.kind {
621                 Eq => Ne,
622                 _ => return None,
623             },
624             BinOp(op) => match joint.kind {
625                 Eq => BinOpEq(op),
626                 BinOp(And) if op == And => AndAnd,
627                 BinOp(Or) if op == Or => OrOr,
628                 Gt if op == Minus => RArrow,
629                 _ => return None,
630             },
631             Dot => match joint.kind {
632                 Dot => DotDot,
633                 DotDot => DotDotDot,
634                 _ => return None,
635             },
636             DotDot => match joint.kind {
637                 Dot => DotDotDot,
638                 Eq => DotDotEq,
639                 _ => return None,
640             },
641             Colon => match joint.kind {
642                 Colon => ModSep,
643                 _ => return None,
644             },
645             SingleQuote => match joint.kind {
646                 Ident(name, false) => Lifetime(Symbol::intern(&format!("'{}", name))),
647                 _ => return None,
648             },
649
650             Le | EqEq | Ne | Ge | AndAnd | OrOr | Tilde | BinOpEq(..) | At | DotDotDot
651             | DotDotEq | Comma | Semi | ModSep | RArrow | LArrow | FatArrow | Pound | Dollar
652             | Question | OpenDelim(..) | CloseDelim(..) | Literal(..) | Ident(..)
653             | Lifetime(..) | Interpolated(..) | DocComment(..) | Eof => return None,
654         };
655
656         Some(Token::new(kind, self.span.to(joint.span)))
657     }
658 }
659
660 impl PartialEq<TokenKind> for Token {
661     fn eq(&self, rhs: &TokenKind) -> bool {
662         self.kind == *rhs
663     }
664 }
665
666 #[derive(Clone, Encodable, Decodable)]
667 /// For interpolation during macro expansion.
668 pub enum Nonterminal {
669     NtItem(P<ast::Item>),
670     NtBlock(P<ast::Block>),
671     NtStmt(P<ast::Stmt>),
672     NtPat(P<ast::Pat>),
673     NtExpr(P<ast::Expr>),
674     NtTy(P<ast::Ty>),
675     NtIdent(Ident, /* is_raw */ bool),
676     NtLifetime(Ident),
677     NtLiteral(P<ast::Expr>),
678     /// Stuff inside brackets for attributes
679     NtMeta(P<ast::AttrItem>),
680     NtPath(P<ast::Path>),
681     NtVis(P<ast::Visibility>),
682 }
683
684 // `Nonterminal` is used a lot. Make sure it doesn't unintentionally get bigger.
685 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
686 rustc_data_structures::static_assert_size!(Nonterminal, 16);
687
688 #[derive(Debug, Copy, Clone, PartialEq, Encodable, Decodable)]
689 pub enum NonterminalKind {
690     Item,
691     Block,
692     Stmt,
693     PatParam {
694         /// Keep track of whether the user used `:pat_param` or `:pat` and we inferred it from the
695         /// edition of the span. This is used for diagnostics.
696         inferred: bool,
697     },
698     PatWithOr,
699     Expr,
700     Ty,
701     Ident,
702     Lifetime,
703     Literal,
704     Meta,
705     Path,
706     Vis,
707     TT,
708 }
709
710 impl NonterminalKind {
711     /// The `edition` closure is used to get the edition for the given symbol. Doing
712     /// `span.edition()` is expensive, so we do it lazily.
713     pub fn from_symbol(
714         symbol: Symbol,
715         edition: impl FnOnce() -> Edition,
716     ) -> Option<NonterminalKind> {
717         Some(match symbol {
718             sym::item => NonterminalKind::Item,
719             sym::block => NonterminalKind::Block,
720             sym::stmt => NonterminalKind::Stmt,
721             sym::pat => match edition() {
722                 Edition::Edition2015 | Edition::Edition2018 => {
723                     NonterminalKind::PatParam { inferred: true }
724                 }
725                 Edition::Edition2021 | Edition::Edition2024 => NonterminalKind::PatWithOr,
726             },
727             sym::pat_param => NonterminalKind::PatParam { inferred: false },
728             sym::expr => NonterminalKind::Expr,
729             sym::ty => NonterminalKind::Ty,
730             sym::ident => NonterminalKind::Ident,
731             sym::lifetime => NonterminalKind::Lifetime,
732             sym::literal => NonterminalKind::Literal,
733             sym::meta => NonterminalKind::Meta,
734             sym::path => NonterminalKind::Path,
735             sym::vis => NonterminalKind::Vis,
736             sym::tt => NonterminalKind::TT,
737             _ => return None,
738         })
739     }
740     fn symbol(self) -> Symbol {
741         match self {
742             NonterminalKind::Item => sym::item,
743             NonterminalKind::Block => sym::block,
744             NonterminalKind::Stmt => sym::stmt,
745             NonterminalKind::PatParam { inferred: false } => sym::pat_param,
746             NonterminalKind::PatParam { inferred: true } | NonterminalKind::PatWithOr => sym::pat,
747             NonterminalKind::Expr => sym::expr,
748             NonterminalKind::Ty => sym::ty,
749             NonterminalKind::Ident => sym::ident,
750             NonterminalKind::Lifetime => sym::lifetime,
751             NonterminalKind::Literal => sym::literal,
752             NonterminalKind::Meta => sym::meta,
753             NonterminalKind::Path => sym::path,
754             NonterminalKind::Vis => sym::vis,
755             NonterminalKind::TT => sym::tt,
756         }
757     }
758 }
759
760 impl fmt::Display for NonterminalKind {
761     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
762         write!(f, "{}", self.symbol())
763     }
764 }
765
766 impl Nonterminal {
767     pub fn span(&self) -> Span {
768         match self {
769             NtItem(item) => item.span,
770             NtBlock(block) => block.span,
771             NtStmt(stmt) => stmt.span,
772             NtPat(pat) => pat.span,
773             NtExpr(expr) | NtLiteral(expr) => expr.span,
774             NtTy(ty) => ty.span,
775             NtIdent(ident, _) | NtLifetime(ident) => ident.span,
776             NtMeta(attr_item) => attr_item.span(),
777             NtPath(path) => path.span,
778             NtVis(vis) => vis.span,
779         }
780     }
781 }
782
783 impl PartialEq for Nonterminal {
784     fn eq(&self, rhs: &Self) -> bool {
785         match (self, rhs) {
786             (NtIdent(ident_lhs, is_raw_lhs), NtIdent(ident_rhs, is_raw_rhs)) => {
787                 ident_lhs == ident_rhs && is_raw_lhs == is_raw_rhs
788             }
789             (NtLifetime(ident_lhs), NtLifetime(ident_rhs)) => ident_lhs == ident_rhs,
790             // FIXME: Assume that all "complex" nonterminal are not equal, we can't compare them
791             // correctly based on data from AST. This will prevent them from matching each other
792             // in macros. The comparison will become possible only when each nonterminal has an
793             // attached token stream from which it was parsed.
794             _ => false,
795         }
796     }
797 }
798
799 impl fmt::Debug for Nonterminal {
800     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
801         match *self {
802             NtItem(..) => f.pad("NtItem(..)"),
803             NtBlock(..) => f.pad("NtBlock(..)"),
804             NtStmt(..) => f.pad("NtStmt(..)"),
805             NtPat(..) => f.pad("NtPat(..)"),
806             NtExpr(..) => f.pad("NtExpr(..)"),
807             NtTy(..) => f.pad("NtTy(..)"),
808             NtIdent(..) => f.pad("NtIdent(..)"),
809             NtLiteral(..) => f.pad("NtLiteral(..)"),
810             NtMeta(..) => f.pad("NtMeta(..)"),
811             NtPath(..) => f.pad("NtPath(..)"),
812             NtVis(..) => f.pad("NtVis(..)"),
813             NtLifetime(..) => f.pad("NtLifetime(..)"),
814         }
815     }
816 }
817
818 impl<CTX> HashStable<CTX> for Nonterminal
819 where
820     CTX: crate::HashStableContext,
821 {
822     fn hash_stable(&self, _hcx: &mut CTX, _hasher: &mut StableHasher) {
823         panic!("interpolated tokens should not be present in the HIR")
824     }
825 }