summaryrefslogtreecommitdiffstats
path: root/src/tools/rust-analyzer/crates/hir-expand
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/Cargo.toml4
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/ast_id_map.rs26
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs86
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs36
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/db.rs18
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs298
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/lib.rs30
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs17
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/name.rs64
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/quote.rs4
10 files changed, 456 insertions, 127 deletions
diff --git a/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml b/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml
index dfd470ffc..3359c99b3 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml
@@ -15,11 +15,11 @@ tracing = "0.1.35"
either = "1.7.0"
rustc-hash = "1.1.0"
la-arena = { version = "0.3.0", path = "../../lib/la-arena" }
-itertools = "0.10.3"
+itertools = "0.10.5"
hashbrown = { version = "0.12.1", features = [
"inline-more",
], default-features = false }
-smallvec = { version = "1.9.0", features = ["const_new"] }
+smallvec = { version = "1.10.0", features = ["const_new"] }
stdx = { path = "../stdx", version = "0.0.0" }
base-db = { path = "../base-db", version = "0.0.0" }
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/ast_id_map.rs b/src/tools/rust-analyzer/crates/hir-expand/src/ast_id_map.rs
index c1ddef03b..2b27db0e9 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/ast_id_map.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/ast_id_map.rs
@@ -15,7 +15,7 @@ use std::{
use la_arena::{Arena, Idx};
use profile::Count;
use rustc_hash::FxHasher;
-use syntax::{ast, match_ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr};
+use syntax::{ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr};
/// `AstId` points to an AST node in a specific file.
pub struct FileAstId<N: AstNode> {
@@ -92,18 +92,17 @@ impl AstIdMap {
// change parent's id. This means that, say, adding a new function to a
// trait does not change ids of top-level items, which helps caching.
bdfs(node, |it| {
- match_ast! {
- match it {
- ast::Item(module_item) => {
- res.alloc(module_item.syntax());
- true
- },
- ast::BlockExpr(block) => {
- res.alloc(block.syntax());
- true
- },
- _ => false,
- }
+ let kind = it.kind();
+ if ast::Item::can_cast(kind)
+ || ast::BlockExpr::can_cast(kind)
+ || ast::Variant::can_cast(kind)
+ || ast::RecordField::can_cast(kind)
+ || ast::TupleField::can_cast(kind)
+ {
+ res.alloc(&it);
+ true
+ } else {
+ false
}
});
res.map = hashbrown::HashMap::with_capacity_and_hasher(res.arena.len(), ());
@@ -123,6 +122,7 @@ impl AstIdMap {
let raw = self.erased_ast_id(item.syntax());
FileAstId { raw, _ty: PhantomData }
}
+
fn erased_ast_id(&self, item: &SyntaxNode) -> ErasedFileAstId {
let ptr = SyntaxNodePtr::new(item);
let hash = hash_ptr(&ptr);
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs
index 79989bc2e..8966047c9 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs
@@ -60,7 +60,8 @@ pub fn find_builtin_derive(ident: &name::Name) -> Option<BuiltinDeriveExpander>
struct BasicAdtInfo {
name: tt::Ident,
- type_or_const_params: usize,
+ /// `Some(ty)` if it's a const param of type `ty`, `None` if it's a type param.
+ param_types: Vec<Option<tt::Subtree>>,
}
fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
@@ -92,50 +93,22 @@ fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
let name_token_id =
token_map.token_by_range(name.syntax().text_range()).unwrap_or_else(TokenId::unspecified);
let name_token = tt::Ident { id: name_token_id, text: name.text().into() };
- let type_or_const_params =
- params.map_or(0, |type_param_list| type_param_list.type_or_const_params().count());
- Ok(BasicAdtInfo { name: name_token, type_or_const_params })
-}
-
-fn make_type_args(n: usize, bound: Vec<tt::TokenTree>) -> Vec<tt::TokenTree> {
- let mut result = Vec::<tt::TokenTree>::with_capacity(n * 2);
- result.push(
- tt::Leaf::Punct(tt::Punct {
- char: '<',
- spacing: tt::Spacing::Alone,
- id: tt::TokenId::unspecified(),
- })
- .into(),
- );
- for i in 0..n {
- if i > 0 {
- result.push(
- tt::Leaf::Punct(tt::Punct {
- char: ',',
- spacing: tt::Spacing::Alone,
- id: tt::TokenId::unspecified(),
- })
- .into(),
- );
- }
- result.push(
- tt::Leaf::Ident(tt::Ident {
- id: tt::TokenId::unspecified(),
- text: format!("T{}", i).into(),
- })
- .into(),
- );
- result.extend(bound.iter().cloned());
- }
- result.push(
- tt::Leaf::Punct(tt::Punct {
- char: '>',
- spacing: tt::Spacing::Alone,
- id: tt::TokenId::unspecified(),
+ let param_types = params
+ .into_iter()
+ .flat_map(|param_list| param_list.type_or_const_params())
+ .map(|param| {
+ if let ast::TypeOrConstParam::Const(param) = param {
+ let ty = param
+ .ty()
+ .map(|ty| mbe::syntax_node_to_token_tree(ty.syntax()).0)
+ .unwrap_or_default();
+ Some(ty)
+ } else {
+ None
+ }
})
- .into(),
- );
- result
+ .collect();
+ Ok(BasicAdtInfo { name: name_token, param_types })
}
fn expand_simple_derive(tt: &tt::Subtree, trait_path: tt::Subtree) -> ExpandResult<tt::Subtree> {
@@ -143,14 +116,27 @@ fn expand_simple_derive(tt: &tt::Subtree, trait_path: tt::Subtree) -> ExpandResu
Ok(info) => info,
Err(e) => return ExpandResult::only_err(e),
};
+ let (params, args): (Vec<_>, Vec<_>) = info
+ .param_types
+ .into_iter()
+ .enumerate()
+ .map(|(idx, param_ty)| {
+ let ident = tt::Leaf::Ident(tt::Ident {
+ id: tt::TokenId::unspecified(),
+ text: format!("T{idx}").into(),
+ });
+ let ident_ = ident.clone();
+ if let Some(ty) = param_ty {
+ (quote! { const #ident : #ty , }, quote! { #ident_ , })
+ } else {
+ let bound = trait_path.clone();
+ (quote! { #ident : #bound , }, quote! { #ident_ , })
+ }
+ })
+ .unzip();
let name = info.name;
- let trait_path_clone = trait_path.token_trees.clone();
- let bound = (quote! { : ##trait_path_clone }).token_trees;
- let type_params = make_type_args(info.type_or_const_params, bound);
- let type_args = make_type_args(info.type_or_const_params, Vec::new());
- let trait_path = trait_path.token_trees;
let expanded = quote! {
- impl ##type_params ##trait_path for #name ##type_args {}
+ impl < ##params > #trait_path for #name < ##args > {}
};
ExpandResult::ok(expanded)
}
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs
index 76da7c9f1..7b19518e2 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs
@@ -238,9 +238,9 @@ fn format_args_expand(
) -> ExpandResult<tt::Subtree> {
// We expand `format_args!("", a1, a2)` to
// ```
- // std::fmt::Arguments::new_v1(&[], &[
- // std::fmt::ArgumentV1::new(&arg1,std::fmt::Display::fmt),
- // std::fmt::ArgumentV1::new(&arg2,std::fmt::Display::fmt),
+ // $crate::fmt::Arguments::new_v1(&[], &[
+ // $crate::fmt::ArgumentV1::new(&arg1,$crate::fmt::Display::fmt),
+ // $crate::fmt::ArgumentV1::new(&arg2,$crate::fmt::Display::fmt),
// ])
// ```,
// which is still not really correct, but close enough for now
@@ -251,17 +251,21 @@ fn format_args_expand(
}
for arg in &mut args {
// Remove `key =`.
- if matches!(arg.token_trees.get(1), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=' && p.spacing != tt::Spacing::Joint)
+ if matches!(arg.token_trees.get(1), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=')
{
- arg.token_trees.drain(..2);
+ // but not with `==`
+ if !matches!(arg.token_trees.get(2), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=' )
+ {
+ arg.token_trees.drain(..2);
+ }
}
}
let _format_string = args.remove(0);
let arg_tts = args.into_iter().flat_map(|arg| {
- quote! { std::fmt::ArgumentV1::new(&(#arg), std::fmt::Display::fmt), }
+ quote! { #DOLLAR_CRATE::fmt::ArgumentV1::new(&(#arg), #DOLLAR_CRATE::fmt::Display::fmt), }
}.token_trees);
let expanded = quote! {
- std::fmt::Arguments::new_v1(&[], &[##arg_tts])
+ #DOLLAR_CRATE::fmt::Arguments::new_v1(&[], &[##arg_tts])
};
ExpandResult::ok(expanded)
}
@@ -357,6 +361,12 @@ fn unquote_str(lit: &tt::Literal) -> Option<String> {
token.value().map(|it| it.into_owned())
}
+fn unquote_char(lit: &tt::Literal) -> Option<char> {
+ let lit = ast::make::tokens::literal(&lit.to_string());
+ let token = ast::Char::cast(lit)?;
+ token.value()
+}
+
fn unquote_byte_string(lit: &tt::Literal) -> Option<Vec<u8>> {
let lit = ast::make::tokens::literal(&lit.to_string());
let token = ast::ByteString::cast(lit)?;
@@ -408,8 +418,12 @@ fn concat_expand(
// concat works with string and char literals, so remove any quotes.
// It also works with integer, float and boolean literals, so just use the rest
// as-is.
- let component = unquote_str(it).unwrap_or_else(|| it.text.to_string());
- text.push_str(&component);
+ if let Some(c) = unquote_char(it) {
+ text.push(c);
+ } else {
+ let component = unquote_str(it).unwrap_or_else(|| it.text.to_string());
+ text.push_str(&component);
+ }
}
// handle boolean literals
tt::TokenTree::Leaf(tt::Leaf::Ident(id))
@@ -661,8 +675,8 @@ fn option_env_expand(
};
let expanded = match get_env_inner(db, arg_id, &key) {
- None => quote! { std::option::Option::None::<&str> },
- Some(s) => quote! { std::option::Some(#s) },
+ None => quote! { #DOLLAR_CRATE::option::Option::None::<&str> },
+ Some(s) => quote! { #DOLLAR_CRATE::option::Some(#s) },
};
ExpandResult::ok(ExpandedEager::new(expanded))
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs
index bd60c3d26..87e4db039 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs
@@ -221,8 +221,16 @@ pub fn expand_speculative(
fixup::reverse_fixups(&mut speculative_expansion.value, &spec_args_tmap, &fixups.undo_info);
let (node, rev_tmap) = token_tree_to_syntax_node(&speculative_expansion.value, expand_to);
- let range = rev_tmap.first_range_by_token(token_id, token_to_map.kind())?;
- let token = node.syntax_node().covering_element(range).into_token()?;
+ let syntax_node = node.syntax_node();
+ let token = rev_tmap
+ .ranges_by_token(token_id, token_to_map.kind())
+ .filter_map(|range| syntax_node.covering_element(range).into_token())
+ .min_by_key(|t| {
+ // prefer tokens of the same kind and text
+ // Note the inversion of the score here, as we want to prefer the first token in case
+ // of all tokens having the same score
+ (t.kind() != token_to_map.kind()) as u8 + (t.text() != token_to_map.text()) as u8
+ })?;
Some((node.syntax_node(), token))
}
@@ -321,7 +329,11 @@ fn censor_for_macro_input(loc: &MacroCallLoc, node: &SyntaxNode) -> FxHashSet<Sy
ast::Item::cast(node.clone())?
.attrs()
.take(derive_attr_index as usize + 1)
- // FIXME
+ // FIXME, this resolution should not be done syntactically
+ // derive is a proper macro now, no longer builtin
+ // But we do not have resolution at this stage, this means
+ // we need to know about all macro calls for the given ast item here
+ // so we require some kind of mapping...
.filter(|attr| attr.simple_name().as_deref() == Some("derive"))
.map(|it| it.syntax().clone())
.collect()
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs b/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs
index 9999790fa..893e6fe4b 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs
@@ -5,7 +5,7 @@ use std::mem;
use mbe::{SyntheticToken, SyntheticTokenId, TokenMap};
use rustc_hash::FxHashMap;
use syntax::{
- ast::{self, AstNode},
+ ast::{self, AstNode, HasLoopBody},
match_ast, SyntaxElement, SyntaxKind, SyntaxNode, TextRange,
};
use tt::Subtree;
@@ -67,7 +67,6 @@ pub(crate) 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! {
@@ -142,8 +141,127 @@ pub(crate) fn fixup_syntax(node: &SyntaxNode) -> SyntaxFixups {
]);
}
},
+ ast::WhileExpr(it) => {
+ if it.condition().is_none() {
+ // insert placeholder token after the while token
+ let while_token = match it.while_token() {
+ Some(t) => t,
+ None => continue,
+ };
+ append.insert(while_token.into(), vec![
+ SyntheticToken {
+ kind: SyntaxKind::IDENT,
+ text: "__ra_fixup".into(),
+ range: end_range,
+ id: EMPTY_ID,
+ },
+ ]);
+ }
+ if it.loop_body().is_none() {
+ append.insert(node.clone().into(), vec![
+ SyntheticToken {
+ kind: SyntaxKind::L_CURLY,
+ text: "{".into(),
+ range: end_range,
+ id: EMPTY_ID,
+ },
+ SyntheticToken {
+ kind: SyntaxKind::R_CURLY,
+ text: "}".into(),
+ range: end_range,
+ id: EMPTY_ID,
+ },
+ ]);
+ }
+ },
+ ast::LoopExpr(it) => {
+ if it.loop_body().is_none() {
+ append.insert(node.clone().into(), vec![
+ SyntheticToken {
+ kind: SyntaxKind::L_CURLY,
+ text: "{".into(),
+ range: end_range,
+ id: EMPTY_ID,
+ },
+ SyntheticToken {
+ kind: SyntaxKind::R_CURLY,
+ text: "}".into(),
+ range: end_range,
+ id: EMPTY_ID,
+ },
+ ]);
+ }
+ },
// FIXME: foo::
- // FIXME: for, loop, match etc.
+ ast::MatchExpr(it) => {
+ if it.expr().is_none() {
+ let match_token = match it.match_token() {
+ Some(t) => t,
+ None => continue
+ };
+ append.insert(match_token.into(), vec![
+ SyntheticToken {
+ kind: SyntaxKind::IDENT,
+ text: "__ra_fixup".into(),
+ range: end_range,
+ id: EMPTY_ID
+ },
+ ]);
+ }
+ if it.match_arm_list().is_none() {
+ // No match arms
+ append.insert(node.clone().into(), vec![
+ SyntheticToken {
+ kind: SyntaxKind::L_CURLY,
+ text: "{".into(),
+ range: end_range,
+ id: EMPTY_ID,
+ },
+ SyntheticToken {
+ kind: SyntaxKind::R_CURLY,
+ text: "}".into(),
+ range: end_range,
+ id: EMPTY_ID,
+ },
+ ]);
+ }
+ },
+ ast::ForExpr(it) => {
+ let for_token = match it.for_token() {
+ Some(token) => token,
+ None => continue
+ };
+
+ let [pat, in_token, iter] = [
+ (SyntaxKind::UNDERSCORE, "_"),
+ (SyntaxKind::IN_KW, "in"),
+ (SyntaxKind::IDENT, "__ra_fixup")
+ ].map(|(kind, text)| SyntheticToken { kind, text: text.into(), range: end_range, id: EMPTY_ID});
+
+ if it.pat().is_none() && it.in_token().is_none() && it.iterable().is_none() {
+ append.insert(for_token.into(), vec![pat, in_token, iter]);
+ // does something funky -- see test case for_no_pat
+ } else if it.pat().is_none() {
+ append.insert(for_token.into(), vec![pat]);
+ }
+
+ if it.loop_body().is_none() {
+ append.insert(node.clone().into(), vec![
+ SyntheticToken {
+ kind: SyntaxKind::L_CURLY,
+ text: "{".into(),
+ range: end_range,
+ id: EMPTY_ID,
+ },
+ SyntheticToken {
+ kind: SyntaxKind::R_CURLY,
+ text: "}".into(),
+ range: end_range,
+ id: EMPTY_ID,
+ },
+ ]);
+ }
+ },
_ => (),
}
}
@@ -237,6 +355,111 @@ mod tests {
}
#[test]
+ fn just_for_token() {
+ check(
+ r#"
+fn foo() {
+ for
+}
+"#,
+ expect![[r#"
+fn foo () {for _ in __ra_fixup {}}
+"#]],
+ )
+ }
+
+ #[test]
+ fn for_no_iter_pattern() {
+ check(
+ r#"
+fn foo() {
+ for {}
+}
+"#,
+ expect![[r#"
+fn foo () {for _ in __ra_fixup {}}
+"#]],
+ )
+ }
+
+ #[test]
+ fn for_no_body() {
+ check(
+ r#"
+fn foo() {
+ for bar in qux
+}
+"#,
+ expect![[r#"
+fn foo () {for bar in qux {}}
+"#]],
+ )
+ }
+
+ // FIXME: https://github.com/rust-lang/rust-analyzer/pull/12937#discussion_r937633695
+ #[test]
+ fn for_no_pat() {
+ check(
+ r#"
+fn foo() {
+ for in qux {
+
+ }
+}
+"#,
+ expect![[r#"
+fn foo () {__ra_fixup}
+"#]],
+ )
+ }
+
+ #[test]
+ fn match_no_expr_no_arms() {
+ check(
+ r#"
+fn foo() {
+ match
+}
+"#,
+ expect![[r#"
+fn foo () {match __ra_fixup {}}
+"#]],
+ )
+ }
+
+ #[test]
+ fn match_expr_no_arms() {
+ check(
+ r#"
+fn foo() {
+ match x {
+
+ }
+}
+"#,
+ expect![[r#"
+fn foo () {match x {}}
+"#]],
+ )
+ }
+
+ #[test]
+ fn match_no_expr() {
+ check(
+ r#"
+fn foo() {
+ match {
+ _ => {}
+ }
+}
+"#,
+ expect![[r#"
+fn foo () {match __ra_fixup {}}
+"#]],
+ )
+ }
+
+ #[test]
fn incomplete_field_expr_1() {
check(
r#"
@@ -245,7 +468,7 @@ fn foo() {
}
"#,
expect![[r#"
-fn foo () {a . __ra_fixup}
+fn foo () {a .__ra_fixup}
"#]],
)
}
@@ -255,11 +478,11 @@ fn foo () {a . __ra_fixup}
check(
r#"
fn foo() {
- a. ;
+ a.;
}
"#,
expect![[r#"
-fn foo () {a . __ra_fixup ;}
+fn foo () {a .__ra_fixup ;}
"#]],
)
}
@@ -269,12 +492,12 @@ fn foo () {a . __ra_fixup ;}
check(
r#"
fn foo() {
- a. ;
+ a.;
bar();
}
"#,
expect![[r#"
-fn foo () {a . __ra_fixup ; bar () ;}
+fn foo () {a .__ra_fixup ; bar () ;}
"#]],
)
}
@@ -302,7 +525,7 @@ fn foo() {
}
"#,
expect![[r#"
-fn foo () {let x = a . __ra_fixup ;}
+fn foo () {let x = a .__ra_fixup ;}
"#]],
)
}
@@ -318,7 +541,7 @@ fn foo() {
}
"#,
expect![[r#"
-fn foo () {a . b ; bar () ;}
+fn foo () {a .b ; bar () ;}
"#]],
)
}
@@ -379,4 +602,59 @@ fn foo () {if {} {}}
"#]],
)
}
+
+ #[test]
+ fn fixup_while_1() {
+ check(
+ r#"
+fn foo() {
+ while
+}
+"#,
+ expect![[r#"
+fn foo () {while __ra_fixup {}}
+"#]],
+ )
+ }
+
+ #[test]
+ fn fixup_while_2() {
+ check(
+ r#"
+fn foo() {
+ while foo
+}
+"#,
+ expect![[r#"
+fn foo () {while foo {}}
+"#]],
+ )
+ }
+ #[test]
+ fn fixup_while_3() {
+ check(
+ r#"
+fn foo() {
+ while {}
+}
+"#,
+ expect![[r#"
+fn foo () {while __ra_fixup {}}
+"#]],
+ )
+ }
+
+ #[test]
+ fn fixup_loop() {
+ check(
+ r#"
+fn foo() {
+ loop
+}
+"#,
+ expect![[r#"
+fn foo () {loop {}}
+"#]],
+ )
+ }
}
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
index 252293090..a5b499fe8 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
@@ -130,7 +130,6 @@ pub struct MacroDefId {
pub enum MacroDefKind {
Declarative(AstId<ast::Macro>),
BuiltIn(BuiltinFnLikeExpander, AstId<ast::Macro>),
- // FIXME: maybe just Builtin and rename BuiltinFnLikeExpander to BuiltinExpander
BuiltInAttr(BuiltinAttrExpander, AstId<ast::Macro>),
BuiltInDerive(BuiltinDeriveExpander, AstId<ast::Macro>),
BuiltInEager(EagerExpander, AstId<ast::Macro>),
@@ -617,7 +616,7 @@ impl ExpansionInfo {
let token_id = match token_id_in_attr_input {
Some(token_id) => token_id,
- // the token is not inside an attribute's input so do the lookup in the macro_arg as ususal
+ // the token is not inside an attribute's input so do the lookup in the macro_arg as usual
None => {
let relative_range =
token.value.text_range().checked_sub(self.arg.value.text_range().start())?;
@@ -812,6 +811,31 @@ impl<'a> InFile<&'a SyntaxNode> {
_ => None,
}
}
+
+ pub fn original_syntax_node(self, db: &dyn db::AstDatabase) -> Option<InFile<SyntaxNode>> {
+ // This kind of upmapping can only be achieved in attribute expanded files,
+ // as we don't have node inputs otherwise and therefor can't find an `N` node in the input
+ if !self.file_id.is_macro() {
+ return Some(self.map(Clone::clone));
+ } else if !self.file_id.is_attr_macro(db) {
+ return None;
+ }
+
+ if let Some(InFile { file_id, value: (first, last) }) = ascend_node_border_tokens(db, self)
+ {
+ if file_id.is_macro() {
+ let range = first.text_range().cover(last.text_range());
+ tracing::error!("Failed mapping out of macro file for {:?}", range);
+ return None;
+ }
+ // FIXME: This heuristic is brittle and with the right macro may select completely unrelated nodes
+ let anc = algo::least_common_ancestor(&first.parent()?, &last.parent()?)?;
+ let kind = self.value.kind();
+ let value = anc.ancestors().find(|it| it.kind() == kind)?;
+ return Some(InFile::new(file_id, value));
+ }
+ None
+ }
}
impl InFile<SyntaxToken> {
@@ -970,7 +994,7 @@ impl ExpandTo {
if parent.kind() == MACRO_EXPR
&& parent
.parent()
- .map_or(true, |p| matches!(p.kind(), EXPR_STMT | STMT_LIST | MACRO_STMTS))
+ .map_or(false, |p| matches!(p.kind(), EXPR_STMT | STMT_LIST | MACRO_STMTS))
{
return ExpandTo::Statements;
}
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs
index fea09521e..d7586d129 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs
@@ -22,7 +22,7 @@ pub struct ModPath {
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub struct EscapedModPath<'a>(&'a ModPath);
+pub struct UnescapedModPath<'a>(&'a ModPath);
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum PathKind {
@@ -102,8 +102,8 @@ impl ModPath {
}
}
- pub fn escaped(&self) -> EscapedModPath<'_> {
- EscapedModPath(self)
+ pub fn unescaped(&self) -> UnescapedModPath<'_> {
+ UnescapedModPath(self)
}
fn _fmt(&self, f: &mut fmt::Formatter<'_>, escaped: bool) -> fmt::Result {
@@ -134,9 +134,9 @@ impl ModPath {
}
first_segment = false;
if escaped {
- segment.escaped().fmt(f)?
- } else {
segment.fmt(f)?
+ } else {
+ segment.unescaped().fmt(f)?
};
}
Ok(())
@@ -145,13 +145,13 @@ impl ModPath {
impl Display for ModPath {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- self._fmt(f, false)
+ self._fmt(f, true)
}
}
-impl<'a> Display for EscapedModPath<'a> {
+impl<'a> Display for UnescapedModPath<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- self.0._fmt(f, true)
+ self.0._fmt(f, false)
}
}
@@ -257,6 +257,7 @@ macro_rules! __known_path {
(core::ops::RangeToInclusive) => {};
(core::ops::RangeInclusive) => {};
(core::future::Future) => {};
+ (core::future::IntoFuture) => {};
(core::ops::Try) => {};
($path:path) => {
compile_error!("Please register your known path in the path module")
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs
index 85b0a7735..2679a1c36 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs
@@ -7,12 +7,16 @@ use syntax::{ast, SmolStr, SyntaxKind};
/// `Name` is a wrapper around string, which is used in hir for both references
/// and declarations. In theory, names should also carry hygiene info, but we are
/// not there yet!
+///
+/// Note that `Name` holds and prints escaped name i.e. prefixed with "r#" when it
+/// is a raw identifier. Use [`unescaped()`][Name::unescaped] when you need the
+/// name without "r#".
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Name(Repr);
-/// `EscapedName` will add a prefix "r#" to the wrapped `Name` when it is a raw identifier
+/// Wrapper of `Name` to print the name without "r#" even when it is a raw identifier.
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
-pub struct EscapedName<'a>(&'a Name);
+pub struct UnescapedName<'a>(&'a Name);
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
enum Repr {
@@ -34,37 +38,26 @@ fn is_raw_identifier(name: &str) -> bool {
is_keyword && !matches!(name, "self" | "crate" | "super" | "Self")
}
-impl<'a> fmt::Display for EscapedName<'a> {
+impl<'a> fmt::Display for UnescapedName<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.0 .0 {
Repr::Text(text) => {
- if is_raw_identifier(text) {
- write!(f, "r#{}", &text)
- } else {
- fmt::Display::fmt(&text, f)
- }
+ let text = text.strip_prefix("r#").unwrap_or(text);
+ fmt::Display::fmt(&text, f)
}
Repr::TupleField(idx) => fmt::Display::fmt(&idx, f),
}
}
}
-impl<'a> EscapedName<'a> {
- pub fn is_escaped(&self) -> bool {
- match &self.0 .0 {
- Repr::Text(it) => is_raw_identifier(&it),
- Repr::TupleField(_) => false,
- }
- }
-
- /// Returns the textual representation of this name as a [`SmolStr`].
- /// Prefer using this over [`ToString::to_string`] if possible as this conversion is cheaper in
- /// the general case.
+impl<'a> UnescapedName<'a> {
+ /// Returns the textual representation of this name as a [`SmolStr`]. Prefer using this over
+ /// [`ToString::to_string`] if possible as this conversion is cheaper in the general case.
pub fn to_smol_str(&self) -> SmolStr {
match &self.0 .0 {
Repr::Text(it) => {
- if is_raw_identifier(&it) {
- SmolStr::from_iter(["r#", &it])
+ if let Some(stripped) = it.strip_prefix("r#") {
+ SmolStr::new(stripped)
} else {
it.clone()
}
@@ -98,8 +91,16 @@ impl Name {
/// Resolve a name from the text of token.
fn resolve(raw_text: &str) -> Name {
match raw_text.strip_prefix("r#") {
- Some(text) => Name::new_text(SmolStr::new(text)),
- None => Name::new_text(raw_text.into()),
+ // When `raw_text` starts with "r#" but the name does not coincide with any
+ // keyword, we never need the prefix so we strip it.
+ Some(text) if !is_raw_identifier(text) => Name::new_text(SmolStr::new(text)),
+ // Keywords (in the current edition) *can* be used as a name in earlier editions of
+ // Rust, e.g. "try" in Rust 2015. Even in such cases, we keep track of them in their
+ // escaped form.
+ None if is_raw_identifier(raw_text) => {
+ Name::new_text(SmolStr::from_iter(["r#", raw_text]))
+ }
+ _ => Name::new_text(raw_text.into()),
}
}
@@ -142,8 +143,15 @@ impl Name {
}
}
- pub fn escaped(&self) -> EscapedName<'_> {
- EscapedName(self)
+ pub fn unescaped(&self) -> UnescapedName<'_> {
+ UnescapedName(self)
+ }
+
+ pub fn is_escaped(&self) -> bool {
+ match &self.0 {
+ Repr::Text(it) => it.starts_with("r#"),
+ Repr::TupleField(_) => false,
+ }
}
}
@@ -255,9 +263,11 @@ pub mod known {
Iterator,
IntoIterator,
Item,
+ IntoIter,
Try,
Ok,
Future,
+ IntoFuture,
Result,
Option,
Output,
@@ -327,6 +337,7 @@ pub mod known {
test,
test_case,
recursion_limit,
+ feature,
// Safe intrinsics
abort,
add_with_overflow,
@@ -381,6 +392,7 @@ pub mod known {
bitor,
bitxor_assign,
bitxor,
+ branch,
deref_mut,
deref,
div_assign,
@@ -390,12 +402,14 @@ pub mod known {
future_trait,
index,
index_mut,
+ into_future,
mul_assign,
mul,
neg,
not,
owned_box,
partial_ord,
+ poll,
r#fn,
rem_assign,
rem,
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/quote.rs b/src/tools/rust-analyzer/crates/hir-expand/src/quote.rs
index 82f410ecd..e839e97bf 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/quote.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/quote.rs
@@ -196,8 +196,8 @@ impl_to_to_tokentrees! {
tt::Literal => self { self };
tt::Ident => self { self };
tt::Punct => self { self };
- &str => self { tt::Literal{text: format!("\"{}\"", self.escape_debug()).into(), id: tt::TokenId::unspecified()}};
- String => self { tt::Literal{text: format!("\"{}\"", self.escape_debug()).into(), id: tt::TokenId::unspecified()}}
+ &str => self { tt::Literal{text: format!("\"{}\"", self.escape_default()).into(), id: tt::TokenId::unspecified()}};
+ String => self { tt::Literal{text: format!("\"{}\"", self.escape_default()).into(), id: tt::TokenId::unspecified()}}
}
#[cfg(test)]