Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir_expand/src/fixup.rs')
| -rw-r--r-- | crates/hir_expand/src/fixup.rs | 53 |
1 files changed, 42 insertions, 11 deletions
diff --git a/crates/hir_expand/src/fixup.rs b/crates/hir_expand/src/fixup.rs index f2d43f4d69..36425c8078 100644 --- a/crates/hir_expand/src/fixup.rs +++ b/crates/hir_expand/src/fixup.rs @@ -1,3 +1,7 @@ +//! To make attribute macros work reliably when typing, we need to take care to +//! fix up syntax errors in the code we're passing to them. +use std::mem; + use mbe::{SyntheticToken, SyntheticTokenId, TokenMap}; use rustc_hash::FxHashMap; use syntax::{ @@ -6,16 +10,22 @@ use syntax::{ }; use tt::Subtree; +/// The result of calculating fixes for a syntax node -- a bunch of changes +/// (appending to and replacing nodes), the information that is needed to +/// reverse those changes afterwards, and a token map. #[derive(Debug)] pub struct SyntaxFixups { pub append: FxHashMap<SyntaxNode, Vec<SyntheticToken>>, pub replace: FxHashMap<SyntaxNode, Vec<SyntheticToken>>, - pub map: SyntaxFixupMap, + pub undo_info: SyntaxFixupUndoInfo, + pub token_map: TokenMap, + pub next_id: u32, } +/// This is the information needed to reverse the fixups. #[derive(Debug, PartialEq, Eq)] -pub struct SyntaxFixupMap { - original: Vec<(Subtree, TokenMap)>, +pub struct SyntaxFixupUndoInfo { + original: Vec<Subtree>, } const EMPTY_ID: SyntheticTokenId = SyntheticTokenId(!0); @@ -25,15 +35,26 @@ pub fn fixup_syntax(node: &SyntaxNode) -> SyntaxFixups { let mut replace = FxHashMap::default(); let mut preorder = node.preorder(); let mut original = Vec::new(); + let mut token_map = TokenMap::default(); + let mut next_id = 0; while let Some(event) = preorder.next() { let node = match event { syntax::WalkEvent::Enter(node) => node, syntax::WalkEvent::Leave(_) => continue, }; + if can_handle_error(&node) && has_error_to_handle(&node) { // the node contains an error node, we have to completely replace it by something valid - let original_tree = mbe::syntax_node_to_token_tree(&node); - // TODO handle token ids / token map + let (original_tree, new_tmap, new_next_id) = + mbe::syntax_node_to_token_tree_with_modifications( + &node, + mem::take(&mut token_map), + next_id, + Default::default(), + Default::default(), + ); + token_map = new_tmap; + next_id = new_next_id; let idx = original.len() as u32; original.push(original_tree); let replacement = SyntheticToken { @@ -46,6 +67,8 @@ pub fn fixup_syntax(node: &SyntaxNode) -> SyntaxFixups { preorder.skip_subtree(); continue; } + + // In some other situations, we can fix things by just appending some tokens. let end_range = TextRange::empty(node.text_range().end()); match_ast! { match node { @@ -78,7 +101,13 @@ pub fn fixup_syntax(node: &SyntaxNode) -> SyntaxFixups { } } } - SyntaxFixups { append, replace, map: SyntaxFixupMap { original } } + SyntaxFixups { + append, + replace, + token_map, + next_id, + undo_info: SyntaxFixupUndoInfo { original }, + } } fn has_error(node: &SyntaxNode) -> bool { @@ -93,7 +122,7 @@ fn has_error_to_handle(node: &SyntaxNode) -> bool { has_error(node) || node.children().any(|c| !can_handle_error(&c) && has_error_to_handle(&c)) } -pub fn reverse_fixups(tt: &mut Subtree, token_map: &TokenMap, fixup_map: &SyntaxFixupMap) { +pub fn reverse_fixups(tt: &mut Subtree, token_map: &TokenMap, undo_info: &SyntaxFixupUndoInfo) { tt.token_trees.retain(|tt| match tt { tt::TokenTree::Leaf(leaf) => { token_map.synthetic_token_id(leaf.id()).is_none() @@ -102,10 +131,10 @@ pub fn reverse_fixups(tt: &mut Subtree, token_map: &TokenMap, fixup_map: &Syntax _ => true, }); tt.token_trees.iter_mut().for_each(|tt| match tt { - tt::TokenTree::Subtree(tt) => reverse_fixups(tt, token_map, fixup_map), + tt::TokenTree::Subtree(tt) => reverse_fixups(tt, token_map, undo_info), tt::TokenTree::Leaf(leaf) => { if let Some(id) = token_map.synthetic_token_id(leaf.id()) { - let (original, _original_tmap) = &fixup_map.original[id.0 as usize]; + let original = &undo_info.original[id.0 as usize]; *tt = tt::TokenTree::Subtree(original.clone()); } } @@ -123,8 +152,10 @@ mod tests { let parsed = syntax::SourceFile::parse(ra_fixture); eprintln!("parse: {:#?}", parsed.syntax_node()); let fixups = super::fixup_syntax(&parsed.syntax_node()); - let (mut tt, tmap) = mbe::syntax_node_to_token_tree_with_modifications( + let (mut tt, tmap, _) = mbe::syntax_node_to_token_tree_with_modifications( &parsed.syntax_node(), + fixups.token_map, + fixups.next_id, fixups.replace, fixups.append, ); @@ -144,7 +175,7 @@ mod tests { parse.syntax_node() ); - reverse_fixups(&mut tt, &tmap, &fixups.map); + reverse_fixups(&mut tt, &tmap, &fixups.undo_info); // the fixed-up + reversed version should be equivalent to the original input // (but token IDs don't matter) |