]> git.proxmox.com Git - rustc.git/blob - compiler/rustc_parse/src/parser/nonterminal.rs
New upstream version 1.49.0~beta.4+dfsg1
[rustc.git] / compiler / rustc_parse / src / parser / nonterminal.rs
1 use rustc_ast::ptr::P;
2 use rustc_ast::token::{self, Nonterminal, NonterminalKind, Token};
3 use rustc_ast_pretty::pprust;
4 use rustc_errors::PResult;
5 use rustc_span::symbol::{kw, Ident};
6
7 use crate::parser::{FollowedByType, Parser, PathStyle};
8
9 impl<'a> Parser<'a> {
10 /// Checks whether a non-terminal may begin with a particular token.
11 ///
12 /// Returning `false` is a *stability guarantee* that such a matcher will *never* begin with that
13 /// token. Be conservative (return true) if not sure.
14 pub fn nonterminal_may_begin_with(kind: NonterminalKind, token: &Token) -> bool {
15 /// Checks whether the non-terminal may contain a single (non-keyword) identifier.
16 fn may_be_ident(nt: &token::Nonterminal) -> bool {
17 match *nt {
18 token::NtItem(_) | token::NtBlock(_) | token::NtVis(_) | token::NtLifetime(_) => {
19 false
20 }
21 _ => true,
22 }
23 }
24
25 match kind {
26 NonterminalKind::Expr => {
27 token.can_begin_expr()
28 // This exception is here for backwards compatibility.
29 && !token.is_keyword(kw::Let)
30 }
31 NonterminalKind::Ty => token.can_begin_type(),
32 NonterminalKind::Ident => get_macro_ident(token).is_some(),
33 NonterminalKind::Literal => token.can_begin_literal_maybe_minus(),
34 NonterminalKind::Vis => match token.kind {
35 // The follow-set of :vis + "priv" keyword + interpolated
36 token::Comma | token::Ident(..) | token::Interpolated(..) => true,
37 _ => token.can_begin_type(),
38 },
39 NonterminalKind::Block => match token.kind {
40 token::OpenDelim(token::Brace) => true,
41 token::Interpolated(ref nt) => !matches!(**nt, token::NtItem(_)
42 | token::NtPat(_)
43 | token::NtTy(_)
44 | token::NtIdent(..)
45 | token::NtMeta(_)
46 | token::NtPath(_)
47 | token::NtVis(_)),
48 _ => false,
49 },
50 NonterminalKind::Path | NonterminalKind::Meta => match token.kind {
51 token::ModSep | token::Ident(..) => true,
52 token::Interpolated(ref nt) => match **nt {
53 token::NtPath(_) | token::NtMeta(_) => true,
54 _ => may_be_ident(&nt),
55 },
56 _ => false,
57 },
58 NonterminalKind::Pat => match token.kind {
59 token::Ident(..) | // box, ref, mut, and other identifiers (can stricten)
60 token::OpenDelim(token::Paren) | // tuple pattern
61 token::OpenDelim(token::Bracket) | // slice pattern
62 token::BinOp(token::And) | // reference
63 token::BinOp(token::Minus) | // negative literal
64 token::AndAnd | // double reference
65 token::Literal(..) | // literal
66 token::DotDot | // range pattern (future compat)
67 token::DotDotDot | // range pattern (future compat)
68 token::ModSep | // path
69 token::Lt | // path (UFCS constant)
70 token::BinOp(token::Shl) => true, // path (double UFCS)
71 token::Interpolated(ref nt) => may_be_ident(nt),
72 _ => false,
73 },
74 NonterminalKind::Lifetime => match token.kind {
75 token::Lifetime(_) => true,
76 token::Interpolated(ref nt) => {
77 matches!(**nt, token::NtLifetime(_) | token::NtTT(_))
78 }
79 _ => false,
80 },
81 NonterminalKind::TT | NonterminalKind::Item | NonterminalKind::Stmt => {
82 !matches!(token.kind, token::CloseDelim(_))
83 }
84 }
85 }
86
87 pub fn parse_nonterminal(&mut self, kind: NonterminalKind) -> PResult<'a, Nonterminal> {
88 // Any `Nonterminal` which stores its tokens (currently `NtItem` and `NtExpr`)
89 // needs to have them force-captured here.
90 // A `macro_rules!` invocation may pass a captured item/expr to a proc-macro,
91 // which requires having captured tokens available. Since we cannot determine
92 // in advance whether or not a proc-macro will be (transitively) invoked,
93 // we always capture tokens for any `Nonterminal` which needs them.
94 Ok(match kind {
95 NonterminalKind::Item => match self.collect_tokens(|this| this.parse_item())? {
96 (Some(mut item), tokens) => {
97 // If we captured tokens during parsing (due to outer attributes),
98 // use those.
99 if item.tokens.is_none() {
100 item.tokens = tokens;
101 }
102 token::NtItem(item)
103 }
104 (None, _) => {
105 return Err(self.struct_span_err(self.token.span, "expected an item keyword"));
106 }
107 },
108 NonterminalKind::Block => {
109 let (mut block, tokens) = self.collect_tokens(|this| this.parse_block())?;
110 // We have have eaten an NtBlock, which could already have tokens
111 if block.tokens.is_none() {
112 block.tokens = tokens;
113 }
114 token::NtBlock(block)
115 }
116 NonterminalKind::Stmt => {
117 let (stmt, tokens) = self.collect_tokens(|this| this.parse_stmt())?;
118 match stmt {
119 Some(mut s) => {
120 if s.tokens.is_none() {
121 s.tokens = tokens;
122 }
123 token::NtStmt(s)
124 }
125 None => {
126 return Err(self.struct_span_err(self.token.span, "expected a statement"));
127 }
128 }
129 }
130 NonterminalKind::Pat => {
131 let (mut pat, tokens) = self.collect_tokens(|this| this.parse_pat(None))?;
132 // We have have eaten an NtPat, which could already have tokens
133 if pat.tokens.is_none() {
134 pat.tokens = tokens;
135 }
136 token::NtPat(pat)
137 }
138 NonterminalKind::Expr => {
139 let (mut expr, tokens) = self.collect_tokens(|this| this.parse_expr())?;
140 // If we captured tokens during parsing (due to outer attributes),
141 // use those.
142 if expr.tokens.is_none() {
143 expr.tokens = tokens;
144 }
145 token::NtExpr(expr)
146 }
147 NonterminalKind::Literal => {
148 let (mut lit, tokens) =
149 self.collect_tokens(|this| this.parse_literal_maybe_minus())?;
150 // We have have eaten a nonterminal, which could already have tokens
151 if lit.tokens.is_none() {
152 lit.tokens = tokens;
153 }
154 token::NtLiteral(lit)
155 }
156 NonterminalKind::Ty => {
157 let (mut ty, tokens) = self.collect_tokens(|this| this.parse_ty())?;
158 // We have an eaten an NtTy, which could already have tokens
159 if ty.tokens.is_none() {
160 ty.tokens = tokens;
161 }
162 token::NtTy(ty)
163 }
164 // this could be handled like a token, since it is one
165 NonterminalKind::Ident => {
166 if let Some((ident, is_raw)) = get_macro_ident(&self.token) {
167 self.bump();
168 token::NtIdent(ident, is_raw)
169 } else {
170 let token_str = pprust::token_to_string(&self.token);
171 let msg = &format!("expected ident, found {}", &token_str);
172 return Err(self.struct_span_err(self.token.span, msg));
173 }
174 }
175 NonterminalKind::Path => {
176 let (mut path, tokens) =
177 self.collect_tokens(|this| this.parse_path(PathStyle::Type))?;
178 // We have have eaten an NtPath, which could already have tokens
179 if path.tokens.is_none() {
180 path.tokens = tokens;
181 }
182 token::NtPath(path)
183 }
184 NonterminalKind::Meta => {
185 let (mut attr, tokens) = self.collect_tokens(|this| this.parse_attr_item(false))?;
186 // We may have eaten a nonterminal, which could already have tokens
187 if attr.tokens.is_none() {
188 attr.tokens = tokens;
189 }
190 token::NtMeta(P(attr))
191 }
192 NonterminalKind::TT => token::NtTT(self.parse_token_tree()),
193 NonterminalKind::Vis => {
194 let (mut vis, tokens) =
195 self.collect_tokens(|this| this.parse_visibility(FollowedByType::Yes))?;
196 // We may have etan an `NtVis`, which could already have tokens
197 if vis.tokens.is_none() {
198 vis.tokens = tokens;
199 }
200 token::NtVis(vis)
201 }
202 NonterminalKind::Lifetime => {
203 if self.check_lifetime() {
204 token::NtLifetime(self.expect_lifetime().ident)
205 } else {
206 let token_str = pprust::token_to_string(&self.token);
207 let msg = &format!("expected a lifetime, found `{}`", &token_str);
208 return Err(self.struct_span_err(self.token.span, msg));
209 }
210 }
211 })
212 }
213 }
214
215 /// The token is an identifier, but not `_`.
216 /// We prohibit passing `_` to macros expecting `ident` for now.
217 fn get_macro_ident(token: &Token) -> Option<(Ident, bool)> {
218 token.ident().filter(|(ident, _)| ident.name != kw::Underscore)
219 }