mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-01 22:31:43 +00:00
Pass calling span through to builtin macro expansions
This commit is contained in:
parent
f48fa0c6cb
commit
0003e568ca
27 changed files with 623 additions and 497 deletions
|
@ -130,7 +130,7 @@ impl RawAttrs {
|
|||
let attrs = parts.enumerate().take(1 << AttrId::CFG_ATTR_BITS).filter_map(
|
||||
|(idx, attr)| {
|
||||
let tree = Subtree {
|
||||
delimiter: tt::Delimiter::unspecified(),
|
||||
delimiter: tt::Delimiter::dummy_invisible(),
|
||||
token_trees: attr.to_vec(),
|
||||
};
|
||||
Attr::from_tt(db, &tree, index.with_cfg_attr(idx))
|
||||
|
@ -296,7 +296,7 @@ impl Attr {
|
|||
// FIXME: This is necessarily a hack. It'd be nice if we could avoid allocation
|
||||
// here or maybe just parse a mod path from a token tree directly
|
||||
let subtree = tt::Subtree {
|
||||
delimiter: tt::Delimiter::unspecified(),
|
||||
delimiter: tt::Delimiter::dummy_invisible(),
|
||||
token_trees: tts.to_vec(),
|
||||
};
|
||||
let (parse, span_map) =
|
||||
|
|
|
@ -1,18 +1,22 @@
|
|||
//! Builtin attributes.
|
||||
|
||||
use ::tt::Span;
|
||||
use base_db::{
|
||||
span::{SyntaxContextId, ROOT_ERASED_FILE_AST_ID},
|
||||
FileId,
|
||||
};
|
||||
use syntax::{TextRange, TextSize};
|
||||
|
||||
use crate::{db::ExpandDatabase, name, tt, ExpandResult, MacroCallId, MacroCallKind};
|
||||
|
||||
macro_rules! register_builtin {
|
||||
( $(($name:ident, $variant:ident) => $expand:ident),* ) => {
|
||||
($expand_fn:ident: $(($name:ident, $variant:ident) => $expand:ident),* ) => {
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum BuiltinAttrExpander {
|
||||
$($variant),*
|
||||
}
|
||||
|
||||
impl BuiltinAttrExpander {
|
||||
pub fn expand(
|
||||
pub fn $expand_fn(
|
||||
&self,
|
||||
db: &dyn ExpandDatabase,
|
||||
id: MacroCallId,
|
||||
|
@ -47,7 +51,7 @@ impl BuiltinAttrExpander {
|
|||
}
|
||||
}
|
||||
|
||||
register_builtin! {
|
||||
register_builtin! { expand:
|
||||
(bench, Bench) => dummy_attr_expand,
|
||||
(cfg_accessible, CfgAccessible) => dummy_attr_expand,
|
||||
(cfg_eval, CfgEval) => dummy_attr_expand,
|
||||
|
@ -99,21 +103,31 @@ fn derive_attr_expand(
|
|||
) -> ExpandResult<tt::Subtree> {
|
||||
let loc = db.lookup_intern_macro_call(id);
|
||||
let derives = match &loc.kind {
|
||||
MacroCallKind::Attr { attr_args, .. } if loc.def.is_attribute_derive() => attr_args,
|
||||
_ => return ExpandResult::ok(tt::Subtree::empty()),
|
||||
MacroCallKind::Attr { attr_args: Some(attr_args), .. } if loc.def.is_attribute_derive() => {
|
||||
attr_args
|
||||
}
|
||||
_ => return ExpandResult::ok(tt::Subtree::empty(tt::DelimSpan::DUMMY)),
|
||||
};
|
||||
pseudo_derive_attr_expansion(tt, derives)
|
||||
pseudo_derive_attr_expansion(tt, derives, loc.call_site)
|
||||
}
|
||||
|
||||
pub fn pseudo_derive_attr_expansion(
|
||||
tt: &tt::Subtree,
|
||||
args: &tt::Subtree,
|
||||
call_site: SyntaxContextId,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let mk_leaf = |char| {
|
||||
tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct {
|
||||
char,
|
||||
spacing: tt::Spacing::Alone,
|
||||
span: tt::SpanData::DUMMY,
|
||||
span: tt::SpanData {
|
||||
range: TextRange::empty(TextSize::new(0)),
|
||||
anchor: base_db::span::SpanAnchor {
|
||||
file_id: FileId::BOGUS,
|
||||
ast_id: ROOT_ERASED_FILE_AST_ID,
|
||||
},
|
||||
ctx: call_site,
|
||||
},
|
||||
}))
|
||||
};
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
//! Builtin derives.
|
||||
|
||||
use ::tt::Span;
|
||||
use base_db::{span::SpanData, CrateOrigin, LangCrateOrigin};
|
||||
use itertools::izip;
|
||||
use rustc_hash::FxHashSet;
|
||||
|
@ -74,19 +73,19 @@ enum VariantShape {
|
|||
Unit,
|
||||
}
|
||||
|
||||
fn tuple_field_iterator(n: usize) -> impl Iterator<Item = tt::Ident> {
|
||||
(0..n).map(|it| tt::Ident::new(format!("f{it}"), tt::SpanData::DUMMY))
|
||||
fn tuple_field_iterator(span: SpanData, n: usize) -> impl Iterator<Item = tt::Ident> {
|
||||
(0..n).map(move |it| tt::Ident::new(format!("f{it}"), span))
|
||||
}
|
||||
|
||||
impl VariantShape {
|
||||
fn as_pattern(&self, path: tt::Subtree) -> tt::Subtree {
|
||||
self.as_pattern_map(path, |it| quote!(#it))
|
||||
fn as_pattern(&self, path: tt::Subtree, span: SpanData) -> tt::Subtree {
|
||||
self.as_pattern_map(path, span, |it| quote!(span => #it))
|
||||
}
|
||||
|
||||
fn field_names(&self) -> Vec<tt::Ident> {
|
||||
fn field_names(&self, span: SpanData) -> Vec<tt::Ident> {
|
||||
match self {
|
||||
VariantShape::Struct(s) => s.clone(),
|
||||
VariantShape::Tuple(n) => tuple_field_iterator(*n).collect(),
|
||||
VariantShape::Tuple(n) => tuple_field_iterator(span, *n).collect(),
|
||||
VariantShape::Unit => vec![],
|
||||
}
|
||||
}
|
||||
|
@ -94,26 +93,27 @@ impl VariantShape {
|
|||
fn as_pattern_map(
|
||||
&self,
|
||||
path: tt::Subtree,
|
||||
span: SpanData,
|
||||
field_map: impl Fn(&tt::Ident) -> tt::Subtree,
|
||||
) -> tt::Subtree {
|
||||
match self {
|
||||
VariantShape::Struct(fields) => {
|
||||
let fields = fields.iter().map(|it| {
|
||||
let mapped = field_map(it);
|
||||
quote! { #it : #mapped , }
|
||||
quote! {span => #it : #mapped , }
|
||||
});
|
||||
quote! {
|
||||
quote! {span =>
|
||||
#path { ##fields }
|
||||
}
|
||||
}
|
||||
&VariantShape::Tuple(n) => {
|
||||
let fields = tuple_field_iterator(n).map(|it| {
|
||||
let fields = tuple_field_iterator(span, n).map(|it| {
|
||||
let mapped = field_map(&it);
|
||||
quote! {
|
||||
quote! {span =>
|
||||
#mapped ,
|
||||
}
|
||||
});
|
||||
quote! {
|
||||
quote! {span =>
|
||||
#path ( ##fields )
|
||||
}
|
||||
}
|
||||
|
@ -143,17 +143,17 @@ enum AdtShape {
|
|||
}
|
||||
|
||||
impl AdtShape {
|
||||
fn as_pattern(&self, name: &tt::Ident) -> Vec<tt::Subtree> {
|
||||
self.as_pattern_map(name, |it| quote!(#it))
|
||||
fn as_pattern(&self, span: SpanData, name: &tt::Ident) -> Vec<tt::Subtree> {
|
||||
self.as_pattern_map(name, |it| quote!(span =>#it), span)
|
||||
}
|
||||
|
||||
fn field_names(&self) -> Vec<Vec<tt::Ident>> {
|
||||
fn field_names(&self, span: SpanData) -> Vec<Vec<tt::Ident>> {
|
||||
match self {
|
||||
AdtShape::Struct(s) => {
|
||||
vec![s.field_names()]
|
||||
vec![s.field_names(span)]
|
||||
}
|
||||
AdtShape::Enum { variants, .. } => {
|
||||
variants.iter().map(|(_, fields)| fields.field_names()).collect()
|
||||
variants.iter().map(|(_, fields)| fields.field_names(span)).collect()
|
||||
}
|
||||
AdtShape::Union => {
|
||||
never!("using fields of union in derive is always wrong");
|
||||
|
@ -166,18 +166,21 @@ impl AdtShape {
|
|||
&self,
|
||||
name: &tt::Ident,
|
||||
field_map: impl Fn(&tt::Ident) -> tt::Subtree,
|
||||
span: SpanData,
|
||||
) -> Vec<tt::Subtree> {
|
||||
match self {
|
||||
AdtShape::Struct(s) => {
|
||||
vec![s.as_pattern_map(quote! { #name }, field_map)]
|
||||
vec![s.as_pattern_map(quote! {span => #name }, span, field_map)]
|
||||
}
|
||||
AdtShape::Enum { variants, .. } => variants
|
||||
.iter()
|
||||
.map(|(v, fields)| fields.as_pattern_map(quote! { #name :: #v }, &field_map))
|
||||
.map(|(v, fields)| {
|
||||
fields.as_pattern_map(quote! {span => #name :: #v }, span, &field_map)
|
||||
})
|
||||
.collect(),
|
||||
AdtShape::Union => {
|
||||
never!("pattern matching on union is always wrong");
|
||||
vec![quote! { un }]
|
||||
vec![quote! {span => un }]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -193,7 +196,11 @@ struct BasicAdtInfo {
|
|||
associated_types: Vec<tt::Subtree>,
|
||||
}
|
||||
|
||||
fn parse_adt(tm: SpanMapRef<'_>, adt: &ast::Adt) -> Result<BasicAdtInfo, ExpandError> {
|
||||
fn parse_adt(
|
||||
tm: SpanMapRef<'_>,
|
||||
adt: &ast::Adt,
|
||||
call_site: SpanData,
|
||||
) -> Result<BasicAdtInfo, ExpandError> {
|
||||
let (name, generic_param_list, shape) = match adt {
|
||||
ast::Adt::Struct(it) => (
|
||||
it.name(),
|
||||
|
@ -240,7 +247,9 @@ fn parse_adt(tm: SpanMapRef<'_>, adt: &ast::Adt) -> Result<BasicAdtInfo, ExpandE
|
|||
param_type_set.insert(it.as_name());
|
||||
mbe::syntax_node_to_token_tree(it.syntax(), tm)
|
||||
}
|
||||
None => tt::Subtree::empty(),
|
||||
None => {
|
||||
tt::Subtree::empty(::tt::DelimSpan { open: call_site, close: call_site })
|
||||
}
|
||||
}
|
||||
};
|
||||
let bounds = match ¶m {
|
||||
|
@ -253,7 +262,9 @@ fn parse_adt(tm: SpanMapRef<'_>, adt: &ast::Adt) -> Result<BasicAdtInfo, ExpandE
|
|||
let ty = param
|
||||
.ty()
|
||||
.map(|ty| mbe::syntax_node_to_token_tree(ty.syntax(), tm))
|
||||
.unwrap_or_else(tt::Subtree::empty);
|
||||
.unwrap_or_else(|| {
|
||||
tt::Subtree::empty(::tt::DelimSpan { open: call_site, close: call_site })
|
||||
});
|
||||
Some(ty)
|
||||
} else {
|
||||
None
|
||||
|
@ -338,15 +349,20 @@ fn name_to_token(
|
|||
/// therefore does not get bound by the derived trait.
|
||||
fn expand_simple_derive(
|
||||
// FIXME: use
|
||||
_invoc_span: SpanData,
|
||||
invoc_span: SpanData,
|
||||
tt: &ast::Adt,
|
||||
tm: SpanMapRef<'_>,
|
||||
trait_path: tt::Subtree,
|
||||
make_trait_body: impl FnOnce(&BasicAdtInfo) -> tt::Subtree,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let info = match parse_adt(tm, tt) {
|
||||
let info = match parse_adt(tm, tt, invoc_span) {
|
||||
Ok(info) => info,
|
||||
Err(e) => return ExpandResult::new(tt::Subtree::empty(), e),
|
||||
Err(e) => {
|
||||
return ExpandResult::new(
|
||||
tt::Subtree::empty(tt::DelimSpan { open: invoc_span, close: invoc_span }),
|
||||
e,
|
||||
)
|
||||
}
|
||||
};
|
||||
let trait_body = make_trait_body(&info);
|
||||
let mut where_block = vec![];
|
||||
|
@ -357,13 +373,13 @@ fn expand_simple_derive(
|
|||
let ident_ = ident.clone();
|
||||
if let Some(b) = bound {
|
||||
let ident = ident.clone();
|
||||
where_block.push(quote! { #ident : #b , });
|
||||
where_block.push(quote! {invoc_span => #ident : #b , });
|
||||
}
|
||||
if let Some(ty) = param_ty {
|
||||
(quote! { const #ident : #ty , }, quote! { #ident_ , })
|
||||
(quote! {invoc_span => const #ident : #ty , }, quote! {invoc_span => #ident_ , })
|
||||
} else {
|
||||
let bound = trait_path.clone();
|
||||
(quote! { #ident : #bound , }, quote! { #ident_ , })
|
||||
(quote! {invoc_span => #ident : #bound , }, quote! {invoc_span => #ident_ , })
|
||||
}
|
||||
})
|
||||
.unzip();
|
||||
|
@ -371,17 +387,17 @@ fn expand_simple_derive(
|
|||
where_block.extend(info.associated_types.iter().map(|it| {
|
||||
let it = it.clone();
|
||||
let bound = trait_path.clone();
|
||||
quote! { #it : #bound , }
|
||||
quote! {invoc_span => #it : #bound , }
|
||||
}));
|
||||
|
||||
let name = info.name;
|
||||
let expanded = quote! {
|
||||
let expanded = quote! {invoc_span =>
|
||||
impl < ##params > #trait_path for #name < ##args > where ##where_block { #trait_body }
|
||||
};
|
||||
ExpandResult::ok(expanded)
|
||||
}
|
||||
|
||||
fn find_builtin_crate(db: &dyn ExpandDatabase, id: MacroCallId) -> tt::TokenTree {
|
||||
fn find_builtin_crate(db: &dyn ExpandDatabase, id: MacroCallId, span: SpanData) -> tt::TokenTree {
|
||||
// FIXME: make hygiene works for builtin derive macro
|
||||
// such that $crate can be used here.
|
||||
let cg = db.crate_graph();
|
||||
|
@ -389,9 +405,9 @@ fn find_builtin_crate(db: &dyn ExpandDatabase, id: MacroCallId) -> tt::TokenTree
|
|||
|
||||
let tt = if matches!(cg[krate].origin, CrateOrigin::Lang(LangCrateOrigin::Core)) {
|
||||
cov_mark::hit!(test_copy_expand_in_core);
|
||||
quote! { crate }
|
||||
quote! {span => crate }
|
||||
} else {
|
||||
quote! { core }
|
||||
quote! {span => core }
|
||||
};
|
||||
|
||||
tt.token_trees[0].clone()
|
||||
|
@ -404,8 +420,8 @@ fn copy_expand(
|
|||
tt: &ast::Adt,
|
||||
tm: SpanMapRef<'_>,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let krate = find_builtin_crate(db, id);
|
||||
expand_simple_derive(span, tt, tm, quote! { #krate::marker::Copy }, |_| quote! {})
|
||||
let krate = find_builtin_crate(db, id, span);
|
||||
expand_simple_derive(span, tt, tm, quote! {span => #krate::marker::Copy }, |_| quote! {span =>})
|
||||
}
|
||||
|
||||
fn clone_expand(
|
||||
|
@ -415,37 +431,35 @@ fn clone_expand(
|
|||
tt: &ast::Adt,
|
||||
tm: SpanMapRef<'_>,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let krate = find_builtin_crate(db, id);
|
||||
expand_simple_derive(span, tt, tm, quote! { #krate::clone::Clone }, |adt| {
|
||||
let krate = find_builtin_crate(db, id, span);
|
||||
expand_simple_derive(span, tt, tm, quote! {span => #krate::clone::Clone }, |adt| {
|
||||
if matches!(adt.shape, AdtShape::Union) {
|
||||
let star =
|
||||
tt::Punct { char: '*', spacing: ::tt::Spacing::Alone, span: tt::SpanData::DUMMY };
|
||||
return quote! {
|
||||
let star = tt::Punct { char: '*', spacing: ::tt::Spacing::Alone, span };
|
||||
return quote! {span =>
|
||||
fn clone(&self) -> Self {
|
||||
#star self
|
||||
}
|
||||
};
|
||||
}
|
||||
if matches!(&adt.shape, AdtShape::Enum { variants, .. } if variants.is_empty()) {
|
||||
let star =
|
||||
tt::Punct { char: '*', spacing: ::tt::Spacing::Alone, span: tt::SpanData::DUMMY };
|
||||
return quote! {
|
||||
let star = tt::Punct { char: '*', spacing: ::tt::Spacing::Alone, span };
|
||||
return quote! {span =>
|
||||
fn clone(&self) -> Self {
|
||||
match #star self {}
|
||||
}
|
||||
};
|
||||
}
|
||||
let name = &adt.name;
|
||||
let patterns = adt.shape.as_pattern(name);
|
||||
let exprs = adt.shape.as_pattern_map(name, |it| quote! { #it .clone() });
|
||||
let patterns = adt.shape.as_pattern(span, name);
|
||||
let exprs = adt.shape.as_pattern_map(name, |it| quote! {span => #it .clone() }, span);
|
||||
let arms = patterns.into_iter().zip(exprs.into_iter()).map(|(pat, expr)| {
|
||||
let fat_arrow = fat_arrow();
|
||||
quote! {
|
||||
let fat_arrow = fat_arrow(span);
|
||||
quote! {span =>
|
||||
#pat #fat_arrow #expr,
|
||||
}
|
||||
});
|
||||
|
||||
quote! {
|
||||
quote! {span =>
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
##arms
|
||||
|
@ -455,16 +469,16 @@ fn clone_expand(
|
|||
})
|
||||
}
|
||||
|
||||
/// This function exists since `quote! { => }` doesn't work.
|
||||
fn fat_arrow() -> tt::Subtree {
|
||||
let eq = tt::Punct { char: '=', spacing: ::tt::Spacing::Joint, span: tt::SpanData::DUMMY };
|
||||
quote! { #eq> }
|
||||
/// This function exists since `quote! {span => => }` doesn't work.
|
||||
fn fat_arrow(span: SpanData) -> tt::Subtree {
|
||||
let eq = tt::Punct { char: '=', spacing: ::tt::Spacing::Joint, span };
|
||||
quote! {span => #eq> }
|
||||
}
|
||||
|
||||
/// This function exists since `quote! { && }` doesn't work.
|
||||
fn and_and() -> tt::Subtree {
|
||||
let and = tt::Punct { char: '&', spacing: ::tt::Spacing::Joint, span: tt::SpanData::DUMMY };
|
||||
quote! { #and& }
|
||||
/// This function exists since `quote! {span => && }` doesn't work.
|
||||
fn and_and(span: SpanData) -> tt::Subtree {
|
||||
let and = tt::Punct { char: '&', spacing: ::tt::Spacing::Joint, span };
|
||||
quote! {span => #and& }
|
||||
}
|
||||
|
||||
fn default_expand(
|
||||
|
@ -474,33 +488,37 @@ fn default_expand(
|
|||
tt: &ast::Adt,
|
||||
tm: SpanMapRef<'_>,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let krate = &find_builtin_crate(db, id);
|
||||
expand_simple_derive(span, tt, tm, quote! { #krate::default::Default }, |adt| {
|
||||
let krate = &find_builtin_crate(db, id, span);
|
||||
expand_simple_derive(span, tt, tm, quote! {span => #krate::default::Default }, |adt| {
|
||||
let body = match &adt.shape {
|
||||
AdtShape::Struct(fields) => {
|
||||
let name = &adt.name;
|
||||
fields
|
||||
.as_pattern_map(quote!(#name), |_| quote!(#krate::default::Default::default()))
|
||||
fields.as_pattern_map(
|
||||
quote!(span =>#name),
|
||||
span,
|
||||
|_| quote!(span =>#krate::default::Default::default()),
|
||||
)
|
||||
}
|
||||
AdtShape::Enum { default_variant, variants } => {
|
||||
if let Some(d) = default_variant {
|
||||
let (name, fields) = &variants[*d];
|
||||
let adt_name = &adt.name;
|
||||
fields.as_pattern_map(
|
||||
quote!(#adt_name :: #name),
|
||||
|_| quote!(#krate::default::Default::default()),
|
||||
quote!(span =>#adt_name :: #name),
|
||||
span,
|
||||
|_| quote!(span =>#krate::default::Default::default()),
|
||||
)
|
||||
} else {
|
||||
// FIXME: Return expand error here
|
||||
quote!()
|
||||
quote!(span =>)
|
||||
}
|
||||
}
|
||||
AdtShape::Union => {
|
||||
// FIXME: Return expand error here
|
||||
quote!()
|
||||
quote!(span =>)
|
||||
}
|
||||
};
|
||||
quote! {
|
||||
quote! {span =>
|
||||
fn default() -> Self {
|
||||
#body
|
||||
}
|
||||
|
@ -515,38 +533,37 @@ fn debug_expand(
|
|||
tt: &ast::Adt,
|
||||
tm: SpanMapRef<'_>,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let krate = &find_builtin_crate(db, id);
|
||||
expand_simple_derive(span, tt, tm, quote! { #krate::fmt::Debug }, |adt| {
|
||||
let krate = &find_builtin_crate(db, id, span);
|
||||
expand_simple_derive(span, tt, tm, quote! {span => #krate::fmt::Debug }, |adt| {
|
||||
let for_variant = |name: String, v: &VariantShape| match v {
|
||||
VariantShape::Struct(fields) => {
|
||||
let for_fields = fields.iter().map(|it| {
|
||||
let x_string = it.to_string();
|
||||
quote! {
|
||||
quote! {span =>
|
||||
.field(#x_string, & #it)
|
||||
}
|
||||
});
|
||||
quote! {
|
||||
quote! {span =>
|
||||
f.debug_struct(#name) ##for_fields .finish()
|
||||
}
|
||||
}
|
||||
VariantShape::Tuple(n) => {
|
||||
let for_fields = tuple_field_iterator(*n).map(|it| {
|
||||
quote! {
|
||||
let for_fields = tuple_field_iterator(span, *n).map(|it| {
|
||||
quote! {span =>
|
||||
.field( & #it)
|
||||
}
|
||||
});
|
||||
quote! {
|
||||
quote! {span =>
|
||||
f.debug_tuple(#name) ##for_fields .finish()
|
||||
}
|
||||
}
|
||||
VariantShape::Unit => quote! {
|
||||
VariantShape::Unit => quote! {span =>
|
||||
f.write_str(#name)
|
||||
},
|
||||
};
|
||||
if matches!(&adt.shape, AdtShape::Enum { variants, .. } if variants.is_empty()) {
|
||||
let star =
|
||||
tt::Punct { char: '*', spacing: ::tt::Spacing::Alone, span: tt::SpanData::DUMMY };
|
||||
return quote! {
|
||||
let star = tt::Punct { char: '*', spacing: ::tt::Spacing::Alone, span };
|
||||
return quote! {span =>
|
||||
fn fmt(&self, f: &mut #krate::fmt::Formatter) -> #krate::fmt::Result {
|
||||
match #star self {}
|
||||
}
|
||||
|
@ -554,20 +571,20 @@ fn debug_expand(
|
|||
}
|
||||
let arms = match &adt.shape {
|
||||
AdtShape::Struct(fields) => {
|
||||
let fat_arrow = fat_arrow();
|
||||
let fat_arrow = fat_arrow(span);
|
||||
let name = &adt.name;
|
||||
let pat = fields.as_pattern(quote!(#name));
|
||||
let pat = fields.as_pattern(quote!(span =>#name), span);
|
||||
let expr = for_variant(name.to_string(), fields);
|
||||
vec![quote! { #pat #fat_arrow #expr }]
|
||||
vec![quote! {span => #pat #fat_arrow #expr }]
|
||||
}
|
||||
AdtShape::Enum { variants, .. } => variants
|
||||
.iter()
|
||||
.map(|(name, v)| {
|
||||
let fat_arrow = fat_arrow();
|
||||
let fat_arrow = fat_arrow(span);
|
||||
let adt_name = &adt.name;
|
||||
let pat = v.as_pattern(quote!(#adt_name :: #name));
|
||||
let pat = v.as_pattern(quote!(span =>#adt_name :: #name), span);
|
||||
let expr = for_variant(name.to_string(), v);
|
||||
quote! {
|
||||
quote! {span =>
|
||||
#pat #fat_arrow #expr ,
|
||||
}
|
||||
})
|
||||
|
@ -577,7 +594,7 @@ fn debug_expand(
|
|||
vec![]
|
||||
}
|
||||
};
|
||||
quote! {
|
||||
quote! {span =>
|
||||
fn fmt(&self, f: &mut #krate::fmt::Formatter) -> #krate::fmt::Result {
|
||||
match self {
|
||||
##arms
|
||||
|
@ -594,41 +611,42 @@ fn hash_expand(
|
|||
tt: &ast::Adt,
|
||||
tm: SpanMapRef<'_>,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let krate = &find_builtin_crate(db, id);
|
||||
expand_simple_derive(span, tt, tm, quote! { #krate::hash::Hash }, |adt| {
|
||||
let krate = &find_builtin_crate(db, id, span);
|
||||
expand_simple_derive(span, tt, tm, quote! {span => #krate::hash::Hash }, |adt| {
|
||||
if matches!(adt.shape, AdtShape::Union) {
|
||||
// FIXME: Return expand error here
|
||||
return quote! {};
|
||||
return quote! {span =>};
|
||||
}
|
||||
if matches!(&adt.shape, AdtShape::Enum { variants, .. } if variants.is_empty()) {
|
||||
let star =
|
||||
tt::Punct { char: '*', spacing: ::tt::Spacing::Alone, span: tt::SpanData::DUMMY };
|
||||
return quote! {
|
||||
let star = tt::Punct { char: '*', spacing: ::tt::Spacing::Alone, span };
|
||||
return quote! {span =>
|
||||
fn hash<H: #krate::hash::Hasher>(&self, ra_expand_state: &mut H) {
|
||||
match #star self {}
|
||||
}
|
||||
};
|
||||
}
|
||||
let arms = adt.shape.as_pattern(&adt.name).into_iter().zip(adt.shape.field_names()).map(
|
||||
|(pat, names)| {
|
||||
let expr = {
|
||||
let it = names.iter().map(|it| quote! { #it . hash(ra_expand_state); });
|
||||
quote! { {
|
||||
##it
|
||||
} }
|
||||
};
|
||||
let fat_arrow = fat_arrow();
|
||||
quote! {
|
||||
#pat #fat_arrow #expr ,
|
||||
}
|
||||
},
|
||||
);
|
||||
let arms =
|
||||
adt.shape.as_pattern(span, &adt.name).into_iter().zip(adt.shape.field_names(span)).map(
|
||||
|(pat, names)| {
|
||||
let expr = {
|
||||
let it =
|
||||
names.iter().map(|it| quote! {span => #it . hash(ra_expand_state); });
|
||||
quote! {span => {
|
||||
##it
|
||||
} }
|
||||
};
|
||||
let fat_arrow = fat_arrow(span);
|
||||
quote! {span =>
|
||||
#pat #fat_arrow #expr ,
|
||||
}
|
||||
},
|
||||
);
|
||||
let check_discriminant = if matches!(&adt.shape, AdtShape::Enum { .. }) {
|
||||
quote! { #krate::mem::discriminant(self).hash(ra_expand_state); }
|
||||
quote! {span => #krate::mem::discriminant(self).hash(ra_expand_state); }
|
||||
} else {
|
||||
quote! {}
|
||||
quote! {span =>}
|
||||
};
|
||||
quote! {
|
||||
quote! {span =>
|
||||
fn hash<H: #krate::hash::Hasher>(&self, ra_expand_state: &mut H) {
|
||||
#check_discriminant
|
||||
match self {
|
||||
|
@ -646,8 +664,8 @@ fn eq_expand(
|
|||
tt: &ast::Adt,
|
||||
tm: SpanMapRef<'_>,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let krate = find_builtin_crate(db, id);
|
||||
expand_simple_derive(span, tt, tm, quote! { #krate::cmp::Eq }, |_| quote! {})
|
||||
let krate = find_builtin_crate(db, id, span);
|
||||
expand_simple_derive(span, tt, tm, quote! {span => #krate::cmp::Eq }, |_| quote! {span =>})
|
||||
}
|
||||
|
||||
fn partial_eq_expand(
|
||||
|
@ -657,43 +675,43 @@ fn partial_eq_expand(
|
|||
tt: &ast::Adt,
|
||||
tm: SpanMapRef<'_>,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let krate = find_builtin_crate(db, id);
|
||||
expand_simple_derive(span, tt, tm, quote! { #krate::cmp::PartialEq }, |adt| {
|
||||
let krate = find_builtin_crate(db, id, span);
|
||||
expand_simple_derive(span, tt, tm, quote! {span => #krate::cmp::PartialEq }, |adt| {
|
||||
if matches!(adt.shape, AdtShape::Union) {
|
||||
// FIXME: Return expand error here
|
||||
return quote! {};
|
||||
return quote! {span =>};
|
||||
}
|
||||
let name = &adt.name;
|
||||
|
||||
let (self_patterns, other_patterns) = self_and_other_patterns(adt, name);
|
||||
let arms = izip!(self_patterns, other_patterns, adt.shape.field_names()).map(
|
||||
let (self_patterns, other_patterns) = self_and_other_patterns(adt, name, span);
|
||||
let arms = izip!(self_patterns, other_patterns, adt.shape.field_names(span)).map(
|
||||
|(pat1, pat2, names)| {
|
||||
let fat_arrow = fat_arrow();
|
||||
let fat_arrow = fat_arrow(span);
|
||||
let body = match &*names {
|
||||
[] => {
|
||||
quote!(true)
|
||||
quote!(span =>true)
|
||||
}
|
||||
[first, rest @ ..] => {
|
||||
let rest = rest.iter().map(|it| {
|
||||
let t1 = tt::Ident::new(format!("{}_self", it.text), it.span);
|
||||
let t2 = tt::Ident::new(format!("{}_other", it.text), it.span);
|
||||
let and_and = and_and();
|
||||
quote!(#and_and #t1 .eq( #t2 ))
|
||||
let and_and = and_and(span);
|
||||
quote!(span =>#and_and #t1 .eq( #t2 ))
|
||||
});
|
||||
let first = {
|
||||
let t1 = tt::Ident::new(format!("{}_self", first.text), first.span);
|
||||
let t2 = tt::Ident::new(format!("{}_other", first.text), first.span);
|
||||
quote!(#t1 .eq( #t2 ))
|
||||
quote!(span =>#t1 .eq( #t2 ))
|
||||
};
|
||||
quote!(#first ##rest)
|
||||
quote!(span =>#first ##rest)
|
||||
}
|
||||
};
|
||||
quote! { ( #pat1 , #pat2 ) #fat_arrow #body , }
|
||||
quote! {span => ( #pat1 , #pat2 ) #fat_arrow #body , }
|
||||
},
|
||||
);
|
||||
|
||||
let fat_arrow = fat_arrow();
|
||||
quote! {
|
||||
let fat_arrow = fat_arrow(span);
|
||||
quote! {span =>
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
##arms
|
||||
|
@ -707,15 +725,24 @@ fn partial_eq_expand(
|
|||
fn self_and_other_patterns(
|
||||
adt: &BasicAdtInfo,
|
||||
name: &tt::Ident,
|
||||
span: SpanData,
|
||||
) -> (Vec<tt::Subtree>, Vec<tt::Subtree>) {
|
||||
let self_patterns = adt.shape.as_pattern_map(name, |it| {
|
||||
let t = tt::Ident::new(format!("{}_self", it.text), it.span);
|
||||
quote!(#t)
|
||||
});
|
||||
let other_patterns = adt.shape.as_pattern_map(name, |it| {
|
||||
let t = tt::Ident::new(format!("{}_other", it.text), it.span);
|
||||
quote!(#t)
|
||||
});
|
||||
let self_patterns = adt.shape.as_pattern_map(
|
||||
name,
|
||||
|it| {
|
||||
let t = tt::Ident::new(format!("{}_self", it.text), it.span);
|
||||
quote!(span =>#t)
|
||||
},
|
||||
span,
|
||||
);
|
||||
let other_patterns = adt.shape.as_pattern_map(
|
||||
name,
|
||||
|it| {
|
||||
let t = tt::Ident::new(format!("{}_other", it.text), it.span);
|
||||
quote!(span =>#t)
|
||||
},
|
||||
span,
|
||||
);
|
||||
(self_patterns, other_patterns)
|
||||
}
|
||||
|
||||
|
@ -726,17 +753,18 @@ fn ord_expand(
|
|||
tt: &ast::Adt,
|
||||
tm: SpanMapRef<'_>,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let krate = &find_builtin_crate(db, id);
|
||||
expand_simple_derive(span, tt, tm, quote! { #krate::cmp::Ord }, |adt| {
|
||||
let krate = &find_builtin_crate(db, id, span);
|
||||
expand_simple_derive(span, tt, tm, quote! {span => #krate::cmp::Ord }, |adt| {
|
||||
fn compare(
|
||||
krate: &tt::TokenTree,
|
||||
left: tt::Subtree,
|
||||
right: tt::Subtree,
|
||||
rest: tt::Subtree,
|
||||
span: SpanData,
|
||||
) -> tt::Subtree {
|
||||
let fat_arrow1 = fat_arrow();
|
||||
let fat_arrow2 = fat_arrow();
|
||||
quote! {
|
||||
let fat_arrow1 = fat_arrow(span);
|
||||
let fat_arrow2 = fat_arrow(span);
|
||||
quote! {span =>
|
||||
match #left.cmp(&#right) {
|
||||
#krate::cmp::Ordering::Equal #fat_arrow1 {
|
||||
#rest
|
||||
|
@ -747,34 +775,34 @@ fn ord_expand(
|
|||
}
|
||||
if matches!(adt.shape, AdtShape::Union) {
|
||||
// FIXME: Return expand error here
|
||||
return quote!();
|
||||
return quote!(span =>);
|
||||
}
|
||||
let (self_patterns, other_patterns) = self_and_other_patterns(adt, &adt.name);
|
||||
let arms = izip!(self_patterns, other_patterns, adt.shape.field_names()).map(
|
||||
let (self_patterns, other_patterns) = self_and_other_patterns(adt, &adt.name, span);
|
||||
let arms = izip!(self_patterns, other_patterns, adt.shape.field_names(span)).map(
|
||||
|(pat1, pat2, fields)| {
|
||||
let mut body = quote!(#krate::cmp::Ordering::Equal);
|
||||
let mut body = quote!(span =>#krate::cmp::Ordering::Equal);
|
||||
for f in fields.into_iter().rev() {
|
||||
let t1 = tt::Ident::new(format!("{}_self", f.text), f.span);
|
||||
let t2 = tt::Ident::new(format!("{}_other", f.text), f.span);
|
||||
body = compare(krate, quote!(#t1), quote!(#t2), body);
|
||||
body = compare(krate, quote!(span =>#t1), quote!(span =>#t2), body, span);
|
||||
}
|
||||
let fat_arrow = fat_arrow();
|
||||
quote! { ( #pat1 , #pat2 ) #fat_arrow #body , }
|
||||
let fat_arrow = fat_arrow(span);
|
||||
quote! {span => ( #pat1 , #pat2 ) #fat_arrow #body , }
|
||||
},
|
||||
);
|
||||
let fat_arrow = fat_arrow();
|
||||
let mut body = quote! {
|
||||
let fat_arrow = fat_arrow(span);
|
||||
let mut body = quote! {span =>
|
||||
match (self, other) {
|
||||
##arms
|
||||
_unused #fat_arrow #krate::cmp::Ordering::Equal
|
||||
}
|
||||
};
|
||||
if matches!(&adt.shape, AdtShape::Enum { .. }) {
|
||||
let left = quote!(#krate::intrinsics::discriminant_value(self));
|
||||
let right = quote!(#krate::intrinsics::discriminant_value(other));
|
||||
body = compare(krate, left, right, body);
|
||||
let left = quote!(span =>#krate::intrinsics::discriminant_value(self));
|
||||
let right = quote!(span =>#krate::intrinsics::discriminant_value(other));
|
||||
body = compare(krate, left, right, body, span);
|
||||
}
|
||||
quote! {
|
||||
quote! {span =>
|
||||
fn cmp(&self, other: &Self) -> #krate::cmp::Ordering {
|
||||
#body
|
||||
}
|
||||
|
@ -789,17 +817,18 @@ fn partial_ord_expand(
|
|||
tt: &ast::Adt,
|
||||
tm: SpanMapRef<'_>,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let krate = &find_builtin_crate(db, id);
|
||||
expand_simple_derive(span, tt, tm, quote! { #krate::cmp::PartialOrd }, |adt| {
|
||||
let krate = &find_builtin_crate(db, id, span);
|
||||
expand_simple_derive(span, tt, tm, quote! {span => #krate::cmp::PartialOrd }, |adt| {
|
||||
fn compare(
|
||||
krate: &tt::TokenTree,
|
||||
left: tt::Subtree,
|
||||
right: tt::Subtree,
|
||||
rest: tt::Subtree,
|
||||
span: SpanData,
|
||||
) -> tt::Subtree {
|
||||
let fat_arrow1 = fat_arrow();
|
||||
let fat_arrow2 = fat_arrow();
|
||||
quote! {
|
||||
let fat_arrow1 = fat_arrow(span);
|
||||
let fat_arrow2 = fat_arrow(span);
|
||||
quote! {span =>
|
||||
match #left.partial_cmp(&#right) {
|
||||
#krate::option::Option::Some(#krate::cmp::Ordering::Equal) #fat_arrow1 {
|
||||
#rest
|
||||
|
@ -810,37 +839,39 @@ fn partial_ord_expand(
|
|||
}
|
||||
if matches!(adt.shape, AdtShape::Union) {
|
||||
// FIXME: Return expand error here
|
||||
return quote!();
|
||||
return quote!(span =>);
|
||||
}
|
||||
let left = quote!(#krate::intrinsics::discriminant_value(self));
|
||||
let right = quote!(#krate::intrinsics::discriminant_value(other));
|
||||
let left = quote!(span =>#krate::intrinsics::discriminant_value(self));
|
||||
let right = quote!(span =>#krate::intrinsics::discriminant_value(other));
|
||||
|
||||
let (self_patterns, other_patterns) = self_and_other_patterns(adt, &adt.name);
|
||||
let arms = izip!(self_patterns, other_patterns, adt.shape.field_names()).map(
|
||||
let (self_patterns, other_patterns) = self_and_other_patterns(adt, &adt.name, span);
|
||||
let arms = izip!(self_patterns, other_patterns, adt.shape.field_names(span)).map(
|
||||
|(pat1, pat2, fields)| {
|
||||
let mut body = quote!(#krate::option::Option::Some(#krate::cmp::Ordering::Equal));
|
||||
let mut body =
|
||||
quote!(span =>#krate::option::Option::Some(#krate::cmp::Ordering::Equal));
|
||||
for f in fields.into_iter().rev() {
|
||||
let t1 = tt::Ident::new(format!("{}_self", f.text), f.span);
|
||||
let t2 = tt::Ident::new(format!("{}_other", f.text), f.span);
|
||||
body = compare(krate, quote!(#t1), quote!(#t2), body);
|
||||
body = compare(krate, quote!(span =>#t1), quote!(span =>#t2), body, span);
|
||||
}
|
||||
let fat_arrow = fat_arrow();
|
||||
quote! { ( #pat1 , #pat2 ) #fat_arrow #body , }
|
||||
let fat_arrow = fat_arrow(span);
|
||||
quote! {span => ( #pat1 , #pat2 ) #fat_arrow #body , }
|
||||
},
|
||||
);
|
||||
let fat_arrow = fat_arrow();
|
||||
let fat_arrow = fat_arrow(span);
|
||||
let body = compare(
|
||||
krate,
|
||||
left,
|
||||
right,
|
||||
quote! {
|
||||
quote! {span =>
|
||||
match (self, other) {
|
||||
##arms
|
||||
_unused #fat_arrow #krate::option::Option::Some(#krate::cmp::Ordering::Equal)
|
||||
}
|
||||
},
|
||||
span,
|
||||
);
|
||||
quote! {
|
||||
quote! {span =>
|
||||
fn partial_cmp(&self, other: &Self) -> #krate::option::Option::Option<#krate::cmp::Ordering> {
|
||||
#body
|
||||
}
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
//! Builtin macro
|
||||
|
||||
use ::tt::Span;
|
||||
use base_db::{
|
||||
span::{SpanAnchor, ROOT_ERASED_FILE_AST_ID},
|
||||
span::{SpanAnchor, SpanData, SyntaxContextId, ROOT_ERASED_FILE_AST_ID},
|
||||
AnchoredPath, Edition, FileId,
|
||||
};
|
||||
use cfg::CfgExpr;
|
||||
|
@ -15,8 +14,9 @@ use syntax::{
|
|||
|
||||
use crate::{
|
||||
db::ExpandDatabase,
|
||||
hygiene::span_with_def_site_ctxt,
|
||||
name, quote,
|
||||
tt::{self},
|
||||
tt::{self, DelimSpan},
|
||||
EagerCallInfo, ExpandError, ExpandResult, HirFileIdExt, MacroCallId, MacroCallLoc,
|
||||
};
|
||||
|
||||
|
@ -42,7 +42,10 @@ macro_rules! register_builtin {
|
|||
let expander = match *self {
|
||||
$( BuiltinFnLikeExpander::$kind => $expand, )*
|
||||
};
|
||||
expander(db, id, tt)
|
||||
|
||||
let span = db.lookup_intern_macro_call(id).span(db);
|
||||
let span = span_with_def_site_ctxt(db, span, id);
|
||||
expander(db, id, tt, span)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,13 +53,16 @@ macro_rules! register_builtin {
|
|||
pub fn expand(
|
||||
&self,
|
||||
db: &dyn ExpandDatabase,
|
||||
arg_id: MacroCallId,
|
||||
id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let expander = match *self {
|
||||
$( EagerExpander::$e_kind => $e_expand, )*
|
||||
};
|
||||
expander(db, arg_id, tt)
|
||||
|
||||
let span = db.lookup_intern_macro_call(id).span(db);
|
||||
let span = span_with_def_site_ctxt(db, span, id);
|
||||
expander(db, id, tt, span)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -115,29 +121,42 @@ register_builtin! {
|
|||
(option_env, OptionEnv) => option_env_expand
|
||||
}
|
||||
|
||||
const DOLLAR_CRATE: tt::Ident =
|
||||
tt::Ident { text: SmolStr::new_inline("$crate"), span: tt::SpanData::DUMMY };
|
||||
fn mk_pound(span: SpanData) -> tt::Subtree {
|
||||
crate::quote::IntoTt::to_subtree(
|
||||
vec![crate::tt::Leaf::Punct(crate::tt::Punct {
|
||||
char: '#',
|
||||
spacing: crate::tt::Spacing::Alone,
|
||||
span: span,
|
||||
})
|
||||
.into()],
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
||||
fn module_path_expand(
|
||||
_db: &dyn ExpandDatabase,
|
||||
_id: MacroCallId,
|
||||
_tt: &tt::Subtree,
|
||||
span: SpanData,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
// Just return a dummy result.
|
||||
ExpandResult::ok(quote! { "module::path" })
|
||||
ExpandResult::ok(quote! {span =>
|
||||
"module::path"
|
||||
})
|
||||
}
|
||||
|
||||
fn line_expand(
|
||||
_db: &dyn ExpandDatabase,
|
||||
_id: MacroCallId,
|
||||
_tt: &tt::Subtree,
|
||||
span: SpanData,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
// dummy implementation for type-checking purposes
|
||||
ExpandResult::ok(tt::Subtree {
|
||||
delimiter: tt::Delimiter::unspecified(),
|
||||
delimiter: tt::Delimiter::dummy_invisible(),
|
||||
token_trees: vec![tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
|
||||
text: "0u32".into(),
|
||||
span: tt::SpanData::DUMMY,
|
||||
span,
|
||||
}))],
|
||||
})
|
||||
}
|
||||
|
@ -146,26 +165,29 @@ fn log_syntax_expand(
|
|||
_db: &dyn ExpandDatabase,
|
||||
_id: MacroCallId,
|
||||
_tt: &tt::Subtree,
|
||||
span: SpanData,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
ExpandResult::ok(quote! {})
|
||||
ExpandResult::ok(quote! {span =>})
|
||||
}
|
||||
|
||||
fn trace_macros_expand(
|
||||
_db: &dyn ExpandDatabase,
|
||||
_id: MacroCallId,
|
||||
_tt: &tt::Subtree,
|
||||
span: SpanData,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
ExpandResult::ok(quote! {})
|
||||
ExpandResult::ok(quote! {span =>})
|
||||
}
|
||||
|
||||
fn stringify_expand(
|
||||
_db: &dyn ExpandDatabase,
|
||||
_id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
span: SpanData,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let pretty = ::tt::pretty(&tt.token_trees);
|
||||
|
||||
let expanded = quote! {
|
||||
let expanded = quote! {span =>
|
||||
#pretty
|
||||
};
|
||||
|
||||
|
@ -176,27 +198,29 @@ fn assert_expand(
|
|||
_db: &dyn ExpandDatabase,
|
||||
_id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
span: SpanData,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let args = parse_exprs_with_sep(tt, ',');
|
||||
let dollar_crate = tt::Ident { text: SmolStr::new_inline("$crate"), span };
|
||||
let expanded = match &*args {
|
||||
[cond, panic_args @ ..] => {
|
||||
let comma = tt::Subtree {
|
||||
delimiter: tt::Delimiter::unspecified(),
|
||||
delimiter: tt::Delimiter::dummy_invisible(),
|
||||
token_trees: vec![tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct {
|
||||
char: ',',
|
||||
spacing: tt::Spacing::Alone,
|
||||
span: tt::SpanData::DUMMY,
|
||||
span,
|
||||
}))],
|
||||
};
|
||||
let cond = cond.clone();
|
||||
let panic_args = itertools::Itertools::intersperse(panic_args.iter().cloned(), comma);
|
||||
quote! {{
|
||||
quote! {span =>{
|
||||
if !(#cond) {
|
||||
#DOLLAR_CRATE::panic!(##panic_args);
|
||||
#dollar_crate::panic!(##panic_args);
|
||||
}
|
||||
}}
|
||||
}
|
||||
[] => quote! {{}},
|
||||
[] => quote! {span =>{}},
|
||||
};
|
||||
|
||||
ExpandResult::ok(expanded)
|
||||
|
@ -206,12 +230,13 @@ fn file_expand(
|
|||
_db: &dyn ExpandDatabase,
|
||||
_id: MacroCallId,
|
||||
_tt: &tt::Subtree,
|
||||
span: SpanData,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
// FIXME: RA purposefully lacks knowledge of absolute file names
|
||||
// so just return "".
|
||||
let file_name = "";
|
||||
|
||||
let expanded = quote! {
|
||||
let expanded = quote! {span =>
|
||||
#file_name
|
||||
};
|
||||
|
||||
|
@ -222,16 +247,18 @@ fn format_args_expand(
|
|||
db: &dyn ExpandDatabase,
|
||||
id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
span: SpanData,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
format_args_expand_general(db, id, tt, "")
|
||||
format_args_expand_general(db, id, tt, "", span)
|
||||
}
|
||||
|
||||
fn format_args_nl_expand(
|
||||
db: &dyn ExpandDatabase,
|
||||
id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
span: SpanData,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
format_args_expand_general(db, id, tt, "\\n")
|
||||
format_args_expand_general(db, id, tt, "\\n", span)
|
||||
}
|
||||
|
||||
fn format_args_expand_general(
|
||||
|
@ -240,11 +267,12 @@ fn format_args_expand_general(
|
|||
tt: &tt::Subtree,
|
||||
// FIXME: Make use of this so that mir interpretation works properly
|
||||
_end_string: &str,
|
||||
span: SpanData,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let pound = quote! {@PUNCT '#'};
|
||||
let pound = mk_pound(span);
|
||||
let mut tt = tt.clone();
|
||||
tt.delimiter.kind = tt::DelimiterKind::Parenthesis;
|
||||
return ExpandResult::ok(quote! {
|
||||
return ExpandResult::ok(quote! {span =>
|
||||
builtin #pound format_args #tt
|
||||
});
|
||||
}
|
||||
|
@ -253,25 +281,25 @@ fn asm_expand(
|
|||
_db: &dyn ExpandDatabase,
|
||||
_id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
span: SpanData,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
// We expand all assembly snippets to `format_args!` invocations to get format syntax
|
||||
// highlighting for them.
|
||||
|
||||
let mut literals = Vec::new();
|
||||
for tt in tt.token_trees.chunks(2) {
|
||||
match tt {
|
||||
[tt::TokenTree::Leaf(tt::Leaf::Literal(lit))]
|
||||
| [tt::TokenTree::Leaf(tt::Leaf::Literal(lit)), tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: ',', span: _, spacing: _ }))] =>
|
||||
{
|
||||
let krate = DOLLAR_CRATE.clone();
|
||||
literals.push(quote!(#krate::format_args!(#lit);));
|
||||
let dollar_krate = tt::Ident { text: SmolStr::new_inline("$crate"), span };
|
||||
literals.push(quote!(span=>#dollar_krate::format_args!(#lit);));
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
|
||||
let pound = quote! {@PUNCT '#'};
|
||||
let expanded = quote! {
|
||||
let pound = mk_pound(span);
|
||||
let expanded = quote! {span =>
|
||||
builtin #pound asm (
|
||||
{##literals}
|
||||
)
|
||||
|
@ -283,20 +311,22 @@ fn global_asm_expand(
|
|||
_db: &dyn ExpandDatabase,
|
||||
_id: MacroCallId,
|
||||
_tt: &tt::Subtree,
|
||||
span: SpanData,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
// Expand to nothing (at item-level)
|
||||
ExpandResult::ok(quote! {})
|
||||
ExpandResult::ok(quote! {span =>})
|
||||
}
|
||||
|
||||
fn cfg_expand(
|
||||
db: &dyn ExpandDatabase,
|
||||
id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
span: SpanData,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let loc = db.lookup_intern_macro_call(id);
|
||||
let expr = CfgExpr::parse(tt);
|
||||
let enabled = db.crate_graph()[loc.krate].cfg_options.check(&expr) != Some(false);
|
||||
let expanded = if enabled { quote!(true) } else { quote!(false) };
|
||||
let expanded = if enabled { quote!(span=>true) } else { quote!(span=>false) };
|
||||
ExpandResult::ok(expanded)
|
||||
}
|
||||
|
||||
|
@ -304,13 +334,15 @@ fn panic_expand(
|
|||
db: &dyn ExpandDatabase,
|
||||
id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
span: SpanData,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let loc: MacroCallLoc = db.lookup_intern_macro_call(id);
|
||||
let dollar_crate = tt::Ident { text: SmolStr::new_inline("$crate"), span };
|
||||
// Expand to a macro call `$crate::panic::panic_{edition}`
|
||||
let mut call = if db.crate_graph()[loc.krate].edition >= Edition::Edition2021 {
|
||||
quote!(#DOLLAR_CRATE::panic::panic_2021!)
|
||||
quote!(span =>#dollar_crate::panic::panic_2021!)
|
||||
} else {
|
||||
quote!(#DOLLAR_CRATE::panic::panic_2015!)
|
||||
quote!(span =>#dollar_crate::panic::panic_2015!)
|
||||
};
|
||||
|
||||
// Pass the original arguments
|
||||
|
@ -322,13 +354,15 @@ fn unreachable_expand(
|
|||
db: &dyn ExpandDatabase,
|
||||
id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
span: SpanData,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let loc: MacroCallLoc = db.lookup_intern_macro_call(id);
|
||||
// Expand to a macro call `$crate::panic::unreachable_{edition}`
|
||||
let dollar_crate = tt::Ident { text: SmolStr::new_inline("$crate"), span };
|
||||
let mut call = if db.crate_graph()[loc.krate].edition >= Edition::Edition2021 {
|
||||
quote!(#DOLLAR_CRATE::panic::unreachable_2021!)
|
||||
quote!(span =>#dollar_crate::panic::unreachable_2021!)
|
||||
} else {
|
||||
quote!(#DOLLAR_CRATE::panic::unreachable_2015!)
|
||||
quote!(span =>#dollar_crate::panic::unreachable_2015!)
|
||||
};
|
||||
|
||||
// Pass the original arguments
|
||||
|
@ -358,6 +392,7 @@ fn compile_error_expand(
|
|||
_db: &dyn ExpandDatabase,
|
||||
_id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
span: SpanData,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let err = match &*tt.token_trees {
|
||||
[tt::TokenTree::Leaf(tt::Leaf::Literal(it))] => match unquote_str(it) {
|
||||
|
@ -367,13 +402,14 @@ fn compile_error_expand(
|
|||
_ => ExpandError::other("`compile_error!` argument must be a string"),
|
||||
};
|
||||
|
||||
ExpandResult { value: quote! {}, err: Some(err) }
|
||||
ExpandResult { value: quote! {span =>}, err: Some(err) }
|
||||
}
|
||||
|
||||
fn concat_expand(
|
||||
_db: &dyn ExpandDatabase,
|
||||
_arg_id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
span: SpanData,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let mut err = None;
|
||||
let mut text = String::new();
|
||||
|
@ -413,13 +449,14 @@ fn concat_expand(
|
|||
}
|
||||
}
|
||||
}
|
||||
ExpandResult { value: quote!(#text), err }
|
||||
ExpandResult { value: quote!(span =>#text), err }
|
||||
}
|
||||
|
||||
fn concat_bytes_expand(
|
||||
_db: &dyn ExpandDatabase,
|
||||
_arg_id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
span: SpanData,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let mut bytes = Vec::new();
|
||||
let mut err = None;
|
||||
|
@ -452,8 +489,8 @@ fn concat_bytes_expand(
|
|||
}
|
||||
}
|
||||
}
|
||||
let ident = tt::Ident { text: bytes.join(", ").into(), span: tt::SpanData::DUMMY };
|
||||
ExpandResult { value: quote!([#ident]), err }
|
||||
let ident = tt::Ident { text: bytes.join(", ").into(), span };
|
||||
ExpandResult { value: quote!(span =>[#ident]), err }
|
||||
}
|
||||
|
||||
fn concat_bytes_expand_subtree(
|
||||
|
@ -486,6 +523,7 @@ fn concat_idents_expand(
|
|||
_db: &dyn ExpandDatabase,
|
||||
_arg_id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
span: SpanData,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let mut err = None;
|
||||
let mut ident = String::new();
|
||||
|
@ -500,8 +538,9 @@ fn concat_idents_expand(
|
|||
}
|
||||
}
|
||||
}
|
||||
let ident = tt::Ident { text: ident.into(), span: tt::SpanData::DUMMY };
|
||||
ExpandResult { value: quote!(#ident), err }
|
||||
// FIXME merge spans
|
||||
let ident = tt::Ident { text: ident.into(), span };
|
||||
ExpandResult { value: quote!(span =>#ident), err }
|
||||
}
|
||||
|
||||
fn relative_file(
|
||||
|
@ -537,10 +576,11 @@ fn include_expand(
|
|||
db: &dyn ExpandDatabase,
|
||||
arg_id: MacroCallId,
|
||||
_tt: &tt::Subtree,
|
||||
span: SpanData,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
match db.include_expand(arg_id) {
|
||||
Ok((res, _)) => ExpandResult::ok(res.as_ref().clone()),
|
||||
Err(e) => ExpandResult::new(tt::Subtree::empty(), e),
|
||||
Err(e) => ExpandResult::new(tt::Subtree::empty(DelimSpan { open: span, close: span }), e),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -559,6 +599,8 @@ pub(crate) fn include_arg_to_tt(
|
|||
// why are we not going through a SyntaxNode here?
|
||||
let subtree = parse_to_token_tree(
|
||||
SpanAnchor { file_id, ast_id: ROOT_ERASED_FILE_AST_ID },
|
||||
// FIXME
|
||||
SyntaxContextId::ROOT,
|
||||
&db.file_text(file_id),
|
||||
)
|
||||
.ok_or(mbe::ExpandError::ConversionError)?;
|
||||
|
@ -569,17 +611,18 @@ fn include_bytes_expand(
|
|||
_db: &dyn ExpandDatabase,
|
||||
_arg_id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
span: SpanData,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
if let Err(e) = parse_string(tt) {
|
||||
return ExpandResult::new(tt::Subtree::empty(), e);
|
||||
return ExpandResult::new(tt::Subtree::empty(DelimSpan { open: span, close: span }), e);
|
||||
}
|
||||
|
||||
// FIXME: actually read the file here if the user asked for macro expansion
|
||||
let res = tt::Subtree {
|
||||
delimiter: tt::Delimiter::unspecified(),
|
||||
delimiter: tt::Delimiter::dummy_invisible(),
|
||||
token_trees: vec![tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
|
||||
text: r#"b"""#.into(),
|
||||
span: tt::SpanData::DUMMY,
|
||||
span,
|
||||
}))],
|
||||
};
|
||||
ExpandResult::ok(res)
|
||||
|
@ -589,10 +632,13 @@ fn include_str_expand(
|
|||
db: &dyn ExpandDatabase,
|
||||
arg_id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
span: SpanData,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let path = match parse_string(tt) {
|
||||
Ok(it) => it,
|
||||
Err(e) => return ExpandResult::new(tt::Subtree::empty(), e),
|
||||
Err(e) => {
|
||||
return ExpandResult::new(tt::Subtree::empty(DelimSpan { open: span, close: span }), e)
|
||||
}
|
||||
};
|
||||
|
||||
// FIXME: we're not able to read excluded files (which is most of them because
|
||||
|
@ -602,14 +648,14 @@ fn include_str_expand(
|
|||
let file_id = match relative_file(db, arg_id, &path, true) {
|
||||
Ok(file_id) => file_id,
|
||||
Err(_) => {
|
||||
return ExpandResult::ok(quote!(""));
|
||||
return ExpandResult::ok(quote!(span =>""));
|
||||
}
|
||||
};
|
||||
|
||||
let text = db.file_text(file_id);
|
||||
let text = &*text;
|
||||
|
||||
ExpandResult::ok(quote!(#text))
|
||||
ExpandResult::ok(quote!(span =>#text))
|
||||
}
|
||||
|
||||
fn get_env_inner(db: &dyn ExpandDatabase, arg_id: MacroCallId, key: &str) -> Option<String> {
|
||||
|
@ -621,10 +667,13 @@ fn env_expand(
|
|||
db: &dyn ExpandDatabase,
|
||||
arg_id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
span: SpanData,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let key = match parse_string(tt) {
|
||||
Ok(it) => it,
|
||||
Err(e) => return ExpandResult::new(tt::Subtree::empty(), e),
|
||||
Err(e) => {
|
||||
return ExpandResult::new(tt::Subtree::empty(DelimSpan { open: span, close: span }), e)
|
||||
}
|
||||
};
|
||||
|
||||
let mut err = None;
|
||||
|
@ -641,7 +690,7 @@ fn env_expand(
|
|||
// `include!("foo.rs"), which might go to infinite loop
|
||||
"UNRESOLVED_ENV_VAR".to_string()
|
||||
});
|
||||
let expanded = quote! { #s };
|
||||
let expanded = quote! {span => #s };
|
||||
|
||||
ExpandResult { value: expanded, err }
|
||||
}
|
||||
|
@ -650,15 +699,18 @@ fn option_env_expand(
|
|||
db: &dyn ExpandDatabase,
|
||||
arg_id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
span: SpanData,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let key = match parse_string(tt) {
|
||||
Ok(it) => it,
|
||||
Err(e) => return ExpandResult::new(tt::Subtree::empty(), e),
|
||||
Err(e) => {
|
||||
return ExpandResult::new(tt::Subtree::empty(DelimSpan { open: span, close: span }), e)
|
||||
}
|
||||
};
|
||||
// FIXME: Use `DOLLAR_CRATE` when that works in eager macros.
|
||||
let expanded = match get_env_inner(db, arg_id, &key) {
|
||||
None => quote! { ::core::option::Option::None::<&str> },
|
||||
Some(s) => quote! { ::core::option::Option::Some(#s) },
|
||||
None => quote! {span => ::core::option::Option::None::<&str> },
|
||||
Some(s) => quote! {span => ::core::option::Option::Some(#s) },
|
||||
};
|
||||
|
||||
ExpandResult::ok(expanded)
|
||||
|
|
|
@ -1,11 +1,6 @@
|
|||
//! Defines database & queries for macro expansion.
|
||||
|
||||
use ::tt::{SpanAnchor as _, SyntaxContext};
|
||||
use base_db::{
|
||||
salsa,
|
||||
span::{SpanAnchor, SyntaxContextId},
|
||||
CrateId, Edition, FileId, SourceDatabase,
|
||||
};
|
||||
use base_db::{salsa, span::SyntaxContextId, CrateId, Edition, FileId, SourceDatabase};
|
||||
use either::Either;
|
||||
use limit::Limit;
|
||||
use mbe::{syntax_node_to_token_tree, ValueResult};
|
||||
|
@ -53,7 +48,7 @@ impl DeclarativeMacroExpander {
|
|||
) -> ExpandResult<tt::Subtree> {
|
||||
match self.mac.err() {
|
||||
Some(e) => ExpandResult::new(
|
||||
tt::Subtree::empty(),
|
||||
tt::Subtree::empty(tt::DelimSpan::DUMMY),
|
||||
ExpandError::other(format!("invalid macro definition: {e}")),
|
||||
),
|
||||
None => self
|
||||
|
@ -66,7 +61,7 @@ impl DeclarativeMacroExpander {
|
|||
pub fn expand_unhygienic(&self, tt: tt::Subtree) -> ExpandResult<tt::Subtree> {
|
||||
match self.mac.err() {
|
||||
Some(e) => ExpandResult::new(
|
||||
tt::Subtree::empty(),
|
||||
tt::Subtree::empty(tt::DelimSpan::DUMMY),
|
||||
ExpandError::other(format!("invalid macro definition: {e}")),
|
||||
),
|
||||
None => self.mac.expand(&tt, |_| ()).map_err(Into::into),
|
||||
|
@ -191,7 +186,7 @@ pub fn expand_speculative(
|
|||
) -> Option<(SyntaxNode, SyntaxToken)> {
|
||||
let loc = db.lookup_intern_macro_call(actual_macro_call);
|
||||
|
||||
let span_map = RealSpanMap::absolute(SpanAnchor::DUMMY.file_id);
|
||||
let span_map = RealSpanMap::absolute(FileId::BOGUS);
|
||||
let span_map = SpanMapRef::RealSpanMap(&span_map);
|
||||
|
||||
// Build the subtree and token mapping for the speculative args
|
||||
|
@ -235,7 +230,7 @@ pub fn expand_speculative(
|
|||
match attr.token_tree() {
|
||||
Some(token_tree) => {
|
||||
let mut tree = syntax_node_to_token_tree(token_tree.syntax(), span_map);
|
||||
tree.delimiter = tt::Delimiter::UNSPECIFIED;
|
||||
tree.delimiter = tt::Delimiter::DUMMY_INVISIBLE;
|
||||
|
||||
Some(tree)
|
||||
}
|
||||
|
@ -249,7 +244,7 @@ pub fn expand_speculative(
|
|||
// Otherwise the expand query will fetch the non speculative attribute args and pass those instead.
|
||||
let mut speculative_expansion = match loc.def.kind {
|
||||
MacroDefKind::ProcMacro(expander, ..) => {
|
||||
tt.delimiter = tt::Delimiter::UNSPECIFIED;
|
||||
tt.delimiter = tt::Delimiter::DUMMY_INVISIBLE;
|
||||
let call_site = loc.span(db);
|
||||
expander.expand(
|
||||
db,
|
||||
|
@ -263,7 +258,7 @@ pub fn expand_speculative(
|
|||
)
|
||||
}
|
||||
MacroDefKind::BuiltInAttr(BuiltinAttrExpander::Derive, _) => {
|
||||
pseudo_derive_attr_expansion(&tt, attr_arg.as_ref()?)
|
||||
pseudo_derive_attr_expansion(&tt, attr_arg.as_ref()?, loc.call_site)
|
||||
}
|
||||
MacroDefKind::BuiltInDerive(expander, ..) => {
|
||||
// this cast is a bit sus, can we avoid losing the typedness here?
|
||||
|
@ -286,11 +281,7 @@ pub fn expand_speculative(
|
|||
|
||||
let syntax_node = node.syntax_node();
|
||||
let token = rev_tmap
|
||||
.ranges_with_span(tt::SpanData {
|
||||
range: token_to_map.text_range(),
|
||||
anchor: SpanAnchor::DUMMY,
|
||||
ctx: SyntaxContextId::DUMMY,
|
||||
})
|
||||
.ranges_with_span(span_map.span_for_range(token_to_map.text_range()))
|
||||
.filter_map(|range| syntax_node.covering_element(range).into_token())
|
||||
.min_by_key(|t| {
|
||||
// prefer tokens of the same kind and text
|
||||
|
@ -453,7 +444,7 @@ fn macro_arg(
|
|||
|
||||
if loc.def.is_proc_macro() {
|
||||
// proc macros expect their inputs without parentheses, MBEs expect it with them included
|
||||
tt.delimiter = tt::Delimiter::UNSPECIFIED;
|
||||
tt.delimiter = tt::Delimiter::DUMMY_INVISIBLE;
|
||||
}
|
||||
|
||||
if matches!(loc.def.kind, MacroDefKind::BuiltInEager(..)) {
|
||||
|
@ -611,7 +602,7 @@ fn macro_expand(
|
|||
let Some((macro_arg, undo_info)) = value else {
|
||||
return ExpandResult {
|
||||
value: Arc::new(tt::Subtree {
|
||||
delimiter: tt::Delimiter::UNSPECIFIED,
|
||||
delimiter: tt::Delimiter::DUMMY_INVISIBLE,
|
||||
token_trees: Vec::new(),
|
||||
}),
|
||||
// FIXME: We should make sure to enforce an invariant that invalid macro
|
||||
|
@ -683,7 +674,7 @@ fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<A
|
|||
let Some((macro_arg, undo_info)) = db.macro_arg(id).value else {
|
||||
return ExpandResult {
|
||||
value: Arc::new(tt::Subtree {
|
||||
delimiter: tt::Delimiter::UNSPECIFIED,
|
||||
delimiter: tt::Delimiter::DUMMY_INVISIBLE,
|
||||
token_trees: Vec::new(),
|
||||
}),
|
||||
// FIXME: We should make sure to enforce an invariant that invalid macro
|
||||
|
@ -698,7 +689,7 @@ fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<A
|
|||
};
|
||||
|
||||
let attr_arg = match &loc.kind {
|
||||
MacroCallKind::Attr { attr_args, .. } => Some(&**attr_args),
|
||||
MacroCallKind::Attr { attr_args: Some(attr_args), .. } => Some(&**attr_args),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
|
@ -749,7 +740,7 @@ fn check_tt_count(tt: &tt::Subtree) -> Result<(), ExpandResult<Arc<tt::Subtree>>
|
|||
if TOKEN_LIMIT.check(count).is_err() {
|
||||
Err(ExpandResult {
|
||||
value: Arc::new(tt::Subtree {
|
||||
delimiter: tt::Delimiter::UNSPECIFIED,
|
||||
delimiter: tt::Delimiter::DUMMY_INVISIBLE,
|
||||
token_trees: vec![],
|
||||
}),
|
||||
err: Some(ExpandError::other(format!(
|
||||
|
|
|
@ -18,10 +18,7 @@
|
|||
//!
|
||||
//!
|
||||
//! See the full discussion : <https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/Eager.20expansion.20of.20built-in.20macros>
|
||||
use base_db::{
|
||||
span::{SpanAnchor, SyntaxContextId},
|
||||
CrateId,
|
||||
};
|
||||
use base_db::{span::SyntaxContextId, CrateId, FileId};
|
||||
use rustc_hash::FxHashMap;
|
||||
use syntax::{ted, Parse, SyntaxNode, TextRange, TextSize, WalkEvent};
|
||||
use triomphe::Arc;
|
||||
|
@ -79,12 +76,10 @@ pub fn expand_eager_macro_input(
|
|||
};
|
||||
|
||||
// FIXME: Spans!
|
||||
let mut subtree = mbe::syntax_node_to_token_tree(
|
||||
&expanded_eager_input,
|
||||
RealSpanMap::absolute(<SpanAnchor as tt::SpanAnchor>::DUMMY.file_id),
|
||||
);
|
||||
let mut subtree =
|
||||
mbe::syntax_node_to_token_tree(&expanded_eager_input, RealSpanMap::absolute(FileId::BOGUS));
|
||||
|
||||
subtree.delimiter = crate::tt::Delimiter::UNSPECIFIED;
|
||||
subtree.delimiter = crate::tt::Delimiter::DUMMY_INVISIBLE;
|
||||
|
||||
let loc = MacroCallLoc {
|
||||
def,
|
||||
|
|
|
@ -61,6 +61,7 @@ pub mod tt {
|
|||
pub use tt::{DelimiterKind, Spacing, Span, SpanAnchor};
|
||||
|
||||
pub type Delimiter = ::tt::Delimiter<SpanData>;
|
||||
pub type DelimSpan = ::tt::DelimSpan<SpanData>;
|
||||
pub type Subtree = ::tt::Subtree<SpanData>;
|
||||
pub type Leaf = ::tt::Leaf<SpanData>;
|
||||
pub type Literal = ::tt::Literal<SpanData>;
|
||||
|
@ -160,7 +161,7 @@ pub enum MacroCallKind {
|
|||
},
|
||||
Attr {
|
||||
ast_id: AstId<ast::Item>,
|
||||
attr_args: Arc<tt::Subtree>,
|
||||
attr_args: Option<Arc<tt::Subtree>>,
|
||||
/// Syntactical index of the invoking `#[attribute]`.
|
||||
///
|
||||
/// Outer attributes are counted first, then inner attributes. This does not support
|
||||
|
@ -699,7 +700,7 @@ impl ExpansionInfo {
|
|||
let (macro_arg, _) = db.macro_arg(macro_file.macro_call_id).value.unwrap_or_else(|| {
|
||||
(
|
||||
Arc::new(tt::Subtree {
|
||||
delimiter: tt::Delimiter::UNSPECIFIED,
|
||||
delimiter: tt::Delimiter::DUMMY_INVISIBLE,
|
||||
token_trees: Vec::new(),
|
||||
}),
|
||||
SyntaxFixupUndoInfo::NONE,
|
||||
|
|
|
@ -38,9 +38,10 @@ impl ProcMacroExpander {
|
|||
mixed_site: SpanData,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
match self.proc_macro_id {
|
||||
ProcMacroId(DUMMY_ID) => {
|
||||
ExpandResult::new(tt::Subtree::empty(), ExpandError::UnresolvedProcMacro(def_crate))
|
||||
}
|
||||
ProcMacroId(DUMMY_ID) => ExpandResult::new(
|
||||
tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }),
|
||||
ExpandError::UnresolvedProcMacro(def_crate),
|
||||
),
|
||||
ProcMacroId(id) => {
|
||||
let proc_macros = db.proc_macros();
|
||||
let proc_macros = match proc_macros.get(&def_crate) {
|
||||
|
@ -48,7 +49,7 @@ impl ProcMacroExpander {
|
|||
Some(Err(_)) | None => {
|
||||
never!("Non-dummy expander even though there are no proc macros");
|
||||
return ExpandResult::new(
|
||||
tt::Subtree::empty(),
|
||||
tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }),
|
||||
ExpandError::other("Internal error"),
|
||||
);
|
||||
}
|
||||
|
@ -62,7 +63,7 @@ impl ProcMacroExpander {
|
|||
id
|
||||
);
|
||||
return ExpandResult::new(
|
||||
tt::Subtree::empty(),
|
||||
tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }),
|
||||
ExpandError::other("Internal error"),
|
||||
);
|
||||
}
|
||||
|
@ -82,9 +83,10 @@ impl ProcMacroExpander {
|
|||
ExpandResult { value: tt.clone(), err: Some(ExpandError::other(text)) }
|
||||
}
|
||||
ProcMacroExpansionError::System(text)
|
||||
| ProcMacroExpansionError::Panic(text) => {
|
||||
ExpandResult::new(tt::Subtree::empty(), ExpandError::other(text))
|
||||
}
|
||||
| ProcMacroExpansionError::Panic(text) => ExpandResult::new(
|
||||
tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }),
|
||||
ExpandError::other(text),
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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::SpanData as crate::tt::Span>::DUMMY,
|
||||
close: <crate::tt::SpanData as crate::tt::Span>::DUMMY,
|
||||
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::SpanData as crate::tt::Span>::DUMMY,
|
||||
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::SpanData as crate::tt::Span>::DUMMY,
|
||||
span: $span,
|
||||
}).into(),
|
||||
crate::tt::Leaf::Punct(crate::tt::Punct {
|
||||
char: $sec,
|
||||
spacing: crate::tt::Spacing::Alone,
|
||||
span: <crate::tt::SpanData as crate::tt::Span>::DUMMY,
|
||||
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::SpanData as crate::tt::Span>::DUMMY,
|
||||
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 '!')};
|
||||
($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) '!')};
|
||||
|
||||
( $first:tt $($tail:tt)+ ) => {
|
||||
($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,41 +200,45 @@ macro_rules! impl_to_to_tokentrees {
|
|||
}
|
||||
|
||||
impl_to_to_tokentrees! {
|
||||
u32 => self { crate::tt::Literal{text: self.to_string().into(), span: <crate::tt::SpanData as crate::tt::Span>::DUMMY} };
|
||||
usize => self { crate::tt::Literal{text: self.to_string().into(), span: <crate::tt::SpanData as crate::tt::Span>::DUMMY} };
|
||||
i32 => self { crate::tt::Literal{text: self.to_string().into(), span: <crate::tt::SpanData as crate::tt::Span>::DUMMY} };
|
||||
bool => self { crate::tt::Ident{text: self.to_string().into(), span: <crate::tt::SpanData as crate::tt::Span>::DUMMY} };
|
||||
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::SpanData as crate::tt::Span>::DUMMY}};
|
||||
String => self { crate::tt::Literal{text: format!("\"{}\"", self.escape_default()).into(), span: <crate::tt::SpanData as crate::tt::Span>::DUMMY}}
|
||||
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 ::tt::Span;
|
||||
use expect_test::expect;
|
||||
|
||||
const DUMMY: tt::SpanData = tt::SpanData::DUMMY;
|
||||
|
||||
#[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 {
|
||||
|
@ -243,7 +252,7 @@ mod tests {
|
|||
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:?}");
|
||||
expect![[r#"
|
||||
|
@ -255,7 +264,7 @@ mod tests {
|
|||
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 {}
|
||||
|
@ -275,7 +284,8 @@ 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 {
|
||||
|
@ -286,7 +296,7 @@ mod tests {
|
|||
token_trees: fields.collect(),
|
||||
};
|
||||
|
||||
let quoted = quote! {
|
||||
let quoted = quote! {DUMMY =>
|
||||
impl Clone for #struct_name {
|
||||
fn clone(&self) -> Self {
|
||||
Self #list
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue