Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-expand/src/quote.rs')
-rw-r--r--crates/hir-expand/src/quote.rs170
1 files changed, 96 insertions, 74 deletions
diff --git a/crates/hir-expand/src/quote.rs b/crates/hir-expand/src/quote.rs
index ab3809abc7..acbde26c8d 100644
--- a/crates/hir-expand/src/quote.rs
+++ b/crates/hir-expand/src/quote.rs
@@ -1,5 +1,7 @@
//! A simplified version of quote-crate like quasi quote macro
+use base_db::span::SpanData;
+
// A helper macro quote macro
// FIXME:
// 1. Not all puncts are handled
@@ -8,109 +10,109 @@
#[doc(hidden)]
#[macro_export]
macro_rules! __quote {
- () => {
+ ($span:ident) => {
Vec::<crate::tt::TokenTree>::new()
};
- ( @SUBTREE $delim:ident $($tt:tt)* ) => {
+ ( @SUBTREE($span:ident) $delim:ident $($tt:tt)* ) => {
{
- let children = $crate::__quote!($($tt)*);
+ let children = $crate::__quote!($span $($tt)*);
crate::tt::Subtree {
delimiter: crate::tt::Delimiter {
kind: crate::tt::DelimiterKind::$delim,
- open: crate::tt::TokenId::unspecified(),
- close: crate::tt::TokenId::unspecified(),
+ open: $span,
+ close: $span,
},
token_trees: $crate::quote::IntoTt::to_tokens(children),
}
}
};
- ( @PUNCT $first:literal ) => {
+ ( @PUNCT($span:ident) $first:literal ) => {
{
vec![
crate::tt::Leaf::Punct(crate::tt::Punct {
char: $first,
spacing: crate::tt::Spacing::Alone,
- span: crate::tt::TokenId::unspecified(),
+ span: $span,
}).into()
]
}
};
- ( @PUNCT $first:literal, $sec:literal ) => {
+ ( @PUNCT($span:ident) $first:literal, $sec:literal ) => {
{
vec![
crate::tt::Leaf::Punct(crate::tt::Punct {
char: $first,
spacing: crate::tt::Spacing::Joint,
- span: crate::tt::TokenId::unspecified(),
+ span: $span,
}).into(),
crate::tt::Leaf::Punct(crate::tt::Punct {
char: $sec,
spacing: crate::tt::Spacing::Alone,
- span: crate::tt::TokenId::unspecified(),
+ span: $span,
}).into()
]
}
};
// hash variable
- ( # $first:ident $($tail:tt)* ) => {
+ ($span:ident # $first:ident $($tail:tt)* ) => {
{
- let token = $crate::quote::ToTokenTree::to_token($first);
+ let token = $crate::quote::ToTokenTree::to_token($first, $span);
let mut tokens = vec![token.into()];
- let mut tail_tokens = $crate::quote::IntoTt::to_tokens($crate::__quote!($($tail)*));
+ let mut tail_tokens = $crate::quote::IntoTt::to_tokens($crate::__quote!($span $($tail)*));
tokens.append(&mut tail_tokens);
tokens
}
};
- ( ## $first:ident $($tail:tt)* ) => {
+ ($span:ident ## $first:ident $($tail:tt)* ) => {
{
- let mut tokens = $first.into_iter().map($crate::quote::ToTokenTree::to_token).collect::<Vec<crate::tt::TokenTree>>();
- let mut tail_tokens = $crate::quote::IntoTt::to_tokens($crate::__quote!($($tail)*));
+ let mut tokens = $first.into_iter().map(|it| $crate::quote::ToTokenTree::to_token(it, $span)).collect::<Vec<crate::tt::TokenTree>>();
+ let mut tail_tokens = $crate::quote::IntoTt::to_tokens($crate::__quote!($span $($tail)*));
tokens.append(&mut tail_tokens);
tokens
}
};
// Brace
- ( { $($tt:tt)* } ) => { $crate::__quote!(@SUBTREE Brace $($tt)*) };
+ ($span:ident { $($tt:tt)* } ) => { $crate::__quote!(@SUBTREE($span) Brace $($tt)*) };
// Bracket
- ( [ $($tt:tt)* ] ) => { $crate::__quote!(@SUBTREE Bracket $($tt)*) };
+ ($span:ident [ $($tt:tt)* ] ) => { $crate::__quote!(@SUBTREE($span) Bracket $($tt)*) };
// Parenthesis
- ( ( $($tt:tt)* ) ) => { $crate::__quote!(@SUBTREE Parenthesis $($tt)*) };
+ ($span:ident ( $($tt:tt)* ) ) => { $crate::__quote!(@SUBTREE($span) Parenthesis $($tt)*) };
// Literal
- ( $tt:literal ) => { vec![$crate::quote::ToTokenTree::to_token($tt).into()] };
+ ($span:ident $tt:literal ) => { vec![$crate::quote::ToTokenTree::to_token($tt, $span).into()] };
// Ident
- ( $tt:ident ) => {
+ ($span:ident $tt:ident ) => {
vec![ {
crate::tt::Leaf::Ident(crate::tt::Ident {
text: stringify!($tt).into(),
- span: crate::tt::TokenId::unspecified(),
+ span: $span,
}).into()
}]
};
// Puncts
// FIXME: Not all puncts are handled
- ( -> ) => {$crate::__quote!(@PUNCT '-', '>')};
- ( & ) => {$crate::__quote!(@PUNCT '&')};
- ( , ) => {$crate::__quote!(@PUNCT ',')};
- ( : ) => {$crate::__quote!(@PUNCT ':')};
- ( ; ) => {$crate::__quote!(@PUNCT ';')};
- ( :: ) => {$crate::__quote!(@PUNCT ':', ':')};
- ( . ) => {$crate::__quote!(@PUNCT '.')};
- ( < ) => {$crate::__quote!(@PUNCT '<')};
- ( > ) => {$crate::__quote!(@PUNCT '>')};
- ( ! ) => {$crate::__quote!(@PUNCT '!')};
-
- ( $first:tt $($tail:tt)+ ) => {
+ ($span:ident -> ) => {$crate::__quote!(@PUNCT($span) '-', '>')};
+ ($span:ident & ) => {$crate::__quote!(@PUNCT($span) '&')};
+ ($span:ident , ) => {$crate::__quote!(@PUNCT($span) ',')};
+ ($span:ident : ) => {$crate::__quote!(@PUNCT($span) ':')};
+ ($span:ident ; ) => {$crate::__quote!(@PUNCT($span) ';')};
+ ($span:ident :: ) => {$crate::__quote!(@PUNCT($span) ':', ':')};
+ ($span:ident . ) => {$crate::__quote!(@PUNCT($span) '.')};
+ ($span:ident < ) => {$crate::__quote!(@PUNCT($span) '<')};
+ ($span:ident > ) => {$crate::__quote!(@PUNCT($span) '>')};
+ ($span:ident ! ) => {$crate::__quote!(@PUNCT($span) '!')};
+
+ ($span:ident $first:tt $($tail:tt)+ ) => {
{
- let mut tokens = $crate::quote::IntoTt::to_tokens($crate::__quote!($first));
- let mut tail_tokens = $crate::quote::IntoTt::to_tokens($crate::__quote!($($tail)*));
+ let mut tokens = $crate::quote::IntoTt::to_tokens($crate::__quote!($span $first ));
+ let mut tail_tokens = $crate::quote::IntoTt::to_tokens($crate::__quote!($span $($tail)*));
tokens.append(&mut tail_tokens);
tokens
@@ -122,19 +124,22 @@ macro_rules! __quote {
/// It probably should implement in proc-macro
#[macro_export]
macro_rules! quote {
- ( $($tt:tt)* ) => {
- $crate::quote::IntoTt::to_subtree($crate::__quote!($($tt)*))
+ ($span:ident=> $($tt:tt)* ) => {
+ $crate::quote::IntoTt::to_subtree($crate::__quote!($span $($tt)*), $span)
}
}
pub(crate) trait IntoTt {
- fn to_subtree(self) -> crate::tt::Subtree;
+ fn to_subtree(self, span: SpanData) -> crate::tt::Subtree;
fn to_tokens(self) -> Vec<crate::tt::TokenTree>;
}
impl IntoTt for Vec<crate::tt::TokenTree> {
- fn to_subtree(self) -> crate::tt::Subtree {
- crate::tt::Subtree { delimiter: crate::tt::Delimiter::unspecified(), token_trees: self }
+ fn to_subtree(self, span: SpanData) -> crate::tt::Subtree {
+ crate::tt::Subtree {
+ delimiter: crate::tt::Delimiter::invisible_spanned(span),
+ token_trees: self,
+ }
}
fn to_tokens(self) -> Vec<crate::tt::TokenTree> {
@@ -143,7 +148,7 @@ impl IntoTt for Vec<crate::tt::TokenTree> {
}
impl IntoTt for crate::tt::Subtree {
- fn to_subtree(self) -> crate::tt::Subtree {
+ fn to_subtree(self, _: SpanData) -> crate::tt::Subtree {
self
}
@@ -153,39 +158,39 @@ impl IntoTt for crate::tt::Subtree {
}
pub(crate) trait ToTokenTree {
- fn to_token(self) -> crate::tt::TokenTree;
+ fn to_token(self, span: SpanData) -> crate::tt::TokenTree;
}
impl ToTokenTree for crate::tt::TokenTree {
- fn to_token(self) -> crate::tt::TokenTree {
+ fn to_token(self, _: SpanData) -> crate::tt::TokenTree {
self
}
}
impl ToTokenTree for &crate::tt::TokenTree {
- fn to_token(self) -> crate::tt::TokenTree {
+ fn to_token(self, _: SpanData) -> crate::tt::TokenTree {
self.clone()
}
}
impl ToTokenTree for crate::tt::Subtree {
- fn to_token(self) -> crate::tt::TokenTree {
+ fn to_token(self, _: SpanData) -> crate::tt::TokenTree {
self.into()
}
}
macro_rules! impl_to_to_tokentrees {
- ($($ty:ty => $this:ident $im:block);*) => {
+ ($($span:ident: $ty:ty => $this:ident $im:block);*) => {
$(
impl ToTokenTree for $ty {
- fn to_token($this) -> crate::tt::TokenTree {
+ fn to_token($this, $span: SpanData) -> crate::tt::TokenTree {
let leaf: crate::tt::Leaf = $im.into();
leaf.into()
}
}
impl ToTokenTree for &$ty {
- fn to_token($this) -> crate::tt::TokenTree {
+ fn to_token($this, $span: SpanData) -> crate::tt::TokenTree {
let leaf: crate::tt::Leaf = $im.clone().into();
leaf.into()
}
@@ -195,60 +200,76 @@ macro_rules! impl_to_to_tokentrees {
}
impl_to_to_tokentrees! {
- u32 => self { crate::tt::Literal{text: self.to_string().into(), span: crate::tt::TokenId::unspecified()} };
- usize => self { crate::tt::Literal{text: self.to_string().into(), span: crate::tt::TokenId::unspecified()} };
- i32 => self { crate::tt::Literal{text: self.to_string().into(), span: crate::tt::TokenId::unspecified()} };
- bool => self { crate::tt::Ident{text: self.to_string().into(), span: crate::tt::TokenId::unspecified()} };
- crate::tt::Leaf => self { self };
- crate::tt::Literal => self { self };
- crate::tt::Ident => self { self };
- crate::tt::Punct => self { self };
- &str => self { crate::tt::Literal{text: format!("\"{}\"", self.escape_default()).into(), span: crate::tt::TokenId::unspecified()}};
- String => self { crate::tt::Literal{text: format!("\"{}\"", self.escape_default()).into(), span: crate::tt::TokenId::unspecified()}}
+ span: u32 => self { crate::tt::Literal{text: self.to_string().into(), span} };
+ span: usize => self { crate::tt::Literal{text: self.to_string().into(), span} };
+ span: i32 => self { crate::tt::Literal{text: self.to_string().into(), span} };
+ span: bool => self { crate::tt::Ident{text: self.to_string().into(), span} };
+ _span: crate::tt::Leaf => self { self };
+ _span: crate::tt::Literal => self { self };
+ _span: crate::tt::Ident => self { self };
+ _span: crate::tt::Punct => self { self };
+ span: &str => self { crate::tt::Literal{text: format!("\"{}\"", self.escape_default()).into(), span}};
+ span: String => self { crate::tt::Literal{text: format!("\"{}\"", self.escape_default()).into(), span}}
}
#[cfg(test)]
mod tests {
+ use crate::tt;
+ use base_db::{
+ span::{SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID},
+ FileId,
+ };
+ use expect_test::expect;
+ use syntax::{TextRange, TextSize};
+
+ const DUMMY: tt::SpanData = tt::SpanData {
+ range: TextRange::empty(TextSize::new(0)),
+ anchor: SpanAnchor { file_id: FileId::BOGUS, ast_id: ROOT_ERASED_FILE_AST_ID },
+ ctx: SyntaxContextId::ROOT,
+ };
+
#[test]
fn test_quote_delimiters() {
- assert_eq!(quote!({}).to_string(), "{}");
- assert_eq!(quote!(()).to_string(), "()");
- assert_eq!(quote!([]).to_string(), "[]");
+ assert_eq!(quote!(DUMMY =>{}).to_string(), "{}");
+ assert_eq!(quote!(DUMMY =>()).to_string(), "()");
+ assert_eq!(quote!(DUMMY =>[]).to_string(), "[]");
}
#[test]
fn test_quote_idents() {
- assert_eq!(quote!(32).to_string(), "32");
- assert_eq!(quote!(struct).to_string(), "struct");
+ assert_eq!(quote!(DUMMY =>32).to_string(), "32");
+ assert_eq!(quote!(DUMMY =>struct).to_string(), "struct");
}
#[test]
fn test_quote_hash_simple_literal() {
let a = 20;
- assert_eq!(quote!(#a).to_string(), "20");
+ assert_eq!(quote!(DUMMY =>#a).to_string(), "20");
let s: String = "hello".into();
- assert_eq!(quote!(#s).to_string(), "\"hello\"");
+ assert_eq!(quote!(DUMMY =>#s).to_string(), "\"hello\"");
}
fn mk_ident(name: &str) -> crate::tt::Ident {
- crate::tt::Ident { text: name.into(), span: crate::tt::TokenId::unspecified() }
+ crate::tt::Ident { text: name.into(), span: DUMMY }
}
#[test]
fn test_quote_hash_token_tree() {
let a = mk_ident("hello");
- let quoted = quote!(#a);
+ let quoted = quote!(DUMMY =>#a);
assert_eq!(quoted.to_string(), "hello");
let t = format!("{quoted:?}");
- assert_eq!(t, "SUBTREE $$ 4294967295 4294967295\n IDENT hello 4294967295");
+ expect![[r#"
+ SUBTREE $$ SpanData { range: 0..0, anchor: SpanAnchor(FileId(937550), 0), ctx: SyntaxContextId(0) } SpanData { range: 0..0, anchor: SpanAnchor(FileId(937550), 0), ctx: SyntaxContextId(0) }
+ IDENT hello SpanData { range: 0..0, anchor: SpanAnchor(FileId(937550), 0), ctx: SyntaxContextId(0) }"#]].assert_eq(&t);
}
#[test]
fn test_quote_simple_derive_copy() {
let name = mk_ident("Foo");
- let quoted = quote! {
+ let quoted = quote! {DUMMY =>
impl Clone for #name {
fn clone(&self) -> Self {
Self {}
@@ -268,18 +289,19 @@ mod tests {
// }
let struct_name = mk_ident("Foo");
let fields = [mk_ident("name"), mk_ident("id")];
- let fields = fields.iter().flat_map(|it| quote!(#it: self.#it.clone(), ).token_trees);
+ let fields =
+ fields.iter().flat_map(|it| quote!(DUMMY =>#it: self.#it.clone(), ).token_trees);
let list = crate::tt::Subtree {
delimiter: crate::tt::Delimiter {
kind: crate::tt::DelimiterKind::Brace,
- open: crate::tt::TokenId::unspecified(),
- close: crate::tt::TokenId::unspecified(),
+ open: DUMMY,
+ close: DUMMY,
},
token_trees: fields.collect(),
};
- let quoted = quote! {
+ let quoted = quote! {DUMMY =>
impl Clone for #struct_name {
fn clone(&self) -> Self {
Self #list