diff options
| author | Lukas Wirth <lukastw97@gmail.com> | 2023-11-29 15:48:40 +0100 |
|---|---|---|
| committer | Lukas Wirth <lukastw97@gmail.com> | 2023-11-29 16:00:39 +0100 |
| commit | f48fa0c6cba2d442523daced9db09576e4b11e78 (patch) | |
| tree | 855a0c6c8fa9183e10fb182b83d55479fdca70de | |
| parent | 7a8c4c001ba82e360d77d17a5815f2c6fd50e3e5 (diff) | |
| download | rust-f48fa0c6cba2d442523daced9db09576e4b11e78.tar.gz rust-f48fa0c6cba2d442523daced9db09576e4b11e78.zip | |
Re-implement syntax fixups
| -rw-r--r-- | crates/base-db/src/span.rs | 2 | ||||
| -rw-r--r-- | crates/hir-expand/src/db.rs | 103 | ||||
| -rw-r--r-- | crates/hir-expand/src/fixup.rs | 206 | ||||
| -rw-r--r-- | crates/hir-expand/src/lib.rs | 13 | ||||
| -rw-r--r-- | crates/ide-diagnostics/src/handlers/unresolved_module.rs | 2 | ||||
| -rw-r--r-- | crates/mbe/src/lib.rs | 2 | ||||
| -rw-r--r-- | crates/mbe/src/syntax_bridge.rs | 344 |
7 files changed, 408 insertions, 264 deletions
diff --git a/crates/base-db/src/span.rs b/crates/base-db/src/span.rs index 420c669641c..dbccbb382a7 100644 --- a/crates/base-db/src/span.rs +++ b/crates/base-db/src/span.rs @@ -34,8 +34,6 @@ impl SyntaxContextId { // we need a special value that behaves as the current context. pub const SELF_REF: Self = SyntaxContextId(unsafe { InternId::new_unchecked(InternId::MAX - 1) }); - // Used for syntax fixups - pub const FAKE: Self = SyntaxContextId(unsafe { InternId::new_unchecked(InternId::MAX - 2) }); pub fn is_root(self) -> bool { self == Self::ROOT diff --git a/crates/hir-expand/src/db.rs b/crates/hir-expand/src/db.rs index 00a7b0cb3fe..0135d97f1cd 100644 --- a/crates/hir-expand/src/db.rs +++ b/crates/hir-expand/src/db.rs @@ -9,6 +9,7 @@ use base_db::{ use either::Either; use limit::Limit; use mbe::{syntax_node_to_token_tree, ValueResult}; +use rustc_hash::FxHashSet; use syntax::{ ast::{self, HasAttrs, HasDocComments}, AstNode, Parse, SyntaxError, SyntaxNode, SyntaxToken, T, @@ -20,6 +21,7 @@ use crate::{ attrs::RawAttrs, builtin_attr_macro::pseudo_derive_attr_expansion, builtin_fn_macro::EagerExpander, + fixup::{self, SyntaxFixupUndoInfo}, hygiene::{self, SyntaxContextData, Transparency}, span::{RealSpanMap, SpanMap, SpanMapRef}, tt, AstId, BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallInfo, @@ -135,7 +137,7 @@ pub trait ExpandDatabase: SourceDatabase { fn macro_arg( &self, id: MacroCallId, - ) -> ValueResult<Option<Arc<tt::Subtree>>, Arc<Box<[SyntaxError]>>>; + ) -> ValueResult<Option<(Arc<tt::Subtree>, SyntaxFixupUndoInfo)>, Arc<Box<[SyntaxError]>>>; /// Fetches the expander for this macro. #[salsa::transparent] fn macro_expander(&self, id: MacroDefId) -> TokenExpander; @@ -189,15 +191,33 @@ pub fn expand_speculative( ) -> Option<(SyntaxNode, SyntaxToken)> { let loc = db.lookup_intern_macro_call(actual_macro_call); - // Build the subtree and token mapping for the speculative args - let _censor = censor_for_macro_input(&loc, speculative_args); let span_map = RealSpanMap::absolute(SpanAnchor::DUMMY.file_id); let span_map = SpanMapRef::RealSpanMap(&span_map); - let mut tt = mbe::syntax_node_to_token_tree( - speculative_args, - // we don't leak these spans into any query so its fine to make them absolute - span_map, - ); + + // Build the subtree and token mapping for the speculative args + let (mut tt, undo_info) = match loc.kind { + MacroCallKind::FnLike { .. } => { + (mbe::syntax_node_to_token_tree(speculative_args, span_map), SyntaxFixupUndoInfo::NONE) + } + MacroCallKind::Derive { .. } | MacroCallKind::Attr { .. } => { + let censor = censor_for_macro_input(&loc, speculative_args); + let mut fixups = fixup::fixup_syntax(span_map, speculative_args); + fixups.append.retain(|it, _| match it { + syntax::NodeOrToken::Node(it) => !censor.contains(it), + syntax::NodeOrToken::Token(_) => true, + }); + fixups.remove.extend(censor); + ( + mbe::syntax_node_to_token_tree_modified( + speculative_args, + span_map, + fixups.append, + fixups.remove, + ), + fixups.undo_info, + ) + } + }; let attr_arg = match loc.kind { MacroCallKind::Attr { invoc_attr_index, .. } => { @@ -227,7 +247,7 @@ pub fn expand_speculative( // Do the actual expansion, we need to directly expand the proc macro due to the attribute args // Otherwise the expand query will fetch the non speculative attribute args and pass those instead. - let speculative_expansion = match loc.def.kind { + let mut speculative_expansion = match loc.def.kind { MacroDefKind::ProcMacro(expander, ..) => { tt.delimiter = tt::Delimiter::UNSPECIFIED; let call_site = loc.span(db); @@ -261,6 +281,7 @@ pub fn expand_speculative( }; let expand_to = macro_expand_to(db, actual_macro_call); + fixup::reverse_fixups(&mut speculative_expansion.value, &undo_info); let (node, rev_tmap) = token_tree_to_syntax_node(&speculative_expansion.value, expand_to); let syntax_node = node.syntax_node(); @@ -347,7 +368,9 @@ fn parse_with_map(db: &dyn ExpandDatabase, file_id: HirFileId) -> (Parse<SyntaxN fn macro_arg( db: &dyn ExpandDatabase, id: MacroCallId, -) -> ValueResult<Option<Arc<tt::Subtree>>, Arc<Box<[SyntaxError]>>> { + // FIXME: consider the following by putting fixup info into eager call info args + // ) -> ValueResult<Option<Arc<(tt::Subtree, SyntaxFixupUndoInfo)>>, Arc<Box<[SyntaxError]>>> { +) -> ValueResult<Option<(Arc<tt::Subtree>, SyntaxFixupUndoInfo)>, Arc<Box<[SyntaxError]>>> { let mismatched_delimiters = |arg: &SyntaxNode| { let first = arg.first_child_or_token().map_or(T![.], |it| it.kind()); let last = arg.last_child_or_token().map_or(T![.], |it| it.kind()); @@ -375,7 +398,7 @@ fn macro_arg( .then(|| loc.eager.as_deref()) .flatten() { - ValueResult::ok(Some(arg.clone())) + ValueResult::ok(Some((arg.clone(), SyntaxFixupUndoInfo::NONE))) } else { let (parse, map) = parse_with_map(db, loc.kind.file_id()); let root = parse.syntax_node(); @@ -404,22 +427,27 @@ fn macro_arg( } MacroCallKind::Attr { ast_id, .. } => ast_id.to_ptr(db).to_node(&root).syntax().clone(), }; - let censor = censor_for_macro_input(&loc, &syntax); - let mut tt = match loc.kind { + let (mut tt, undo_info) = match loc.kind { MacroCallKind::FnLike { .. } => { - mbe::syntax_node_to_token_tree_censored(&syntax, map.as_ref(), censor) + (mbe::syntax_node_to_token_tree(&syntax, map.as_ref()), SyntaxFixupUndoInfo::NONE) } MacroCallKind::Derive { .. } | MacroCallKind::Attr { .. } => { - // let mut fixups = crate::fixup::fixup_syntax(&syntax); - // fixups.replace.extend(censor.into_iter().map(|node| (node.into(), Vec::new()))); - // let (mut tt, tmap, _) = mbe::syntax_node_to_token_tree_with_modifications( - // &node, - // fixups.token_map, - // fixups.next_id, - // fixups.replace, - // fixups.append, - // ); - mbe::syntax_node_to_token_tree_censored(&syntax, map.as_ref(), censor) + let censor = censor_for_macro_input(&loc, &syntax); + let mut fixups = fixup::fixup_syntax(map.as_ref(), &syntax); + fixups.append.retain(|it, _| match it { + syntax::NodeOrToken::Node(it) => !censor.contains(it), + syntax::NodeOrToken::Token(_) => true, + }); + fixups.remove.extend(censor); + ( + mbe::syntax_node_to_token_tree_modified( + &syntax, + map, + fixups.append, + fixups.remove, + ), + fixups.undo_info, + ) } }; @@ -430,15 +458,15 @@ fn macro_arg( if matches!(loc.def.kind, MacroDefKind::BuiltInEager(..)) { match parse.errors() { - [] => ValueResult::ok(Some(Arc::new(tt))), + [] => ValueResult::ok(Some((Arc::new(tt), undo_info))), errors => ValueResult::new( - Some(Arc::new(tt)), + Some((Arc::new(tt), undo_info)), // Box::<[_]>::from(res.errors()), not stable yet Arc::new(errors.to_vec().into_boxed_slice()), ), } } else { - ValueResult::ok(Some(Arc::new(tt))) + ValueResult::ok(Some((Arc::new(tt), undo_info))) } } } @@ -447,7 +475,7 @@ fn macro_arg( /// Certain macro calls expect some nodes in the input to be preprocessed away, namely: /// - derives expect all `#[derive(..)]` invocations up to the currently invoked one to be stripped /// - attributes expect the invoking attribute to be stripped -fn censor_for_macro_input(loc: &MacroCallLoc, node: &SyntaxNode) -> Vec<SyntaxNode> { +fn censor_for_macro_input(loc: &MacroCallLoc, node: &SyntaxNode) -> FxHashSet<SyntaxNode> { // FIXME: handle `cfg_attr` (|| { let censor = match loc.kind { @@ -574,13 +602,13 @@ fn macro_expand( let MacroCallKind::Derive { ast_id, .. } = loc.kind else { unreachable!() }; let node = ast_id.to_ptr(db).to_node(&root); - // FIXME: we might need to remove the spans from the input to the derive macro here + // FIXME: Use censoring let _censor = censor_for_macro_input(&loc, node.syntax()); expander.expand(db, macro_call_id, &node, map.as_ref()) } _ => { let ValueResult { value, err } = db.macro_arg(macro_call_id); - let Some(macro_arg) = value else { + let Some((macro_arg, undo_info)) = value else { return ExpandResult { value: Arc::new(tt::Subtree { delimiter: tt::Delimiter::UNSPECIFIED, @@ -608,7 +636,7 @@ fn macro_expand( // As such we just return the input subtree here. MacroDefKind::BuiltInEager(..) if loc.eager.is_none() => { return ExpandResult { - value: Arc::new(arg.clone()), + value: macro_arg.clone(), err: err.map(|err| { let mut buf = String::new(); for err in &**err { @@ -624,7 +652,11 @@ fn macro_expand( MacroDefKind::BuiltInEager(it, _) => { it.expand(db, macro_call_id, &arg).map_err(Into::into) } - MacroDefKind::BuiltInAttr(it, _) => it.expand(db, macro_call_id, &arg), + MacroDefKind::BuiltInAttr(it, _) => { + let mut res = it.expand(db, macro_call_id, &arg); + fixup::reverse_fixups(&mut res.value, &undo_info); + res + } _ => unreachable!(), } } @@ -647,9 +679,8 @@ fn macro_expand( } fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<Arc<tt::Subtree>> { - // FIXME: Syntax fix ups let loc = db.lookup_intern_macro_call(id); - let Some(macro_arg) = db.macro_arg(id).value else { + let Some((macro_arg, undo_info)) = db.macro_arg(id).value else { return ExpandResult { value: Arc::new(tt::Subtree { delimiter: tt::Delimiter::UNSPECIFIED, @@ -672,7 +703,7 @@ fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<A }; let call_site = loc.span(db); - let ExpandResult { value: tt, err } = expander.expand( + let ExpandResult { value: mut tt, err } = expander.expand( db, loc.def.krate, loc.krate, @@ -690,6 +721,8 @@ fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<A return value; } + fixup::reverse_fixups(&mut tt, &undo_info); + ExpandResult { value: Arc::new(tt), err } } diff --git a/crates/hir-expand/src/fixup.rs b/crates/hir-expand/src/fixup.rs index 8fa7d576295..77ddf7a48c3 100644 --- a/crates/hir-expand/src/fixup.rs +++ b/crates/hir-expand/src/fixup.rs @@ -1,22 +1,24 @@ //! 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 base_db::{ - span::{ErasedFileAstId, SpanAnchor, SpanData, SyntaxContextId}, + span::{ErasedFileAstId, SpanAnchor, SpanData}, FileId, }; use la_arena::RawIdx; -use mbe::TokenMap; -use rustc_hash::FxHashMap; +use rustc_hash::{FxHashMap, FxHashSet}; use smallvec::SmallVec; use syntax::{ ast::{self, AstNode, HasLoopBody}, match_ast, SyntaxElement, SyntaxKind, SyntaxNode, TextRange, TextSize, }; +use triomphe::Arc; use tt::Spacing; -use crate::tt::{Ident, Leaf, Punct, Subtree}; +use crate::{ + span::SpanMapRef, + tt::{Ident, Leaf, Punct, 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 @@ -24,14 +26,19 @@ use crate::tt::{Ident, Leaf, Punct, Subtree}; #[derive(Debug, Default)] pub(crate) struct SyntaxFixups { pub(crate) append: FxHashMap<SyntaxElement, Vec<Leaf>>, - pub(crate) replace: FxHashMap<SyntaxElement, Vec<()>>, + pub(crate) remove: FxHashSet<SyntaxNode>, pub(crate) undo_info: SyntaxFixupUndoInfo, } /// This is the information needed to reverse the fixups. -#[derive(Debug, Default, PartialEq, Eq)] +#[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct SyntaxFixupUndoInfo { - original: Box<[Subtree]>, + // FIXME: ThinArc<[Subtree]> + original: Option<Arc<Box<[Subtree]>>>, +} + +impl SyntaxFixupUndoInfo { + pub(crate) const NONE: Self = SyntaxFixupUndoInfo { original: None }; } // censoring -> just don't convert the node @@ -39,47 +46,45 @@ pub struct SyntaxFixupUndoInfo { // append -> insert a fake node, here we need to assemble some dummy span that we can figure out how // to remove later -pub(crate) fn fixup_syntax(node: &SyntaxNode) -> SyntaxFixups { +pub(crate) fn fixup_syntax(span_map: SpanMapRef<'_>, node: &SyntaxNode) -> SyntaxFixups { let mut append = FxHashMap::<SyntaxElement, _>::default(); - let mut replace = FxHashMap::<SyntaxElement, _>::default(); + let mut remove = FxHashSet::<SyntaxNode>::default(); let mut preorder = node.preorder(); let mut original = Vec::new(); let dummy_range = TextRange::empty(TextSize::new(0)); + // we use a file id of `FileId(!0)` to signal a fake node, and the text range's start offset as + // the index into the replacement vec but only if the end points to !0 let dummy_anchor = - SpanAnchor { file_id: FileId(!0), ast_id: ErasedFileAstId::from_raw(RawIdx::from(0)) }; - let fake_span = - SpanData { range: dummy_range, anchor: dummy_anchor, ctx: SyntaxContextId::FAKE }; + SpanAnchor { file_id: FileId(!0), ast_id: ErasedFileAstId::from_raw(RawIdx::from(!0)) }; + let fake_span = |range| SpanData { + range: dummy_range, + anchor: dummy_anchor, + ctx: span_map.span_for_range(range).ctx, + }; while let Some(event) = preorder.next() { let syntax::WalkEvent::Enter(node) = event else { continue }; - /* + let node_range = node.text_range(); if can_handle_error(&node) && has_error_to_handle(&node) { + remove.insert(node.clone().into()); // the node contains an error node, we have to completely replace it by something valid - 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 original_tree = mbe::syntax_node_to_token_tree(&node, span_map); let idx = original.len() as u32; original.push(original_tree); - let replacement = SyntheticToken { - kind: SyntaxKind::IDENT, + let replacement = Leaf::Ident(Ident { text: "__ra_fixup".into(), - range: node.text_range(), - id: SyntheticTokenId(idx), - }; - replace.insert(node.clone().into(), vec![replacement]); + span: SpanData { + range: TextRange::new(TextSize::new(idx), TextSize::new(!0)), + anchor: dummy_anchor, + ctx: span_map.span_for_range(node_range).ctx, + }, + }); + append.insert(node.clone().into(), vec![replacement]); 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 { ast::FieldExpr(it) => { @@ -88,7 +93,7 @@ pub(crate) fn fixup_syntax(node: &SyntaxNode) -> SyntaxFixups { append.insert(node.clone().into(), vec![ Leaf::Ident(Ident { text: "__ra_fixup".into(), - span: fake_span + span: fake_span(node_range), }), ]); } @@ -99,7 +104,7 @@ pub(crate) fn fixup_syntax(node: &SyntaxNode) -> SyntaxFixups { Leaf::Punct(Punct { char: ';', spacing: Spacing::Alone, - span: fake_span + span: fake_span(node_range), }), ]); } @@ -110,7 +115,7 @@ pub(crate) fn fixup_syntax(node: &SyntaxNode) -> SyntaxFixups { Leaf::Punct(Punct { char: ';', spacing: Spacing::Alone, - span: fake_span + span: fake_span(node_range) }), ]); } @@ -125,7 +130,7 @@ pub(crate) fn fixup_syntax(node: &SyntaxNode) -> SyntaxFixups { append.insert(if_token.into(), vec![ Leaf::Ident(Ident { text: "__ra_fixup".into(), - span: fake_span + span: fake_span(node_range) }), ]); } @@ -135,12 +140,12 @@ pub(crate) fn fixup_syntax(node: &SyntaxNode) -> SyntaxFixups { Leaf::Punct(Punct { char: '{', spacing: Spacing::Alone, - span: fake_span + span: fake_span(node_range) }), Leaf::Punct(Punct { char: '}', spacing: Spacing::Alone, - span: fake_span + span: fake_span(node_range) }), ]); } @@ -155,7 +160,7 @@ pub(crate) fn fixup_syntax(node: &SyntaxNode) -> SyntaxFixups { append.insert(while_token.into(), vec![ Leaf::Ident(Ident { text: "__ra_fixup".into(), - span: fake_span + span: fake_span(node_range) }), ]); } @@ -165,12 +170,12 @@ pub(crate) fn fixup_syntax(node: &SyntaxNode) -> SyntaxFixups { Leaf::Punct(Punct { char: '{', spacing: Spacing::Alone, - span: fake_span + span: fake_span(node_range) }), Leaf::Punct(Punct { char: '}', spacing: Spacing::Alone, - span: fake_span + span: fake_span(node_range) }), ]); } @@ -182,12 +187,12 @@ pub(crate) fn fixup_syntax(node: &SyntaxNode) -> SyntaxFixups { Leaf::Punct(Punct { char: '{', spacing: Spacing::Alone, - span: fake_span + span: fake_span(node_range) }), Leaf::Punct(Punct { char: '}', spacing: Spacing::Alone, - span: fake_span + span: fake_span(node_range) }), ]); } @@ -202,7 +207,7 @@ pub(crate) fn fixup_syntax(node: &SyntaxNode) -> SyntaxFixups { append.insert(match_token.into(), vec![ Leaf::Ident(Ident { text: "__ra_fixup".into(), - span: fake_span + span: fake_span(node_range) }), ]); } @@ -213,12 +218,12 @@ pub(crate) fn fixup_syntax(node: &SyntaxNode) -> SyntaxFixups { Leaf::Punct(Punct { char: '{', spacing: Spacing::Alone, - span: fake_span + span: fake_span(node_range) }), Leaf::Punct(Punct { char: '}', spacing: Spacing::Alone, - span: fake_span + span: fake_span(node_range) }), ]); } @@ -236,7 +241,7 @@ pub(crate) fn fixup_syntax(node: &SyntaxNode) -> SyntaxFixups { ].map(|text| Leaf::Ident(Ident { text: text.into(), - span: fake_span + span: fake_span(node_range) }), ); @@ -253,12 +258,12 @@ pub(crate) fn fixup_syntax(node: &SyntaxNode) -> SyntaxFixups { Leaf::Punct(Punct { char: '{', spacing: Spacing::Alone, - span: fake_span + span: fake_span(node_range) }), Leaf::Punct(Punct { char: '}', spacing: Spacing::Alone, - span: fake_span + span: fake_span(node_range) }), ]); } @@ -267,10 +272,13 @@ pub(crate) fn fixup_syntax(node: &SyntaxNode) -> SyntaxFixups { } } } + let needs_fixups = !append.is_empty() || !original.is_empty(); SyntaxFixups { append, - replace, - undo_info: SyntaxFixupUndoInfo { original: original.into_boxed_slice() }, + remove, + undo_info: SyntaxFixupUndoInfo { + original: needs_fixups.then(|| Arc::new(original.into_boxed_slice())), + }, } } @@ -287,42 +295,55 @@ fn has_error_to_handle(node: &SyntaxNode) -> bool { } pub(crate) fn reverse_fixups(tt: &mut Subtree, undo_info: &SyntaxFixupUndoInfo) { + let Some(undo_info) = undo_info.original.as_deref() else { return }; + let undo_info = &**undo_info; + reverse_fixups_(tt, undo_info); +} + +fn reverse_fixups_(tt: &mut Subtree, undo_info: &[Subtree]) { let tts = std::mem::take(&mut tt.token_trees); tt.token_trees = tts .into_iter() // delete all fake nodes .filter(|tt| match tt { - tt::TokenTree::Leaf(leaf) => leaf.span().ctx != SyntaxContextId::FAKE, - tt::TokenTree::Subtree(st) => st.delimiter.open.ctx != SyntaxContextId::FAKE, + tt::TokenTree::Leaf(leaf) => { + let span = leaf.span(); + span.anchor.file_id != FileId(!0) || span.range.end() == TextSize::new(!0) + } + tt::TokenTree::Subtree(_) => true, + }) + .flat_map(|tt| match tt { + tt::TokenTree::Subtree(mut tt) => { + reverse_fixups_(&mut tt, undo_info); + SmallVec::from_const([tt.into()]) + } + tt::TokenTree::Leaf(leaf) => { + if leaf.span().anchor.file_id == FileId(!0) { + let original = undo_info[u32::from(leaf.span().range.start()) as usize].clone(); + if original.delimiter.kind == tt::DelimiterKind::Invisible { + original.token_trees.into() + } else { + SmallVec::from_const([original.into()]) + } + } else { + SmallVec::from_const([leaf.into()]) + } + } }) - // .flat_map(|tt| match tt { - // tt::TokenTree::Subtree(mut tt) => { - // reverse_fixups(&mut tt, undo_info); - // SmallVec::from_const([tt.into()]) - // } - // tt::TokenTree::Leaf(leaf) => { - // if let Some(id) = leaf.span().anchor { - // let original = undo_info.original[id.0 as usize].clone(); - // if original.delimiter.kind == tt::DelimiterKind::Invisible { - // original.token_trees.into() - // } else { - // SmallVec::from_const([original.into()]) - // } - // } else { - // SmallVec::from_const([leaf.into()]) - // } - // } - // }) .collect(); } #[cfg(test)] mod tests { + use base_db::FileId; use expect_test::{expect, Expect}; + use triomphe::Arc; - use crate::tt; - - use super::reverse_fixups; + use crate::{ + fixup::reverse_fixups, + span::{RealSpanMap, SpanMap}, + tt, + }; // The following three functions are only meant to check partial structural equivalence of // `TokenTree`s, see the last assertion in `check()`. @@ -352,13 +373,13 @@ mod tests { #[track_caller] fn check(ra_fixture: &str, mut expect: Expect) { let parsed = syntax::SourceFile::parse(ra_fixture); - let fixups = super::fixup_syntax(&parsed.syntax_node()); - let (mut tt, tmap, _) = mbe::syntax_node_to_token_tree_with_modifications( + let span_map = SpanMap::RealSpanMap(Arc::new(RealSpanMap::absolute(FileId(0)))); + let fixups = super::fixup_syntax(span_map.as_ref(), &parsed.syntax_node()); + let mut tt = mbe::syntax_node_to_token_tree_modified( &parsed.syntax_node(), - fixups.token_map, - fixups.next_id, - fixups.replace, + span_map.as_ref(), fixups.append, + fixups.remove, ); let actual = format!("{tt}\n"); @@ -374,14 +395,15 @@ mod tests { parse.syntax_node() ); - reverse_fixups(&mut tt, &tmap, &fixups.undo_info); + reverse_fixups(&mut tt, &fixups.undo_info); // the fixed-up + reversed version should be equivalent to the original input // modulo token IDs and `Punct`s' spacing. - let (original_as_tt, _) = mbe::syntax_node_to_token_tree(&parsed.syntax_node()); + let original_as_tt = + mbe::syntax_node_to_token_tree(&parsed.syntax_node(), span_map.as_ref()); assert!( check_subtree_eq(&tt, &original_as_tt), - "different token tree: {tt:?},\n{original_as_tt:?}" + "different token tree:\n{tt:?}\n\n{original_as_tt:?}" ); } @@ -394,7 +416,7 @@ fn foo() { } "#, expect![[r#" -fn foo () {for _ in __ra_fixup {}} +fn foo () {for _ in __ra_fixup { }} "#]], ) } @@ -422,7 +444,7 @@ fn foo() { } "#, expect![[r#" -fn foo () {for bar in qux {}} +fn foo () {for bar in qux { }} "#]], ) } @@ -453,7 +475,7 @@ fn foo() { } "#, expect![[r#" -fn foo () {match __ra_fixup {}} +fn foo () {match __ra_fixup { }} "#]], ) } @@ -485,7 +507,7 @@ fn foo() { } "#, expect![[r#" -fn foo () {match __ra_fixup {}} +fn foo () {match __ra_fixup { }} "#]], ) } @@ -600,7 +622,7 @@ fn foo() { } "#, expect![[r#" -fn foo () {if a {}} +fn foo () {if a { }} "#]], ) } @@ -614,7 +636,7 @@ fn foo() { } "#, expect![[r#" -fn foo () {if __ra_fixup {}} +fn foo () {if __ra_fixup { }} "#]], ) } @@ -628,7 +650,7 @@ fn foo() { } "#, expect![[r#" -fn foo () {if __ra_fixup {} {}} +fn foo () {if __ra_fixup {} { }} "#]], ) } @@ -642,7 +664,7 @@ fn foo() { } "#, expect![[r#" -fn foo () {while __ra_fixup {}} +fn foo () {while __ra_fixup { }} "#]], ) } @@ -656,7 +678,7 @@ fn foo() { } "#, expect![[r#" -fn foo () {while foo {}} +fn foo () {while foo { }} "#]], ) } @@ -683,7 +705,7 @@ fn foo() { } "#, expect![[r#" -fn foo () {loop {}} +fn foo () {loop { }} "#]], ) } diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs index 9027ea1c27b..491c5e638ee 100644 --- a/crates/hir-expand/src/lib.rs +++ b/crates/hir-expand/src/lib.rs @@ -20,7 +20,7 @@ pub mod mod_path; pub mod attrs; pub mod span; pub mod files; -// mod fixup; +mod fixup; use triomphe::Arc; @@ -42,6 +42,7 @@ use crate::{ builtin_derive_macro::BuiltinDeriveExpander, builtin_fn_macro::{BuiltinFnLikeExpander, EagerExpander}, db::TokenExpander, + fixup::SyntaxFixupUndoInfo, mod_path::ModPath, proc_macro::ProcMacroExpander, span::{ExpansionSpanMap, SpanMap}, @@ -695,8 +696,14 @@ impl ExpansionInfo { let (parse, exp_map) = db.parse_macro_expansion(macro_file).value; let expanded = InMacroFile { file_id: macro_file, value: parse.syntax_node() }; - let macro_arg = db.macro_arg(macro_file.macro_call_id).value.unwrap_or_else(|| { - Arc::new(tt::Subtree { delimiter: tt::Delimiter::UNSPECIFIED, token_trees: Vec::new() }) + let (macro_arg, _) = db.macro_arg(macro_file.macro_call_id).value.unwrap_or_else(|| { + ( + Arc::new(tt::Subtree { + delimiter: tt::Delimiter::UNSPECIFIED, + token_trees: Vec::new(), + }), + SyntaxFixupUndoInfo::NONE, + ) }); let def = loc.def.ast_id().left().and_then(|id| { diff --git a/crates/ide-diagnostics/src/handlers/unresolved_module.rs b/crates/ide-diagnostics/src/handlers/unresolved_module.rs index 234ca2a7671..4f81ec05125 100644 --- a/crates/ide-diagnostics/src/handlers/unresolved_module.rs +++ b/crates/ide-diagnostics/src/handlers/unresolved_module.rs @@ -150,7 +150,7 @@ mod baz {} ], ), main_node: Some( - InFile { + InFileWrapper { file_id: FileId( 0, ), diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs index fdf97ad538c..c19112b4c54 100644 --- a/crates/mbe/src/lib.rs +++ b/crates/mbe/src/lib.rs @@ -36,7 +36,7 @@ pub use crate::{ syntax_bridge::{ map_from_syntax_node, parse_exprs_with_sep, parse_to_token_tree, parse_to_token_tree_static_span, syntax_node_to_token_tree, - syntax_node_to_token_tree_censored, token_tree_to_syntax_node, SpanMapper, + syntax_node_to_token_tree_modified, token_tree_to_syntax_node, SpanMapper, }, token_map::TokenMap, }; diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs index c61c5262860..be5eafd014a 100644 --- a/crates/mbe/src/syntax_bridge.rs +++ b/crates/mbe/src/syntax_bridge.rs @@ -1,6 +1,7 @@ //! Conversions between [`SyntaxNode`] and [`tt::TokenTree`]. -use stdx::non_empty_vec::NonEmptyVec; +use rustc_hash::{FxHashMap, FxHashSet}; +use stdx::{never, non_empty_vec::NonEmptyVec}; use syntax::{ ast::{self, make::tokens::doc_comment}, AstToken, NodeOrToken, Parse, PreorderWithTokens, SmolStr, SyntaxElement, SyntaxKind, @@ -74,14 +75,15 @@ where Ctx: SyntaxContext, SpanMap: SpanMapper<SpanData<Anchor, Ctx>>, { - let mut c = Converter::new(node, vec![], map); + let mut c = Converter::new(node, map, Default::default(), Default::default()); convert_tokens(&mut c) } -pub fn syntax_node_to_token_tree_censored<Anchor, Ctx, SpanMap>( +pub fn syntax_node_to_token_tree_modified<Anchor, Ctx, SpanMap>( node: &SyntaxNode, map: SpanMap, - censored: Vec<SyntaxNode>, + append: FxHashMap<SyntaxElement, Vec<tt::Leaf<SpanData<Anchor, Ctx>>>>, + remove: FxHashSet<SyntaxNode>, ) -> tt::Subtree<SpanData<Anchor, Ctx>> where SpanMap: SpanMapper<SpanData<Anchor, Ctx>>, @@ -89,7 +91,7 @@ where Anchor: Copy, Ctx: SyntaxContext, { - let mut c = Converter::new(node, censored, map); + let mut c = Converter::new(node, map, append, remove); convert_tokens(&mut c) } @@ -237,102 +239,105 @@ where while let Some((token, abs_range)) = conv.bump() { let tt::Subtree { delimiter, token_trees: result } = stack.last_mut(); - let kind = token.kind(conv); - - let tt = match kind { - // Desugar doc comments into doc attributes - COMMENT => { - let span = conv.span_for(abs_range); - if let Some(tokens) = conv.convert_doc_comment(&token, span) { - result.extend(tokens); - } - continue; - } - _ if kind.is_punct() && kind != UNDERSCORE => { - let expected = match delimiter.kind { - tt::DelimiterKind::Parenthesis => Some(T![')']), - tt::DelimiterKind::Brace => Some(T!['}']), - tt::DelimiterKind::Bracket => Some(T![']']), - tt::DelimiterKind::Invisible => None, - }; - - // Current token is a closing delimiter that we expect, fix up the closing span - // and end the subtree here - if matches!(expected, Some(expected) if expected == kind) { - if let Some(mut subtree) = stack.pop() { - subtree.delimiter.close = conv.span_for(abs_range); - stack.last_mut().token_trees.push(subtree.into()); + let tt = match token.as_leaf() { + Some(leaf) => tt::TokenTree::Leaf(leaf.clone()), + None => match token.kind(conv) { + // Desugar doc comments into doc attributes + COMMENT => { + let span = conv.span_for(abs_range); + if let Some(tokens) = conv.convert_doc_comment(&token, span) { + result.extend(tokens); } continue; } + kind if kind.is_punct() && kind != UNDERSCORE => { + let expected = match delimiter.kind { + tt::DelimiterKind::Parenthesis => Some(T![')']), + tt::DelimiterKind::Brace => Some(T!['}']), + tt::DelimiterKind::Bracket => Some(T![']']), + tt::DelimiterKind::Invisible => None, + }; - let delim = match kind { - T!['('] => Some(tt::DelimiterKind::Parenthesis), - T!['{'] => Some(tt::DelimiterKind::Brace), - T!['['] => Some(tt::DelimiterKind::Bracket), - _ => None, - }; - - // Start a new subtree - if let Some(kind) = delim { - let open = conv.span_for(abs_range); - stack.push(tt::Subtree { - delimiter: tt::Delimiter { - open, - // will be overwritten on subtree close above - close: open, - kind, - }, - token_trees: vec![], - }); - continue; - } + // Current token is a closing delimiter that we expect, fix up the closing span + // and end the subtree here + if matches!(expected, Some(expected) if expected == kind) { + if let Some(mut subtree) = stack.pop() { + subtree.delimiter.close = conv.span_for(abs_range); + stack.last_mut().token_trees.push(subtree.into()); + } + continue; + } - let spacing = match conv.peek().map(|next| next.kind(conv)) { - Some(kind) if is_single_token_op(kind) => tt::Spacing::Joint, - _ => tt::Spacing::Alone, - }; - let Some(char) = token.to_char(conv) else { - panic!("Token from lexer must be single char: token = {token:#?}") - }; - tt::Leaf::from(tt::Punct { char, spacing, span: conv.span_for(abs_range) }).into() - } - _ => { - macro_rules! make_leaf { - ($i:ident) => { - tt::$i { span: conv.span_for(abs_range), text: token.to_text(conv) }.into() + let delim = match kind { + T!['('] => Some(tt::DelimiterKind::Parenthesis), + T!['{'] => Some(tt::DelimiterKind::Brace), + T!['['] => Some(tt::DelimiterKind::Bracket), + _ => None, }; - } - let leaf: tt::Leaf<_> = match kind { - T![true] | T![false] => make_leaf!(Ident), - IDENT => make_leaf!(Ident), - UNDERSCORE => make_leaf!(Ident), - k if k.is_keyword() => make_leaf!(Ident), - k if k.is_literal() => make_leaf!(Literal), - LIFETIME_IDENT => { - let apostrophe = tt::Leaf::from(tt::Punct { - char: '\'', - spacing: tt::Spacing::Joint, - span: conv - .span_for(TextRange::at(abs_range.start(), TextSize::of('\''))), - }); - result.push(apostrophe.into()); - - let ident = tt::Leaf::from(tt::Ident { - text: SmolStr::new(&token.to_text(conv)[1..]), - span: conv.span_for(TextRange::at( - abs_range.start() + TextSize::of('\''), - abs_range.end(), - )), + + // Start a new subtree + if let Some(kind) = delim { + let open = conv.span_for(abs_range); + stack.push(tt::Subtree { + delimiter: tt::Delimiter { + open, + // will be overwritten on subtree close above + close: open, + kind, + }, + token_trees: vec![], }); - result.push(ident.into()); continue; } - _ => continue, - }; - leaf.into() - } + let spacing = match conv.peek().map(|next| next.kind(conv)) { + Some(kind) if is_single_token_op(kind) => tt::Spacing::Joint, + _ => tt::Spacing::Alone, + }; + let Some(char) = token.to_char(conv) else { + panic!("Token from lexer must be single char: token = {token:#?}") + }; + tt::Leaf::from(tt::Punct { char, spacing, span: conv.span_for(abs_range) }) + .into() + } + kind => { + macro_rules! make_leaf { + ($i:ident) => { + tt::$i { span: conv.span_for(abs_range), text: token.to_text(conv) } + .into() + }; + } + let leaf: tt::Leaf<_> = match kind { + T![true] | T![false] => make_leaf!(Ident), + IDENT => make_leaf!(Ident), + UNDERSCORE => make_leaf!(Ident), + k if k.is_keyword() => make_leaf!(Ident), + k if k.is_literal() => make_leaf!(Literal), + LIFETIME_IDENT => { + let apostrophe = tt::Leaf::from(tt::Punct { + char: '\'', + spacing: tt::Spacing::Joint, + span: conv + .span_for(TextRange::at(abs_range.start(), TextSize::of('\''))), + }); + result.push(apostrophe.into()); + + let ident = tt::Leaf::from(tt::Ident { + text: SmolStr::new(&token.to_text(conv)[1..]), + span: conv.span_for(TextRange::at( + abs_range.start() + TextSize::of('\''), + abs_range.end(), + )), + }); + result.push(ident.into()); + continue; + } + _ => continue, + }; + + leaf.into() + } + }, }; result.push(tt); @@ -470,16 +475,20 @@ struct StaticRawConverter<'a, S> { span: S, } -trait SrcToken<Ctx>: std::fmt::Debug { +trait SrcToken<Ctx, S>: std::fmt::Debug { fn kind(&self, ctx: &Ctx) -> SyntaxKind; fn to_char(&self, ctx: &Ctx) -> Option<char>; fn to_text(&self, ctx: &Ctx) -> SmolStr; + + fn as_leaf(&self) -> Option<&tt::Leaf<S>> { + None + } } trait TokenConverter<S>: Sized { - type Token: SrcToken<Self>; + type Token: SrcToken<Self, S>; fn convert_doc_comment(&self, token: &Self::Token, span: S) -> Option<Vec<tt::TokenTree<S>>>; @@ -490,7 +499,7 @@ trait TokenConverter<S>: Sized { fn span_for(&self, range: TextRange) -> S; } -impl<Anchor> SrcToken<RawConverter<'_, Anchor>> for usize { +impl<Anchor, S> SrcToken<RawConverter<'_, Anchor>, S> for usize { fn kind(&self, ctx: &RawConverter<'_, Anchor>) -> SyntaxKind { ctx.lexed.kind(*self) } @@ -504,7 +513,7 @@ impl<Anchor> SrcToken<RawConverter<'_, Anchor>> for usize { } } -impl<S: Span> SrcToken<StaticRawConverter<'_, S>> for usize { +impl<S: Span> SrcToken<StaticRawConverter<'_, S>, S> for usize { fn kind(&self, ctx: &StaticRawConverter<'_, S>) -> SyntaxKind { ctx.lexed.kind(*self) } @@ -593,32 +602,79 @@ where } } -struct Converter<SpanMap> { +struct Converter<SpanMap, S> { current: Option<SyntaxToken>, + current_leafs: Vec<tt::Leaf<S>>, preorder: PreorderWithTokens, range: TextRange, punct_offset: Option<(SyntaxToken, TextSize)>, /// Used to make the emitted text ranges in the spans relative to the span anchor. map: SpanMap, - censored: Vec<SyntaxNode>, -} - -impl<SpanMap> Converter<SpanMap> { - fn new(node: &SyntaxNode, censored: Vec<SyntaxNode>, map: SpanMap) -> Self { - let range = node.text_range(); - let mut preorder = node.preorder_with_tokens(); - let first = Self::next_token(&mut preorder, &censored); - Converter { current: first, preorder, range, punct_offset: None, censored, map } - } - - fn next_token(preorder: &mut PreorderWithTokens, censor: &[SyntaxNode]) -> Option<SyntaxToken> { - while let Some(ev) = preorder.next() { + append: FxHashMap<SyntaxElement, Vec<tt::Leaf<S>>>, + remove: FxHashSet<SyntaxNode>, +} + +impl<SpanMap, S> Converter<SpanMap, S> { + fn new( + node: &SyntaxNode, + map: SpanMap, + append: FxHashMap<SyntaxElement, Vec<tt::Leaf<S>>>, + remove: FxHashSet<SyntaxNode>, + ) -> Self { + let mut this = Converter { + current: None, + preorder: node.preorder_with_tokens(), + range: node.text_range(), + punct_offset: None, + map, + append, + remove, + current_leafs: vec![], + }; + let first = this.next_token(); + this.current = first; + this + } + + fn next_token(&mut self) -> Option<SyntaxToken> { + // while let Some(ev) = self.preorder.next() { + // match ev { + // WalkEvent::Enter(SyntaxElement::Token(t)) => { + // if let Some(leafs) = self.append.remove(&t.clone().into()) { + // self.current_leafs.extend(leafs); + // } + // return Some(t); + // } + // WalkEvent::Enter(SyntaxElement::Node(n)) if self.remove.contains(&n) => { + // self.preorder.skip_subtree(); + // if let Some(leafs) = self.append.remove(&n.into()) { + // self.current_leafs.extend(leafs); + // } + // } + // _ => (), + // } + // } + // None; + + while let Some(ev) = self.preorder.next() { match ev { WalkEvent::Enter(SyntaxElement::Token(t)) => return Some(t), - WalkEvent::Enter(SyntaxElement::Node(n)) if censor.contains(&n) => { - preorder.skip_subtree() + WalkEvent::Enter(SyntaxElement::Node(n)) if self.remove.contains(&n) => { + self.preorder.skip_subtree(); + if let Some(mut v) = self.append.remove(&n.into()) { + v.reverse(); + self.current_leafs.extend(v); + return None; + } + } + WalkEvent::Enter(SyntaxElement::Node(_)) => (), + WalkEvent::Leave(ele) => { + if let Some(mut v) = self.append.remove(&ele) { + v.reverse(); + self.current_leafs.extend(v); + return None; + } } - _ => (), } } None @@ -626,45 +682,62 @@ impl<SpanMap> Converter<SpanMap> { } #[derive(Debug)] -enum SynToken { +enum SynToken<S> { Ordinary(SyntaxToken), - Punct(SyntaxToken, usize), + Punct { token: SyntaxToken, offset: usize }, + Leaf(tt::Leaf<S>), } -impl SynToken { +impl<S> SynToken<S> { fn token(&self) -> &SyntaxToken { match self { - SynToken::Ordinary(it) | SynToken::Punct(it, _) => it, + SynToken::Ordinary(it) | SynToken::Punct { token: it, offset: _ } => it, + SynToken::Leaf(_) => unreachable!(), } } } -impl<SpanMap> SrcToken<Converter<SpanMap>> for SynToken { - fn kind(&self, ctx: &Converter<SpanMap>) -> SyntaxKind { +impl<SpanMap, S: std::fmt::Debug> SrcToken<Converter<SpanMap, S>, S> for SynToken<S> { + fn kind(&self, ctx: &Converter<SpanMap, S>) -> SyntaxKind { match self { SynToken::Ordinary(token) => token.kind(), - SynToken::Punct(..) => SyntaxKind::from_char(self.to_char(ctx).unwrap()).unwrap(), + SynToken::Punct { .. } => SyntaxKind::from_char(self.to_char(ctx).unwrap()).unwrap(), + SynToken::Leaf(_) => { + never!(); + SyntaxKind::ERROR + } } } - fn to_char(&self, _ctx: &Converter<SpanMap>) -> Option<char> { + fn to_char(&self, _ctx: &Converter<SpanMap, S>) -> Option<char> { match self { SynToken::Ordinary(_) => None, - SynToken::Punct(it, i) => it.text().chars().nth(*i), + SynToken::Punct { token: it, offset: i } => it.text().chars().nth(*i), + SynToken::Leaf(_) => None, + } + } + fn to_text(&self, _ctx: &Converter<SpanMap, S>) -> SmolStr { + match self { + SynToken::Ordinary(token) | SynToken::Punct { token, offset: _ } => token.text().into(), + SynToken::Leaf(_) => { + never!(); + "".into() + } } } - fn to_text(&self, _ctx: &Converter<SpanMap>) -> SmolStr { + fn as_leaf(&self) -> Option<&tt::Leaf<S>> { match self { - SynToken::Ordinary(token) | SynToken::Punct(token, _) => token.text().into(), + SynToken::Ordinary(_) | SynToken::Punct { .. } => None, + SynToken::Leaf(it) => Some(it), } } } -impl<S, SpanMap> TokenConverter<S> for Converter<SpanMap> +impl<S, SpanMap> TokenConverter<S> for Converter<SpanMap, S> where S: Span, SpanMap: SpanMapper<S>, { - type Token = SynToken; + type Token = SynToken<S>; fn convert_doc_comment(&self, token: &Self::Token, span: S) -> Option<Vec<tt::TokenTree<S>>> { convert_doc_comment(token.token(), span) } @@ -676,20 +749,31 @@ where let range = punct.text_range(); self.punct_offset = Some((punct.clone(), offset)); let range = TextRange::at(range.start() + offset, TextSize::of('.')); - return Some((SynToken::Punct(punct, u32::from(offset) as usize), range)); + return Some(( + SynToken::Punct { token: punct, offset: u32::from(offset) as usize }, + range, + )); + } + } + + if let Some(leaf) = self.current_leafs.pop() { + if self.current_leafs.is_empty() { + self.current = self.next_token(); } + return Some((SynToken::Leaf(leaf), TextRange::empty(TextSize::new(0)))); } let curr = self.current.clone()?; if !self.range.contains_range(curr.text_range()) { return None; } - self.current = Self::next_token(&mut self.preorder, &self.censored); + + self.current = self.next_token(); let token = if curr.kind().is_punct() { self.punct_offset = Some((curr.clone(), 0.into())); let range = curr.text_range(); let range = TextRange::at(range.start(), TextSize::of('.')); - (SynToken::Punct(curr, 0 as usize), range) + (SynToken::Punct { token: curr, offset: 0 as usize }, range) } else { self.punct_offset = None; let range = curr.text_range(); @@ -703,7 +787,7 @@ where if let Some((punct, mut offset)) = self.punct_offset.clone() { offset += TextSize::of('.'); if usize::from(offset) < punct.text().len() { - return Some(SynToken::Punct(punct, usize::from(offset))); + return Some(SynToken::Punct { token: punct, offset: usize::from(offset) }); } } @@ -713,7 +797,7 @@ where } let token = if curr.kind().is_punct() { - SynToken::Punct(curr, 0 as usize) + SynToken::Punct { token: curr, offset: 0 as usize } } else { SynToken::Ordinary(curr) }; |
