]>
Commit | Line | Data |
---|---|---|
3dfed10e XL |
1 | use rustc_ast::ptr::P; |
2 | use rustc_ast::token::{self, Nonterminal, NonterminalKind, Token}; | |
17df50a5 | 3 | use rustc_ast::AstLike; |
3dfed10e XL |
4 | use rustc_ast_pretty::pprust; |
5 | use rustc_errors::PResult; | |
6 | use rustc_span::symbol::{kw, Ident}; | |
7 | ||
136023e0 | 8 | use crate::parser::pat::{RecoverColon, RecoverComma}; |
5869c6ff | 9 | use crate::parser::{FollowedByType, ForceCollect, Parser, PathStyle}; |
3dfed10e XL |
10 | |
11 | impl<'a> Parser<'a> { | |
12 | /// Checks whether a non-terminal may begin with a particular token. | |
13 | /// | |
14 | /// Returning `false` is a *stability guarantee* that such a matcher will *never* begin with that | |
15 | /// token. Be conservative (return true) if not sure. | |
5869c6ff | 16 | pub fn nonterminal_may_begin_with(kind: NonterminalKind, token: &Token) -> bool { |
3dfed10e XL |
17 | /// Checks whether the non-terminal may contain a single (non-keyword) identifier. |
18 | fn may_be_ident(nt: &token::Nonterminal) -> bool { | |
19 | match *nt { | |
20 | token::NtItem(_) | token::NtBlock(_) | token::NtVis(_) | token::NtLifetime(_) => { | |
21 | false | |
22 | } | |
23 | _ => true, | |
24 | } | |
25 | } | |
26 | ||
27 | match kind { | |
28 | NonterminalKind::Expr => { | |
29 | token.can_begin_expr() | |
30 | // This exception is here for backwards compatibility. | |
31 | && !token.is_keyword(kw::Let) | |
b9856134 XL |
32 | // This exception is here for backwards compatibility. |
33 | && !token.is_keyword(kw::Const) | |
3dfed10e XL |
34 | } |
35 | NonterminalKind::Ty => token.can_begin_type(), | |
36 | NonterminalKind::Ident => get_macro_ident(token).is_some(), | |
37 | NonterminalKind::Literal => token.can_begin_literal_maybe_minus(), | |
38 | NonterminalKind::Vis => match token.kind { | |
39 | // The follow-set of :vis + "priv" keyword + interpolated | |
40 | token::Comma | token::Ident(..) | token::Interpolated(..) => true, | |
41 | _ => token.can_begin_type(), | |
42 | }, | |
43 | NonterminalKind::Block => match token.kind { | |
44 | token::OpenDelim(token::Brace) => true, | |
5869c6ff XL |
45 | token::Interpolated(ref nt) => !matches!( |
46 | **nt, | |
47 | token::NtItem(_) | |
48 | | token::NtPat(_) | |
49 | | token::NtTy(_) | |
50 | | token::NtIdent(..) | |
51 | | token::NtMeta(_) | |
52 | | token::NtPath(_) | |
53 | | token::NtVis(_) | |
54 | ), | |
3dfed10e XL |
55 | _ => false, |
56 | }, | |
57 | NonterminalKind::Path | NonterminalKind::Meta => match token.kind { | |
58 | token::ModSep | token::Ident(..) => true, | |
59 | token::Interpolated(ref nt) => match **nt { | |
60 | token::NtPath(_) | token::NtMeta(_) => true, | |
61 | _ => may_be_ident(&nt), | |
62 | }, | |
63 | _ => false, | |
64 | }, | |
cdc7bbd5 XL |
65 | NonterminalKind::PatParam { .. } | NonterminalKind::PatWithOr { .. } => { |
66 | match token.kind { | |
3dfed10e XL |
67 | token::Ident(..) | // box, ref, mut, and other identifiers (can stricten) |
68 | token::OpenDelim(token::Paren) | // tuple pattern | |
69 | token::OpenDelim(token::Bracket) | // slice pattern | |
70 | token::BinOp(token::And) | // reference | |
71 | token::BinOp(token::Minus) | // negative literal | |
72 | token::AndAnd | // double reference | |
73 | token::Literal(..) | // literal | |
74 | token::DotDot | // range pattern (future compat) | |
75 | token::DotDotDot | // range pattern (future compat) | |
76 | token::ModSep | // path | |
77 | token::Lt | // path (UFCS constant) | |
78 | token::BinOp(token::Shl) => true, // path (double UFCS) | |
fc512014 | 79 | // leading vert `|` or-pattern |
cdc7bbd5 | 80 | token::BinOp(token::Or) => matches!(kind, NonterminalKind::PatWithOr {..}), |
3dfed10e XL |
81 | token::Interpolated(ref nt) => may_be_ident(nt), |
82 | _ => false, | |
cdc7bbd5 XL |
83 | } |
84 | } | |
3dfed10e XL |
85 | NonterminalKind::Lifetime => match token.kind { |
86 | token::Lifetime(_) => true, | |
29967ef6 XL |
87 | token::Interpolated(ref nt) => { |
88 | matches!(**nt, token::NtLifetime(_) | token::NtTT(_)) | |
89 | } | |
3dfed10e XL |
90 | _ => false, |
91 | }, | |
29967ef6 XL |
92 | NonterminalKind::TT | NonterminalKind::Item | NonterminalKind::Stmt => { |
93 | !matches!(token.kind, token::CloseDelim(_)) | |
94 | } | |
3dfed10e XL |
95 | } |
96 | } | |
97 | ||
fc512014 | 98 | /// Parse a non-terminal (e.g. MBE `:pat` or `:ident`). |
5869c6ff | 99 | pub fn parse_nonterminal(&mut self, kind: NonterminalKind) -> PResult<'a, Nonterminal> { |
3dfed10e XL |
100 | // Any `Nonterminal` which stores its tokens (currently `NtItem` and `NtExpr`) |
101 | // needs to have them force-captured here. | |
102 | // A `macro_rules!` invocation may pass a captured item/expr to a proc-macro, | |
103 | // which requires having captured tokens available. Since we cannot determine | |
104 | // in advance whether or not a proc-macro will be (transitively) invoked, | |
105 | // we always capture tokens for any `Nonterminal` which needs them. | |
17df50a5 | 106 | let mut nt = match kind { |
5869c6ff XL |
107 | NonterminalKind::Item => match self.parse_item(ForceCollect::Yes)? { |
108 | Some(item) => token::NtItem(item), | |
109 | None => { | |
3dfed10e XL |
110 | return Err(self.struct_span_err(self.token.span, "expected an item keyword")); |
111 | } | |
112 | }, | |
1b1a35ee | 113 | NonterminalKind::Block => { |
6a06907d XL |
114 | // While a block *expression* may have attributes (e.g. `#[my_attr] { ... }`), |
115 | // the ':block' matcher does not support them | |
116 | token::NtBlock(self.collect_tokens_no_attrs(|this| this.parse_block())?) | |
1b1a35ee | 117 | } |
5869c6ff XL |
118 | NonterminalKind::Stmt => match self.parse_stmt(ForceCollect::Yes)? { |
119 | Some(s) => token::NtStmt(s), | |
120 | None => { | |
121 | return Err(self.struct_span_err(self.token.span, "expected a statement")); | |
1b1a35ee | 122 | } |
5869c6ff | 123 | }, |
cdc7bbd5 | 124 | NonterminalKind::PatParam { .. } | NonterminalKind::PatWithOr { .. } => { |
6a06907d | 125 | token::NtPat(self.collect_tokens_no_attrs(|this| match kind { |
cdc7bbd5 XL |
126 | NonterminalKind::PatParam { .. } => this.parse_pat_no_top_alt(None), |
127 | NonterminalKind::PatWithOr { .. } => { | |
136023e0 | 128 | this.parse_pat_allow_top_alt(None, RecoverComma::No, RecoverColon::No) |
fc512014 | 129 | } |
5869c6ff XL |
130 | _ => unreachable!(), |
131 | })?) | |
3dfed10e | 132 | } |
6a06907d | 133 | |
cdc7bbd5 | 134 | NonterminalKind::Expr => token::NtExpr(self.parse_expr_force_collect()?), |
1b1a35ee | 135 | NonterminalKind::Literal => { |
6a06907d XL |
136 | // The `:literal` matcher does not support attributes |
137 | token::NtLiteral( | |
138 | self.collect_tokens_no_attrs(|this| this.parse_literal_maybe_minus())?, | |
139 | ) | |
140 | } | |
141 | ||
142 | NonterminalKind::Ty => { | |
143 | token::NtTy(self.collect_tokens_no_attrs(|this| this.parse_ty())?) | |
1b1a35ee | 144 | } |
3dfed10e XL |
145 | // this could be handled like a token, since it is one |
146 | NonterminalKind::Ident => { | |
147 | if let Some((ident, is_raw)) = get_macro_ident(&self.token) { | |
148 | self.bump(); | |
149 | token::NtIdent(ident, is_raw) | |
150 | } else { | |
151 | let token_str = pprust::token_to_string(&self.token); | |
152 | let msg = &format!("expected ident, found {}", &token_str); | |
153 | return Err(self.struct_span_err(self.token.span, msg)); | |
154 | } | |
155 | } | |
6a06907d XL |
156 | NonterminalKind::Path => token::NtPath( |
157 | self.collect_tokens_no_attrs(|this| this.parse_path(PathStyle::Type))?, | |
158 | ), | |
cdc7bbd5 | 159 | NonterminalKind::Meta => token::NtMeta(P(self.parse_attr_item(true)?)), |
3dfed10e | 160 | NonterminalKind::TT => token::NtTT(self.parse_token_tree()), |
5869c6ff | 161 | NonterminalKind::Vis => token::NtVis( |
6a06907d | 162 | self.collect_tokens_no_attrs(|this| this.parse_visibility(FollowedByType::Yes))?, |
5869c6ff | 163 | ), |
3dfed10e XL |
164 | NonterminalKind::Lifetime => { |
165 | if self.check_lifetime() { | |
166 | token::NtLifetime(self.expect_lifetime().ident) | |
167 | } else { | |
168 | let token_str = pprust::token_to_string(&self.token); | |
169 | let msg = &format!("expected a lifetime, found `{}`", &token_str); | |
170 | return Err(self.struct_span_err(self.token.span, msg)); | |
171 | } | |
172 | } | |
17df50a5 XL |
173 | }; |
174 | ||
175 | // If tokens are supported at all, they should be collected. | |
176 | if matches!(nt.tokens_mut(), Some(None)) { | |
177 | panic!( | |
178 | "Missing tokens for nt {:?} at {:?}: {:?}", | |
179 | nt, | |
180 | nt.span(), | |
181 | pprust::nonterminal_to_string(&nt) | |
182 | ); | |
183 | } | |
184 | ||
185 | Ok(nt) | |
3dfed10e XL |
186 | } |
187 | } | |
188 | ||
189 | /// The token is an identifier, but not `_`. | |
190 | /// We prohibit passing `_` to macros expecting `ident` for now. | |
191 | fn get_macro_ident(token: &Token) -> Option<(Ident, bool)> { | |
192 | token.ident().filter(|(ident, _)| ident.name != kw::Underscore) | |
193 | } |