]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_parse/src/lib.rs
New upstream version 1.50.0+dfsg1
[rustc.git] / compiler / rustc_parse / src / lib.rs
CommitLineData
9fa01778
XL
1//! The main parser interface.
2
60c5eb7d 3#![feature(crate_visibility_modifier)]
ba9703b0 4#![feature(bindings_after_at)]
1b1a35ee 5#![feature(iter_order_by)]
ba9703b0 6#![feature(or_patterns)]
60c5eb7d 7
3dfed10e 8use rustc_ast as ast;
fc512014 9use rustc_ast::attr::HasAttrs;
29967ef6
XL
10use rustc_ast::token::{self, DelimToken, Nonterminal, Token, TokenKind};
11use rustc_ast::tokenstream::{self, LazyTokenStream, TokenStream, TokenTree};
74b04a01 12use rustc_ast_pretty::pprust;
fc512014 13use rustc_data_structures::fx::FxHashSet;
e74abb32 14use rustc_data_structures::sync::Lrc;
dfeec247 15use rustc_errors::{Diagnostic, FatalError, Level, PResult};
74b04a01 16use rustc_session::parse::ParseSess;
3dfed10e 17use rustc_span::{symbol::kw, FileName, SourceFile, Span, DUMMY_SP};
9346a6ac 18
3dfed10e 19use smallvec::SmallVec;
fc512014 20use std::cell::RefCell;
3dfed10e 21use std::mem;
ba9703b0 22use std::path::Path;
1a4d82fc 23use std::str;
223e47cc 24
3dfed10e 25use tracing::{debug, info};
e74abb32 26
29967ef6 27pub const MACRO_ARGUMENTS: Option<&str> = Some("macro arguments");
9346a6ac 28
1a4d82fc 29#[macro_use]
223e47cc 30pub mod parser;
dfeec247 31use parser::{emit_unclosed_delims, make_unclosed_delims_error, Parser};
1a4d82fc 32pub mod lexer;
60c5eb7d 33pub mod validate_attr;
476ff2be 34
e1599b0c 35// A bunch of utility functions of the form `parse_<thing>_from_<source>`
223e47cc
LB
36// where <thing> includes crate, expr, item, stmt, tts, and one that
37// uses a HOF to parse anything, and <source> includes file and
e1599b0c 38// `source_str`.
223e47cc 39
e74abb32
XL
40/// A variant of 'panictry!' that works on a Vec<Diagnostic> instead of a single DiagnosticBuilder.
41macro_rules! panictry_buffer {
dfeec247 42 ($handler:expr, $e:expr) => {{
60c5eb7d 43 use rustc_errors::FatalError;
dfeec247 44 use std::result::Result::{Err, Ok};
e74abb32
XL
45 match $e {
46 Ok(e) => e,
47 Err(errs) => {
48 for e in errs {
49 $handler.emit_diagnostic(&e);
50 }
51 FatalError.raise()
52 }
53 }
dfeec247 54 }};
e74abb32
XL
55}
56
c30ab7b3 57pub fn parse_crate_from_file<'a>(input: &Path, sess: &'a ParseSess) -> PResult<'a, ast::Crate> {
ba9703b0 58 let mut parser = new_parser_from_file(sess, input, None);
54a0048b 59 parser.parse_crate_mod()
223e47cc
LB
60}
61
dfeec247
XL
62pub fn parse_crate_attrs_from_file<'a>(
63 input: &Path,
64 sess: &'a ParseSess,
65) -> PResult<'a, Vec<ast::Attribute>> {
ba9703b0 66 let mut parser = new_parser_from_file(sess, input, None);
54a0048b 67 parser.parse_inner_attributes()
1a4d82fc
JJ
68}
69
dfeec247
XL
70pub fn parse_crate_from_source_str(
71 name: FileName,
72 source: String,
73 sess: &ParseSess,
74) -> PResult<'_, ast::Crate> {
c30ab7b3 75 new_parser_from_source_str(sess, name, source).parse_crate_mod()
223e47cc
LB
76}
77
dfeec247
XL
78pub fn parse_crate_attrs_from_source_str(
79 name: FileName,
80 source: String,
81 sess: &ParseSess,
82) -> PResult<'_, Vec<ast::Attribute>> {
c30ab7b3 83 new_parser_from_source_str(sess, name, source).parse_inner_attributes()
1a4d82fc
JJ
84}
85
9fa01778
XL
86pub fn parse_stream_from_source_str(
87 name: FileName,
88 source: String,
89 sess: &ParseSess,
90 override_span: Option<Span>,
91) -> TokenStream {
dfeec247
XL
92 let (stream, mut errors) =
93 source_file_to_stream(sess, sess.source_map().new_source_file(name, source), override_span);
e74abb32 94 emit_unclosed_delims(&mut errors, &sess);
9fa01778 95 stream
223e47cc
LB
96}
97
9fa01778
XL
98/// Creates a new parser from a source string.
99pub fn new_parser_from_source_str(sess: &ParseSess, name: FileName, source: String) -> Parser<'_> {
a1dfa0c6
XL
100 panictry_buffer!(&sess.span_diagnostic, maybe_new_parser_from_source_str(sess, name, source))
101}
102
9fa01778 103/// Creates a new parser from a source string. Returns any buffered errors from lexing the initial
a1dfa0c6 104/// token stream.
dfeec247
XL
105pub fn maybe_new_parser_from_source_str(
106 sess: &ParseSess,
107 name: FileName,
108 source: String,
109) -> Result<Parser<'_>, Vec<Diagnostic>> {
ba9703b0 110 maybe_source_file_to_parser(sess, sess.source_map().new_source_file(name, source))
223e47cc
LB
111}
112
e1599b0c 113/// Creates a new parser, handling errors as appropriate if the file doesn't exist.
1b1a35ee 114/// If a span is given, that is used on an error as the source of the problem.
ba9703b0
XL
115pub fn new_parser_from_file<'a>(sess: &'a ParseSess, path: &Path, sp: Option<Span>) -> Parser<'a> {
116 source_file_to_parser(sess, file_to_source_file(sess, path, sp))
223e47cc
LB
117}
118
e1599b0c 119/// Given a `source_file` and config, returns a parser.
9fa01778 120fn source_file_to_parser(sess: &ParseSess, source_file: Lrc<SourceFile>) -> Parser<'_> {
dfeec247 121 panictry_buffer!(&sess.span_diagnostic, maybe_source_file_to_parser(sess, source_file))
a1dfa0c6
XL
122}
123
e1599b0c 124/// Given a `source_file` and config, return a parser. Returns any buffered errors from lexing the
a1dfa0c6 125/// initial token stream.
9fa01778
XL
126fn maybe_source_file_to_parser(
127 sess: &ParseSess,
128 source_file: Lrc<SourceFile>,
129) -> Result<Parser<'_>, Vec<Diagnostic>> {
b7449926 130 let end_pos = source_file.end_pos;
9fa01778 131 let (stream, unclosed_delims) = maybe_file_to_stream(sess, source_file, None)?;
dc9dc135 132 let mut parser = stream_to_parser(sess, stream, None);
9fa01778 133 parser.unclosed_delims = unclosed_delims;
74b04a01 134 if parser.token == token::Eof {
dc9dc135 135 parser.token.span = Span::new(end_pos, end_pos, parser.token.span.ctxt());
c1a9b12d
SL
136 }
137
a1dfa0c6 138 Ok(parser)
970d7e83
LB
139}
140
e1599b0c 141// Base abstractions
970d7e83 142
0731742a
XL
143/// Given a session and a path and an optional span (for error reporting),
144/// add the path to the session's source_map and return the new source_file or
145/// error when a file can't be read.
dfeec247
XL
146fn try_file_to_source_file(
147 sess: &ParseSess,
148 path: &Path,
149 spanopt: Option<Span>,
150) -> Result<Lrc<SourceFile>, Diagnostic> {
151 sess.source_map().load_file(path).map_err(|e| {
0731742a
XL
152 let msg = format!("couldn't read {}: {}", path.display(), e);
153 let mut diag = Diagnostic::new(Level::Fatal, &msg);
154 if let Some(sp) = spanopt {
155 diag.set_span(sp);
156 }
157 diag
158 })
159}
160
970d7e83 161/// Given a session and a path and an optional span (for error reporting),
e1599b0c 162/// adds the path to the session's `source_map` and returns the new `source_file`.
dfeec247 163fn file_to_source_file(sess: &ParseSess, path: &Path, spanopt: Option<Span>) -> Lrc<SourceFile> {
0731742a 164 match try_file_to_source_file(sess, path, spanopt) {
b7449926 165 Ok(source_file) => source_file,
0731742a 166 Err(d) => {
e1599b0c 167 sess.span_diagnostic.emit_diagnostic(&d);
0731742a 168 FatalError.raise();
223e47cc
LB
169 }
170 }
171}
172
e1599b0c 173/// Given a `source_file`, produces a sequence of token trees.
9fa01778
XL
174pub fn source_file_to_stream(
175 sess: &ParseSess,
176 source_file: Lrc<SourceFile>,
177 override_span: Option<Span>,
178) -> (TokenStream, Vec<lexer::UnmatchedBrace>) {
a1dfa0c6
XL
179 panictry_buffer!(&sess.span_diagnostic, maybe_file_to_stream(sess, source_file, override_span))
180}
181
9fa01778 182/// Given a source file, produces a sequence of token trees. Returns any buffered errors from
48663c56 183/// parsing the token stream.
9fa01778
XL
184pub fn maybe_file_to_stream(
185 sess: &ParseSess,
186 source_file: Lrc<SourceFile>,
187 override_span: Option<Span>,
188) -> Result<(TokenStream, Vec<lexer::UnmatchedBrace>), Vec<Diagnostic>> {
1b1a35ee
XL
189 let src = source_file.src.as_ref().unwrap_or_else(|| {
190 sess.span_diagnostic
191 .bug(&format!("cannot lex `source_file` without source: {}", source_file.name));
192 });
193
194 let (token_trees, unmatched_braces) =
195 lexer::parse_token_trees(sess, src.as_str(), source_file.start_pos, override_span);
a1dfa0c6 196
48663c56
XL
197 match token_trees {
198 Ok(stream) => Ok((stream, unmatched_braces)),
a1dfa0c6
XL
199 Err(err) => {
200 let mut buffer = Vec::with_capacity(1);
201 err.buffer(&mut buffer);
9fa01778 202 // Not using `emit_unclosed_delims` to use `db.buffer`
48663c56 203 for unmatched in unmatched_braces {
e74abb32
XL
204 if let Some(err) = make_unclosed_delims_error(unmatched, &sess) {
205 err.buffer(&mut buffer);
9fa01778 206 }
9fa01778 207 }
a1dfa0c6
XL
208 Err(buffer)
209 }
210 }
970d7e83
LB
211}
212
e1599b0c 213/// Given a stream and the `ParseSess`, produces a parser.
dc9dc135
XL
214pub fn stream_to_parser<'a>(
215 sess: &'a ParseSess,
216 stream: TokenStream,
217 subparser_name: Option<&'static str>,
218) -> Parser<'a> {
ba9703b0 219 Parser::new(sess, stream, false, subparser_name)
1a4d82fc
JJ
220}
221
e74abb32 222/// Runs the given subparser `f` on the tokens of the given `attr`'s item.
60c5eb7d 223pub fn parse_in<'a, T>(
e74abb32 224 sess: &'a ParseSess,
60c5eb7d
XL
225 tts: TokenStream,
226 name: &'static str,
e74abb32
XL
227 mut f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>,
228) -> PResult<'a, T> {
ba9703b0 229 let mut parser = Parser::new(sess, tts, false, Some(name));
e74abb32
XL
230 let result = f(&mut parser)?;
231 if parser.token != token::Eof {
232 parser.unexpected()?;
233 }
234 Ok(result)
8faf50e0
XL
235}
236
e74abb32
XL
237// NOTE(Centril): The following probably shouldn't be here but it acknowledges the
238// fact that architecturally, we are using parsing (read on below to understand why).
239
240pub fn nt_to_tokenstream(nt: &Nonterminal, sess: &ParseSess, span: Span) -> TokenStream {
241 // A `Nonterminal` is often a parsed AST item. At this point we now
242 // need to convert the parsed AST to an actual token stream, e.g.
243 // un-parse it basically.
244 //
245 // Unfortunately there's not really a great way to do that in a
246 // guaranteed lossless fashion right now. The fallback here is to just
247 // stringify the AST node and reparse it, but this loses all span
248 // information.
249 //
250 // As a result, some AST nodes are annotated with the token stream they
251 // came from. Here we attempt to extract these lossless token streams
252 // before we fall back to the stringification.
29967ef6
XL
253
254 let convert_tokens =
fc512014 255 |tokens: Option<&LazyTokenStream>| tokens.as_ref().map(|t| t.create_token_stream());
29967ef6 256
e74abb32 257 let tokens = match *nt {
29967ef6 258 Nonterminal::NtItem(ref item) => prepend_attrs(&item.attrs, item.tokens.as_ref()),
fc512014
XL
259 Nonterminal::NtBlock(ref block) => convert_tokens(block.tokens.as_ref()),
260 Nonterminal::NtStmt(ref stmt) => prepend_attrs(stmt.attrs(), stmt.tokens()),
261 Nonterminal::NtPat(ref pat) => convert_tokens(pat.tokens.as_ref()),
262 Nonterminal::NtTy(ref ty) => convert_tokens(ty.tokens.as_ref()),
e74abb32
XL
263 Nonterminal::NtIdent(ident, is_raw) => {
264 Some(tokenstream::TokenTree::token(token::Ident(ident.name, is_raw), ident.span).into())
265 }
266 Nonterminal::NtLifetime(ident) => {
267 Some(tokenstream::TokenTree::token(token::Lifetime(ident.name), ident.span).into())
8faf50e0 268 }
fc512014
XL
269 Nonterminal::NtMeta(ref attr) => convert_tokens(attr.tokens.as_ref()),
270 Nonterminal::NtPath(ref path) => convert_tokens(path.tokens.as_ref()),
271 Nonterminal::NtVis(ref vis) => convert_tokens(vis.tokens.as_ref()),
dfeec247 272 Nonterminal::NtTT(ref tt) => Some(tt.clone().into()),
1b1a35ee 273 Nonterminal::NtExpr(ref expr) | Nonterminal::NtLiteral(ref expr) => {
f9f354fc
XL
274 if expr.tokens.is_none() {
275 debug!("missing tokens for expr {:?}", expr);
276 }
29967ef6 277 prepend_attrs(&expr.attrs, expr.tokens.as_ref())
f9f354fc 278 }
e74abb32
XL
279 };
280
fc512014
XL
281 // Caches the stringification of 'good' `TokenStreams` which passed
282 // `tokenstream_probably_equal_for_proc_macro`. This allows us to avoid
283 // repeatedly stringifying and comparing the same `TokenStream` for deeply
284 // nested nonterminals.
285 //
286 // We cache by the strinification instead of the `TokenStream` to avoid
287 // needing to implement `Hash` for `TokenStream`. Note that it's possible to
288 // have two distinct `TokenStream`s that stringify to the same result
289 // (e.g. if they differ only in hygiene information). However, any
290 // information lost during the stringification process is also intentionally
291 // ignored by `tokenstream_probably_equal_for_proc_macro`, so it's fine
292 // that a single cache entry may 'map' to multiple distinct `TokenStream`s.
293 //
294 // This is a temporary hack to prevent compilation blowup on certain inputs.
295 // The entire pretty-print/retokenize process will be removed soon.
296 thread_local! {
297 static GOOD_TOKEN_CACHE: RefCell<FxHashSet<String>> = Default::default();
298 }
299
e74abb32 300 // FIXME(#43081): Avoid this pretty-print + reparse hack
29967ef6
XL
301 // Pretty-print the AST struct without inserting any parenthesis
302 // beyond those explicitly written by the user (e.g. `ExpnKind::Paren`).
303 // The resulting stream may have incorrect precedence, but it's only
304 // ever used for a comparison against the capture tokenstream.
305 let source = pprust::nonterminal_to_string_no_extra_parens(nt);
e74abb32 306 let filename = FileName::macro_expansion_source_code(&source);
fc512014 307 let reparsed_tokens = parse_stream_from_source_str(filename, source.clone(), sess, Some(span));
e74abb32
XL
308
309 // During early phases of the compiler the AST could get modified
310 // directly (e.g., attributes added or removed) and the internal cache
311 // of tokens my not be invalidated or updated. Consequently if the
312 // "lossless" token stream disagrees with our actual stringification
313 // (which has historically been much more battle-tested) then we go
314 // with the lossy stream anyway (losing span information).
315 //
316 // Note that the comparison isn't `==` here to avoid comparing spans,
317 // but it *also* is a "probable" equality which is a pretty weird
318 // definition. We mostly want to catch actual changes to the AST
319 // like a `#[cfg]` being processed or some weird `macro_rules!`
320 // expansion.
321 //
322 // What we *don't* want to catch is the fact that a user-defined
323 // literal like `0xf` is stringified as `15`, causing the cached token
324 // stream to not be literal `==` token-wise (ignoring spans) to the
325 // token stream we got from stringification.
326 //
327 // Instead the "probably equal" check here is "does each token
328 // recursively have the same discriminant?" We basically don't look at
329 // the token values here and assume that such fine grained token stream
330 // modifications, including adding/removing typically non-semantic
331 // tokens such as extra braces and commas, don't happen.
332 if let Some(tokens) = tokens {
fc512014
XL
333 if GOOD_TOKEN_CACHE.with(|cache| cache.borrow().contains(&source)) {
334 return tokens;
335 }
336
29967ef6
XL
337 // Compare with a non-relaxed delim match to start.
338 if tokenstream_probably_equal_for_proc_macro(&tokens, &reparsed_tokens, sess, false) {
fc512014 339 GOOD_TOKEN_CACHE.with(|cache| cache.borrow_mut().insert(source.clone()));
dfeec247 340 return tokens;
e74abb32 341 }
29967ef6
XL
342
343 // The check failed. This time, we pretty-print the AST struct with parenthesis
344 // inserted to preserve precedence. This may cause `None`-delimiters in the captured
345 // token stream to match up with inserted parenthesis in the reparsed stream.
346 let source_with_parens = pprust::nonterminal_to_string(nt);
347 let filename_with_parens = FileName::macro_expansion_source_code(&source_with_parens);
fc512014
XL
348
349 if GOOD_TOKEN_CACHE.with(|cache| cache.borrow().contains(&source_with_parens)) {
350 return tokens;
351 }
352
29967ef6
XL
353 let reparsed_tokens_with_parens = parse_stream_from_source_str(
354 filename_with_parens,
355 source_with_parens,
356 sess,
357 Some(span),
358 );
359
360 // Compare with a relaxed delim match - we want inserted parenthesis in the
361 // reparsed stream to match `None`-delimiters in the original stream.
362 if tokenstream_probably_equal_for_proc_macro(
363 &tokens,
364 &reparsed_tokens_with_parens,
365 sess,
366 true,
367 ) {
fc512014 368 GOOD_TOKEN_CACHE.with(|cache| cache.borrow_mut().insert(source.clone()));
29967ef6
XL
369 return tokens;
370 }
371
dfeec247
XL
372 info!(
373 "cached tokens found, but they're not \"probably equal\", \
374 going with stringified version"
375 );
29967ef6
XL
376 info!("cached tokens: {}", pprust::tts_to_string(&tokens));
377 info!("reparsed tokens: {}", pprust::tts_to_string(&reparsed_tokens_with_parens));
378
379 info!("cached tokens debug: {:?}", tokens);
380 info!("reparsed tokens debug: {:?}", reparsed_tokens_with_parens);
8faf50e0 381 }
1b1a35ee 382 reparsed_tokens
e74abb32 383}
8faf50e0 384
3dfed10e
XL
385// See comments in `Nonterminal::to_tokenstream` for why we care about
386// *probably* equal here rather than actual equality
387//
388// This is otherwise the same as `eq_unspanned`, only recursing with a
389// different method.
390pub fn tokenstream_probably_equal_for_proc_macro(
1b1a35ee
XL
391 tokens: &TokenStream,
392 reparsed_tokens: &TokenStream,
3dfed10e 393 sess: &ParseSess,
29967ef6 394 relaxed_delim_match: bool,
3dfed10e
XL
395) -> bool {
396 // When checking for `probably_eq`, we ignore certain tokens that aren't
397 // preserved in the AST. Because they are not preserved, the pretty
398 // printer arbitrarily adds or removes them when printing as token
399 // streams, making a comparison between a token stream generated from an
400 // AST and a token stream which was parsed into an AST more reliable.
401 fn semantic_tree(tree: &TokenTree) -> bool {
402 if let TokenTree::Token(token) = tree {
403 if let
404 // The pretty printer tends to add trailing commas to
405 // everything, and in particular, after struct fields.
406 | token::Comma
3dfed10e
XL
407 // The pretty printer collapses many semicolons into one.
408 | token::Semi
1b1a35ee
XL
409 // We don't preserve leading `|` tokens in patterns, so
410 // we ignore them entirely
411 | token::BinOp(token::BinOpToken::Or)
412 // We don't preserve trailing '+' tokens in trait bounds,
413 // so we ignore them entirely
414 | token::BinOp(token::BinOpToken::Plus)
3dfed10e
XL
415 // The pretty printer can turn `$crate` into `::crate_name`
416 | token::ModSep = token.kind {
417 return false;
418 }
419 }
420 true
421 }
422
423 // When comparing two `TokenStream`s, we ignore the `IsJoint` information.
424 //
425 // However, `rustc_parse::lexer::tokentrees::TokenStreamBuilder` will
426 // use `Token.glue` on adjacent tokens with the proper `IsJoint`.
427 // Since we are ignoreing `IsJoint`, a 'glued' token (e.g. `BinOp(Shr)`)
428 // and its 'split'/'unglued' compoenents (e.g. `Gt, Gt`) are equivalent
429 // when determining if two `TokenStream`s are 'probably equal'.
430 //
431 // Therefore, we use `break_two_token_op` to convert all tokens
432 // to the 'unglued' form (if it exists). This ensures that two
433 // `TokenStream`s which differ only in how their tokens are glued
434 // will be considered 'probably equal', which allows us to keep spans.
435 //
436 // This is important when the original `TokenStream` contained
437 // extra spaces (e.g. `f :: < Vec < _ > > ( ) ;'). These extra spaces
438 // will be omitted when we pretty-print, which can cause the original
439 // and reparsed `TokenStream`s to differ in the assignment of `IsJoint`,
440 // leading to some tokens being 'glued' together in one stream but not
441 // the other. See #68489 for more details.
442 fn break_tokens(tree: TokenTree) -> impl Iterator<Item = TokenTree> {
443 // In almost all cases, we should have either zero or one levels
444 // of 'unglueing'. However, in some unusual cases, we may need
445 // to iterate breaking tokens mutliple times. For example:
446 // '[BinOpEq(Shr)] => [Gt, Ge] -> [Gt, Gt, Eq]'
447 let mut token_trees: SmallVec<[_; 2]>;
fc512014 448 if let TokenTree::Token(token) = tree {
3dfed10e 449 let mut out = SmallVec::<[_; 2]>::new();
fc512014 450 out.push(token);
3dfed10e
XL
451 // Iterate to fixpoint:
452 // * We start off with 'out' containing our initial token, and `temp` empty
453 // * If we are able to break any tokens in `out`, then `out` will have
454 // at least one more element than 'temp', so we will try to break tokens
455 // again.
456 // * If we cannot break any tokens in 'out', we are done
457 loop {
458 let mut temp = SmallVec::<[_; 2]>::new();
459 let mut changed = false;
460
461 for token in out.into_iter() {
462 if let Some((first, second)) = token.kind.break_two_token_op() {
463 temp.push(Token::new(first, DUMMY_SP));
464 temp.push(Token::new(second, DUMMY_SP));
465 changed = true;
466 } else {
467 temp.push(token);
468 }
469 }
470 out = temp;
471 if !changed {
472 break;
473 }
474 }
475 token_trees = out.into_iter().map(TokenTree::Token).collect();
476 } else {
477 token_trees = SmallVec::new();
478 token_trees.push(tree);
479 }
480 token_trees.into_iter()
481 }
482
1b1a35ee
XL
483 fn expand_token(tree: TokenTree, sess: &ParseSess) -> impl Iterator<Item = TokenTree> {
484 // When checking tokenstreams for 'probable equality', we are comparing
485 // a captured (from parsing) `TokenStream` to a reparsed tokenstream.
486 // The reparsed Tokenstream will never have `None`-delimited groups,
487 // since they are only ever inserted as a result of macro expansion.
488 // Therefore, inserting a `None`-delimtied group here (when we
489 // convert a nested `Nonterminal` to a tokenstream) would cause
490 // a mismatch with the reparsed tokenstream.
491 //
492 // Note that we currently do not handle the case where the
493 // reparsed stream has a `Parenthesis`-delimited group
494 // inserted. This will cause a spurious mismatch:
495 // issue #75734 tracks resolving this.
496
497 let expanded: SmallVec<[_; 1]> =
498 if let TokenTree::Token(Token { kind: TokenKind::Interpolated(nt), span }) = &tree {
499 nt_to_tokenstream(nt, sess, *span)
500 .into_trees()
501 .flat_map(|t| expand_token(t, sess))
502 .collect()
503 } else {
504 // Filter before and after breaking tokens,
505 // since we may want to ignore both glued and unglued tokens.
506 std::iter::once(tree)
507 .filter(semantic_tree)
508 .flat_map(break_tokens)
509 .filter(semantic_tree)
510 .collect()
511 };
512 expanded.into_iter()
513 }
3dfed10e
XL
514
515 // Break tokens after we expand any nonterminals, so that we break tokens
516 // that are produced as a result of nonterminal expansion.
1b1a35ee
XL
517 let tokens = tokens.trees().flat_map(|t| expand_token(t, sess));
518 let reparsed_tokens = reparsed_tokens.trees().flat_map(|t| expand_token(t, sess));
519
29967ef6
XL
520 tokens.eq_by(reparsed_tokens, |t, rt| {
521 tokentree_probably_equal_for_proc_macro(&t, &rt, sess, relaxed_delim_match)
522 })
3dfed10e
XL
523}
524
525// See comments in `Nonterminal::to_tokenstream` for why we care about
526// *probably* equal here rather than actual equality
527//
528// This is otherwise the same as `eq_unspanned`, only recursing with a
529// different method.
530pub fn tokentree_probably_equal_for_proc_macro(
1b1a35ee
XL
531 token: &TokenTree,
532 reparsed_token: &TokenTree,
3dfed10e 533 sess: &ParseSess,
29967ef6 534 relaxed_delim_match: bool,
3dfed10e 535) -> bool {
1b1a35ee
XL
536 match (token, reparsed_token) {
537 (TokenTree::Token(token), TokenTree::Token(reparsed_token)) => {
538 token_probably_equal_for_proc_macro(token, reparsed_token)
3dfed10e 539 }
1b1a35ee
XL
540 (
541 TokenTree::Delimited(_, delim, tokens),
542 TokenTree::Delimited(_, reparsed_delim, reparsed_tokens),
29967ef6
XL
543 ) if delim == reparsed_delim => tokenstream_probably_equal_for_proc_macro(
544 tokens,
545 reparsed_tokens,
546 sess,
547 relaxed_delim_match,
548 ),
549 (TokenTree::Delimited(_, DelimToken::NoDelim, tokens), reparsed_token) => {
550 if relaxed_delim_match {
551 if let TokenTree::Delimited(_, DelimToken::Paren, reparsed_tokens) = reparsed_token
552 {
553 if tokenstream_probably_equal_for_proc_macro(
554 tokens,
555 reparsed_tokens,
556 sess,
557 relaxed_delim_match,
558 ) {
559 return true;
560 }
561 }
562 }
563 tokens.len() == 1
564 && tokentree_probably_equal_for_proc_macro(
565 &tokens.trees().next().unwrap(),
566 reparsed_token,
567 sess,
568 relaxed_delim_match,
569 )
3dfed10e
XL
570 }
571 _ => false,
572 }
573}
574
575// See comments in `Nonterminal::to_tokenstream` for why we care about
576// *probably* equal here rather than actual equality
577fn token_probably_equal_for_proc_macro(first: &Token, other: &Token) -> bool {
578 if mem::discriminant(&first.kind) != mem::discriminant(&other.kind) {
579 return false;
580 }
581 use rustc_ast::token::TokenKind::*;
582 match (&first.kind, &other.kind) {
583 (&Eq, &Eq)
584 | (&Lt, &Lt)
585 | (&Le, &Le)
586 | (&EqEq, &EqEq)
587 | (&Ne, &Ne)
588 | (&Ge, &Ge)
589 | (&Gt, &Gt)
590 | (&AndAnd, &AndAnd)
591 | (&OrOr, &OrOr)
592 | (&Not, &Not)
593 | (&Tilde, &Tilde)
594 | (&At, &At)
595 | (&Dot, &Dot)
596 | (&DotDot, &DotDot)
597 | (&DotDotDot, &DotDotDot)
598 | (&DotDotEq, &DotDotEq)
599 | (&Comma, &Comma)
600 | (&Semi, &Semi)
601 | (&Colon, &Colon)
602 | (&ModSep, &ModSep)
603 | (&RArrow, &RArrow)
604 | (&LArrow, &LArrow)
605 | (&FatArrow, &FatArrow)
606 | (&Pound, &Pound)
607 | (&Dollar, &Dollar)
608 | (&Question, &Question)
3dfed10e
XL
609 | (&Eof, &Eof) => true,
610
611 (&BinOp(a), &BinOp(b)) | (&BinOpEq(a), &BinOpEq(b)) => a == b,
612
613 (&OpenDelim(a), &OpenDelim(b)) | (&CloseDelim(a), &CloseDelim(b)) => a == b,
614
615 (&DocComment(a1, a2, a3), &DocComment(b1, b2, b3)) => a1 == b1 && a2 == b2 && a3 == b3,
616
3dfed10e
XL
617 (&Literal(a), &Literal(b)) => a == b,
618
619 (&Lifetime(a), &Lifetime(b)) => a == b,
620 (&Ident(a, b), &Ident(c, d)) => {
621 b == d && (a == c || a == kw::DollarCrate || c == kw::DollarCrate)
622 }
623
624 (&Interpolated(..), &Interpolated(..)) => panic!("Unexpanded Interpolated!"),
625
626 _ => panic!("forgot to add a token?"),
627 }
628}
629
e74abb32 630fn prepend_attrs(
e74abb32 631 attrs: &[ast::Attribute],
29967ef6 632 tokens: Option<&tokenstream::LazyTokenStream>,
e74abb32 633) -> Option<tokenstream::TokenStream> {
29967ef6 634 let tokens = tokens?.create_token_stream();
74b04a01 635 if attrs.is_empty() {
29967ef6 636 return Some(tokens);
e74abb32
XL
637 }
638 let mut builder = tokenstream::TokenStreamBuilder::new();
639 for attr in attrs {
29967ef6
XL
640 // FIXME: Correctly handle tokens for inner attributes.
641 // For now, we fall back to reparsing the original AST node
642 if attr.style == ast::AttrStyle::Inner {
643 return None;
e74abb32 644 }
29967ef6 645 builder.push(attr.tokens());
8faf50e0 646 }
29967ef6 647 builder.push(tokens);
e74abb32 648 Some(builder.build())
8faf50e0 649}