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.rs206
1 files changed, 114 insertions, 92 deletions
diff --git a/crates/hir-expand/src/fixup.rs b/crates/hir-expand/src/fixup.rs
index 8fa7d57629..77ddf7a48c 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 { }}
"#]],
)
}