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