mirror of
				https://github.com/rust-lang/rust-analyzer.git
				synced 2025-10-30 19:49:36 +00:00 
			
		
		
		
	 97afb7bfba
			
		
	
	
		97afb7bfba
		
	
	
	
	
		
			
			More correctly, *also* per-token. Because as it turns out, while the top-level edition affects parsing (I think), the per-token edition affects escaping of identifiers/keywords.
		
			
				
	
	
		
			119 lines
		
	
	
	
		
			5.1 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			119 lines
		
	
	
	
		
			5.1 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| //! Convert macro-by-example tokens which are specific to macro expansion into a
 | |
| //! format that works for our parser.
 | |
| 
 | |
| use std::fmt;
 | |
| use std::hash::Hash;
 | |
| 
 | |
| use rustc_hash::FxHashMap;
 | |
| use span::{Edition, SpanData};
 | |
| use syntax::{SyntaxKind, SyntaxKind::*, T};
 | |
| 
 | |
| pub fn to_parser_input<Ctx: Copy + fmt::Debug + PartialEq + Eq + Hash>(
 | |
|     buffer: tt::TokenTreesView<'_, SpanData<Ctx>>,
 | |
|     span_to_edition: &mut dyn FnMut(Ctx) -> Edition,
 | |
| ) -> parser::Input {
 | |
|     let mut res = parser::Input::default();
 | |
| 
 | |
|     let mut current = buffer.cursor();
 | |
|     let mut syntax_context_to_edition_cache = FxHashMap::default();
 | |
| 
 | |
|     while !current.eof() {
 | |
|         let tt = current.token_tree();
 | |
| 
 | |
|         // Check if it is lifetime
 | |
|         if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) = tt {
 | |
|             if punct.char == '\'' {
 | |
|                 current.bump();
 | |
|                 match current.token_tree() {
 | |
|                     Some(tt::TokenTree::Leaf(tt::Leaf::Ident(_ident))) => {
 | |
|                         res.push(LIFETIME_IDENT);
 | |
|                         current.bump();
 | |
|                         continue;
 | |
|                     }
 | |
|                     _ => panic!("Next token must be ident"),
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         match tt {
 | |
|             Some(tt::TokenTree::Leaf(leaf)) => {
 | |
|                 match leaf {
 | |
|                     tt::Leaf::Literal(lit) => {
 | |
|                         let kind = match lit.kind {
 | |
|                             tt::LitKind::Byte => SyntaxKind::BYTE,
 | |
|                             tt::LitKind::Char => SyntaxKind::CHAR,
 | |
|                             tt::LitKind::Integer => SyntaxKind::INT_NUMBER,
 | |
|                             tt::LitKind::Float => SyntaxKind::FLOAT_NUMBER,
 | |
|                             tt::LitKind::Str | tt::LitKind::StrRaw(_) => SyntaxKind::STRING,
 | |
|                             tt::LitKind::ByteStr | tt::LitKind::ByteStrRaw(_) => {
 | |
|                                 SyntaxKind::BYTE_STRING
 | |
|                             }
 | |
|                             tt::LitKind::CStr | tt::LitKind::CStrRaw(_) => SyntaxKind::C_STRING,
 | |
|                             tt::LitKind::Err(_) => SyntaxKind::ERROR,
 | |
|                         };
 | |
|                         res.push(kind);
 | |
| 
 | |
|                         if kind == FLOAT_NUMBER && !lit.symbol.as_str().ends_with('.') {
 | |
|                             // Tag the token as joint if it is float with a fractional part
 | |
|                             // we use this jointness to inform the parser about what token split
 | |
|                             // event to emit when we encounter a float literal in a field access
 | |
|                             res.was_joint();
 | |
|                         }
 | |
|                     }
 | |
|                     tt::Leaf::Ident(ident) => {
 | |
|                         let edition = *syntax_context_to_edition_cache
 | |
|                             .entry(ident.span.ctx)
 | |
|                             .or_insert_with(|| span_to_edition(ident.span.ctx));
 | |
|                         match ident.sym.as_str() {
 | |
|                             "_" => res.push(T![_]),
 | |
|                             i if i.starts_with('\'') => res.push(LIFETIME_IDENT),
 | |
|                             _ if ident.is_raw.yes() => res.push(IDENT),
 | |
|                             text => match SyntaxKind::from_keyword(text, edition) {
 | |
|                                 Some(kind) => res.push(kind),
 | |
|                                 None => {
 | |
|                                     let contextual_keyword =
 | |
|                                         SyntaxKind::from_contextual_keyword(text, edition)
 | |
|                                             .unwrap_or(SyntaxKind::IDENT);
 | |
|                                     res.push_ident(contextual_keyword);
 | |
|                                 }
 | |
|                             },
 | |
|                         }
 | |
|                     }
 | |
|                     tt::Leaf::Punct(punct) => {
 | |
|                         let kind = SyntaxKind::from_char(punct.char)
 | |
|                             .unwrap_or_else(|| panic!("{punct:#?} is not a valid punct"));
 | |
|                         res.push(kind);
 | |
|                         if punct.spacing == tt::Spacing::Joint {
 | |
|                             res.was_joint();
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|                 current.bump();
 | |
|             }
 | |
|             Some(tt::TokenTree::Subtree(subtree)) => {
 | |
|                 if let Some(kind) = match subtree.delimiter.kind {
 | |
|                     tt::DelimiterKind::Parenthesis => Some(T!['(']),
 | |
|                     tt::DelimiterKind::Brace => Some(T!['{']),
 | |
|                     tt::DelimiterKind::Bracket => Some(T!['[']),
 | |
|                     tt::DelimiterKind::Invisible => None,
 | |
|                 } {
 | |
|                     res.push(kind);
 | |
|                 }
 | |
|                 current.bump();
 | |
|             }
 | |
|             None => {
 | |
|                 let subtree = current.end();
 | |
|                 if let Some(kind) = match subtree.delimiter.kind {
 | |
|                     tt::DelimiterKind::Parenthesis => Some(T![')']),
 | |
|                     tt::DelimiterKind::Brace => Some(T!['}']),
 | |
|                     tt::DelimiterKind::Bracket => Some(T![']']),
 | |
|                     tt::DelimiterKind::Invisible => None,
 | |
|                 } {
 | |
|                     res.push(kind);
 | |
|                 }
 | |
|             }
 | |
|         };
 | |
|     }
 | |
| 
 | |
|     res
 | |
| }
 |