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.rs87
1 files changed, 63 insertions, 24 deletions
diff --git a/crates/hir_expand/src/fixup.rs b/crates/hir_expand/src/fixup.rs
index f82fba46e9..a4acf9e987 100644
--- a/crates/hir_expand/src/fixup.rs
+++ b/crates/hir_expand/src/fixup.rs
@@ -1,10 +1,10 @@
-use mbe::SyntheticToken;
+use mbe::{SyntheticToken, SyntheticTokenId, TokenMap};
use rustc_hash::FxHashMap;
use syntax::{
ast::{self, AstNode},
- match_ast, SyntaxKind, SyntaxNode, SyntaxToken,
+ match_ast, SyntaxKind, SyntaxNode, TextRange,
};
-use tt::{Leaf, Subtree};
+use tt::Subtree;
#[derive(Debug)]
pub struct SyntaxFixups {
@@ -16,6 +16,7 @@ pub fn fixup_syntax(node: &SyntaxNode) -> SyntaxFixups {
let mut append = FxHashMap::default();
let mut replace = FxHashMap::default();
let mut preorder = node.preorder();
+ let empty_id = SyntheticTokenId(0);
while let Some(event) = preorder.next() {
let node = match event {
syntax::WalkEvent::Enter(node) => node,
@@ -27,12 +28,32 @@ pub fn fixup_syntax(node: &SyntaxNode) -> SyntaxFixups {
preorder.skip_subtree();
continue;
}
+ let end_range = TextRange::empty(node.text_range().end());
match_ast! {
match node {
ast::FieldExpr(it) => {
if it.name_ref().is_none() {
// incomplete field access: some_expr.|
- append.insert(node.clone(), vec![(SyntaxKind::IDENT, "__ra_fixup".into())]);
+ append.insert(node.clone(), vec![
+ SyntheticToken {
+ kind: SyntaxKind::IDENT,
+ text: "__ra_fixup".into(),
+ range: end_range,
+ id: empty_id,
+ },
+ ]);
+ }
+ },
+ ast::ExprStmt(it) => {
+ if it.semicolon_token().is_none() {
+ append.insert(node.clone(), vec![
+ SyntheticToken {
+ kind: SyntaxKind::SEMICOLON,
+ text: ";".into(),
+ range: end_range,
+ id: empty_id,
+ },
+ ]);
}
},
_ => (),
@@ -42,20 +63,21 @@ pub fn fixup_syntax(node: &SyntaxNode) -> SyntaxFixups {
SyntaxFixups { append, replace }
}
-pub fn reverse_fixups(tt: &mut Subtree) {
+pub fn reverse_fixups(tt: &mut Subtree, token_map: &TokenMap) {
+ eprintln!("token_map: {:?}", token_map);
tt.token_trees.retain(|tt| match tt {
- tt::TokenTree::Leaf(Leaf::Ident(ident)) => ident.text != "__ra_fixup",
+ tt::TokenTree::Leaf(leaf) => token_map.synthetic_token_id(leaf.id()).is_none(),
_ => true,
});
tt.token_trees.iter_mut().for_each(|tt| match tt {
- tt::TokenTree::Subtree(tt) => reverse_fixups(tt),
+ tt::TokenTree::Subtree(tt) => reverse_fixups(tt, token_map),
_ => {}
});
}
#[cfg(test)]
mod tests {
- use expect_test::{Expect, expect};
+ use expect_test::{expect, Expect};
use super::reverse_fixups;
@@ -63,7 +85,7 @@ mod tests {
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_censored(
+ let (mut tt, tmap) = mbe::syntax_node_to_token_tree_censored(
&parsed.syntax_node(),
fixups.replace,
fixups.append,
@@ -77,9 +99,14 @@ mod tests {
// the fixed-up tree should be syntactically valid
let (parse, _) = mbe::token_tree_to_syntax_node(&tt, ::mbe::TopEntryPoint::MacroItems);
- assert_eq!(parse.errors(), &[], "parse has syntax errors. parse tree:\n{:#?}", parse.syntax_node());
+ assert_eq!(
+ parse.errors(),
+ &[],
+ "parse has syntax errors. parse tree:\n{:#?}",
+ parse.syntax_node()
+ );
- reverse_fixups(&mut tt);
+ reverse_fixups(&mut tt, &tmap);
// the fixed-up + reversed version should be equivalent to the original input
// (but token IDs don't matter)
@@ -89,48 +116,60 @@ mod tests {
#[test]
fn incomplete_field_expr_1() {
- check(r#"
+ check(
+ r#"
fn foo() {
a.
}
-"#, expect![[r#"
+"#,
+ expect![[r#"
fn foo () {a . __ra_fixup}
-"#]])
+"#]],
+ )
}
#[test]
fn incomplete_field_expr_2() {
- check(r#"
+ check(
+ r#"
fn foo() {
a. ;
}
-"#, expect![[r#"
+"#,
+ expect![[r#"
fn foo () {a . __ra_fixup ;}
-"#]])
+"#]],
+ )
}
#[test]
fn incomplete_field_expr_3() {
- check(r#"
+ check(
+ r#"
fn foo() {
a. ;
bar();
}
-"#, expect![[r#"
+"#,
+ expect![[r#"
fn foo () {a . __ra_fixup ; bar () ;}
-"#]])
+"#]],
+ )
}
#[test]
fn field_expr_before_call() {
// another case that easily happens while typing
- check(r#"
+ check(
+ r#"
fn foo() {
a.b
bar();
}
-"#, expect![[r#"
-fn foo () {a . b bar () ;}
-"#]])
+"#,
+ expect![[r#"
+fn foo () {a . b ; bar () ;}
+"#]],
+ )
}
}