Merge commit 'aa9bc86125' into sync-from-ra

This commit is contained in:
Laurențiu Nicola 2023-06-05 12:04:23 +03:00
parent 1570299af4
commit c48062fe2a
598 changed files with 57696 additions and 17615 deletions

View file

@ -115,6 +115,7 @@ impl AstIdMap {
}
}
}
res.arena.shrink_to_fit();
res
}
@ -123,6 +124,10 @@ impl AstIdMap {
FileAstId { raw, _ty: PhantomData }
}
pub fn get<N: AstNode>(&self, id: FileAstId<N>) -> AstPtr<N> {
AstPtr::try_from_raw(self.arena[id.raw].clone()).unwrap()
}
fn erased_ast_id(&self, item: &SyntaxNode) -> ErasedFileAstId {
let ptr = SyntaxNodePtr::new(item);
let hash = hash_ptr(&ptr);
@ -136,10 +141,6 @@ impl AstIdMap {
}
}
pub fn get<N: AstNode>(&self, id: FileAstId<N>) -> AstPtr<N> {
AstPtr::try_from_raw(self.arena[id.raw].clone()).unwrap()
}
fn alloc(&mut self, item: &SyntaxNode) -> ErasedFileAstId {
self.arena.alloc(SyntaxNodePtr::new(item))
}

View file

@ -1,5 +1,5 @@
//! A higher level attributes based on TokenTree, with also some shortcuts.
use std::{fmt, ops, sync::Arc};
use std::{fmt, ops};
use base_db::CrateId;
use cfg::CfgExpr;
@ -8,12 +8,12 @@ use intern::Interned;
use mbe::{syntax_node_to_token_tree, DelimiterKind, Punct};
use smallvec::{smallvec, SmallVec};
use syntax::{ast, match_ast, AstNode, SmolStr, SyntaxNode};
use triomphe::Arc;
use crate::{
db::ExpandDatabase,
hygiene::Hygiene,
mod_path::{ModPath, PathKind},
name::AsName,
mod_path::ModPath,
tt::{self, Subtree},
InFile,
};
@ -21,6 +21,7 @@ use crate::{
/// Syntactical attributes, without filtering of `cfg_attr`s.
#[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct RawAttrs {
// FIXME: Make this a ThinArc
entries: Option<Arc<[Attr]>>,
}
@ -50,7 +51,9 @@ impl RawAttrs {
path: Interned::new(ModPath::from(crate::name!(doc))),
}),
})
.collect::<Arc<_>>();
.collect::<Vec<_>>();
// FIXME: use `Arc::from_iter` when it becomes available
let entries: Arc<[Attr]> = Arc::from(entries);
Self { entries: if entries.is_empty() { None } else { Some(entries) } }
}
@ -68,7 +71,7 @@ impl RawAttrs {
(Some(a), Some(b)) => {
let last_ast_index = a.last().map_or(0, |it| it.id.ast_index() + 1) as u32;
Self {
entries: Some(
entries: Some(Arc::from(
a.iter()
.cloned()
.chain(b.iter().map(|it| {
@ -78,8 +81,9 @@ impl RawAttrs {
<< AttrId::AST_INDEX_BITS;
it
}))
.collect(),
),
// FIXME: use `Arc::from_iter` when it becomes available
.collect::<Vec<_>>(),
)),
}
}
}
@ -96,48 +100,51 @@ impl RawAttrs {
}
let crate_graph = db.crate_graph();
let new_attrs = self
.iter()
.flat_map(|attr| -> SmallVec<[_; 1]> {
let is_cfg_attr =
attr.path.as_ident().map_or(false, |name| *name == crate::name![cfg_attr]);
if !is_cfg_attr {
return smallvec![attr.clone()];
}
let new_attrs = Arc::from(
self.iter()
.flat_map(|attr| -> SmallVec<[_; 1]> {
let is_cfg_attr =
attr.path.as_ident().map_or(false, |name| *name == crate::name![cfg_attr]);
if !is_cfg_attr {
return smallvec![attr.clone()];
}
let subtree = match attr.token_tree_value() {
Some(it) => it,
_ => return smallvec![attr.clone()],
};
let subtree = match attr.token_tree_value() {
Some(it) => it,
_ => return smallvec![attr.clone()],
};
let (cfg, parts) = match parse_cfg_attr_input(subtree) {
Some(it) => it,
None => return smallvec![attr.clone()],
};
let index = attr.id;
let attrs =
parts.enumerate().take(1 << AttrId::CFG_ATTR_BITS).filter_map(|(idx, attr)| {
let tree = Subtree {
delimiter: tt::Delimiter::unspecified(),
token_trees: attr.to_vec(),
};
// FIXME hygiene
let hygiene = Hygiene::new_unhygienic();
Attr::from_tt(db, &tree, &hygiene, index.with_cfg_attr(idx))
});
let (cfg, parts) = match parse_cfg_attr_input(subtree) {
Some(it) => it,
None => return smallvec![attr.clone()],
};
let index = attr.id;
let attrs = parts.enumerate().take(1 << AttrId::CFG_ATTR_BITS).filter_map(
|(idx, attr)| {
let tree = Subtree {
delimiter: tt::Delimiter::unspecified(),
token_trees: attr.to_vec(),
};
// FIXME hygiene
let hygiene = Hygiene::new_unhygienic();
Attr::from_tt(db, &tree, &hygiene, index.with_cfg_attr(idx))
},
);
let cfg_options = &crate_graph[krate].cfg_options;
let cfg = Subtree { delimiter: subtree.delimiter, token_trees: cfg.to_vec() };
let cfg = CfgExpr::parse(&cfg);
if cfg_options.check(&cfg) == Some(false) {
smallvec![]
} else {
cov_mark::hit!(cfg_attr_active);
let cfg_options = &crate_graph[krate].cfg_options;
let cfg = Subtree { delimiter: subtree.delimiter, token_trees: cfg.to_vec() };
let cfg = CfgExpr::parse(&cfg);
if cfg_options.check(&cfg) == Some(false) {
smallvec![]
} else {
cov_mark::hit!(cfg_attr_active);
attrs.collect()
}
})
.collect();
attrs.collect()
}
})
// FIXME: use `Arc::from_iter` when it becomes available
.collect::<Vec<_>>(),
);
RawAttrs { entries: Some(new_attrs) }
}
@ -266,7 +273,11 @@ impl Attr {
}
/// Parses this attribute as a token tree consisting of comma separated paths.
pub fn parse_path_comma_token_tree(&self) -> Option<impl Iterator<Item = ModPath> + '_> {
pub fn parse_path_comma_token_tree<'a>(
&'a self,
db: &'a dyn ExpandDatabase,
hygiene: &'a Hygiene,
) -> Option<impl Iterator<Item = ModPath> + 'a> {
let args = self.token_tree_value()?;
if args.delimiter.kind != DelimiterKind::Parenthesis {
@ -275,19 +286,37 @@ impl Attr {
let paths = args
.token_trees
.split(|tt| matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Punct(Punct { char: ',', .. }))))
.filter_map(|tts| {
.filter_map(move |tts| {
if tts.is_empty() {
return None;
}
let segments = tts.iter().filter_map(|tt| match tt {
tt::TokenTree::Leaf(tt::Leaf::Ident(id)) => Some(id.as_name()),
_ => None,
});
Some(ModPath::from_segments(PathKind::Plain, segments))
// FIXME: This is necessarily a hack. It'd be nice if we could avoid allocation here.
let subtree = tt::Subtree {
delimiter: tt::Delimiter::unspecified(),
token_trees: tts.into_iter().cloned().collect(),
};
let (parse, _) =
mbe::token_tree_to_syntax_node(&subtree, mbe::TopEntryPoint::MetaItem);
let meta = ast::Meta::cast(parse.syntax_node())?;
// Only simple paths are allowed.
if meta.eq_token().is_some() || meta.expr().is_some() || meta.token_tree().is_some()
{
return None;
}
let path = meta.path()?;
ModPath::from_src(db, path, hygiene)
});
Some(paths)
}
pub fn cfg(&self) -> Option<CfgExpr> {
if *self.path.as_ident()? == crate::name![cfg] {
self.token_tree_value().map(CfgExpr::parse)
} else {
None
}
}
}
pub fn collect_attrs(

View file

@ -96,7 +96,7 @@ fn derive_attr_expand(
) -> ExpandResult<tt::Subtree> {
let loc = db.lookup_intern_macro_call(id);
let derives = match &loc.kind {
MacroCallKind::Attr { attr_args, is_derive: true, .. } => &attr_args.0,
MacroCallKind::Attr { attr_args, .. } if loc.def.is_attribute_derive() => &attr_args.0,
_ => return ExpandResult::ok(tt::Subtree::empty()),
};
pseudo_derive_attr_expansion(tt, derives)

View file

@ -1,11 +1,19 @@
//! Builtin derives.
use ::tt::Ident;
use base_db::{CrateOrigin, LangCrateOrigin};
use itertools::izip;
use mbe::TokenMap;
use std::collections::HashSet;
use stdx::never;
use tracing::debug;
use crate::tt::{self, TokenId};
use syntax::{
ast::{self, AstNode, HasGenericParams, HasModuleItem, HasName},
ast::{
self, AstNode, FieldList, HasAttrs, HasGenericParams, HasModuleItem, HasName,
HasTypeBounds, PathType,
},
match_ast,
};
@ -58,10 +66,129 @@ pub fn find_builtin_derive(ident: &name::Name) -> Option<BuiltinDeriveExpander>
BuiltinDeriveExpander::find_by_name(ident)
}
enum VariantShape {
Struct(Vec<tt::Ident>),
Tuple(usize),
Unit,
}
fn tuple_field_iterator(n: usize) -> impl Iterator<Item = tt::Ident> {
(0..n).map(|x| Ident::new(format!("f{x}"), tt::TokenId::unspecified()))
}
impl VariantShape {
fn as_pattern(&self, path: tt::Subtree) -> tt::Subtree {
self.as_pattern_map(path, |x| quote!(#x))
}
fn field_names(&self) -> Vec<tt::Ident> {
match self {
VariantShape::Struct(s) => s.clone(),
VariantShape::Tuple(n) => tuple_field_iterator(*n).collect(),
VariantShape::Unit => vec![],
}
}
fn as_pattern_map(
&self,
path: tt::Subtree,
field_map: impl Fn(&tt::Ident) -> tt::Subtree,
) -> tt::Subtree {
match self {
VariantShape::Struct(fields) => {
let fields = fields.iter().map(|x| {
let mapped = field_map(x);
quote! { #x : #mapped , }
});
quote! {
#path { ##fields }
}
}
&VariantShape::Tuple(n) => {
let fields = tuple_field_iterator(n).map(|x| {
let mapped = field_map(&x);
quote! {
#mapped ,
}
});
quote! {
#path ( ##fields )
}
}
VariantShape::Unit => path,
}
}
fn from(value: Option<FieldList>, token_map: &TokenMap) -> Result<Self, ExpandError> {
let r = match value {
None => VariantShape::Unit,
Some(FieldList::RecordFieldList(x)) => VariantShape::Struct(
x.fields()
.map(|x| x.name())
.map(|x| name_to_token(token_map, x))
.collect::<Result<_, _>>()?,
),
Some(FieldList::TupleFieldList(x)) => VariantShape::Tuple(x.fields().count()),
};
Ok(r)
}
}
enum AdtShape {
Struct(VariantShape),
Enum { variants: Vec<(tt::Ident, VariantShape)>, default_variant: Option<usize> },
Union,
}
impl AdtShape {
fn as_pattern(&self, name: &tt::Ident) -> Vec<tt::Subtree> {
self.as_pattern_map(name, |x| quote!(#x))
}
fn field_names(&self) -> Vec<Vec<tt::Ident>> {
match self {
AdtShape::Struct(s) => {
vec![s.field_names()]
}
AdtShape::Enum { variants, .. } => {
variants.iter().map(|(_, fields)| fields.field_names()).collect()
}
AdtShape::Union => {
never!("using fields of union in derive is always wrong");
vec![]
}
}
}
fn as_pattern_map(
&self,
name: &tt::Ident,
field_map: impl Fn(&tt::Ident) -> tt::Subtree,
) -> Vec<tt::Subtree> {
match self {
AdtShape::Struct(s) => {
vec![s.as_pattern_map(quote! { #name }, field_map)]
}
AdtShape::Enum { variants, .. } => variants
.iter()
.map(|(v, fields)| fields.as_pattern_map(quote! { #name :: #v }, &field_map))
.collect(),
AdtShape::Union => {
never!("pattern matching on union is always wrong");
vec![quote! { un }]
}
}
}
}
struct BasicAdtInfo {
name: tt::Ident,
/// `Some(ty)` if it's a const param of type `ty`, `None` if it's a type param.
param_types: Vec<Option<tt::Subtree>>,
shape: AdtShape,
/// first field is the name, and
/// second field is `Some(ty)` if it's a const param of type `ty`, `None` if it's a type param.
/// third fields is where bounds, if any
param_types: Vec<(tt::Subtree, Option<tt::Subtree>, Option<tt::Subtree>)>,
associated_types: Vec<tt::Subtree>,
}
fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
@ -75,29 +202,52 @@ fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
ExpandError::Other("no item found".into())
})?;
let node = item.syntax();
let (name, params) = match_ast! {
let (name, params, shape) = match_ast! {
match node {
ast::Struct(it) => (it.name(), it.generic_param_list()),
ast::Enum(it) => (it.name(), it.generic_param_list()),
ast::Union(it) => (it.name(), it.generic_param_list()),
ast::Struct(it) => (it.name(), it.generic_param_list(), AdtShape::Struct(VariantShape::from(it.field_list(), &token_map)?)),
ast::Enum(it) => {
let default_variant = it.variant_list().into_iter().flat_map(|x| x.variants()).position(|x| x.attrs().any(|x| x.simple_name() == Some("default".into())));
(
it.name(),
it.generic_param_list(),
AdtShape::Enum {
default_variant,
variants: it.variant_list()
.into_iter()
.flat_map(|x| x.variants())
.map(|x| Ok((name_to_token(&token_map,x.name())?, VariantShape::from(x.field_list(), &token_map)?))).collect::<Result<_, ExpandError>>()?
}
)
},
ast::Union(it) => (it.name(), it.generic_param_list(), AdtShape::Union),
_ => {
debug!("unexpected node is {:?}", node);
return Err(ExpandError::Other("expected struct, enum or union".into()))
},
}
};
let name = name.ok_or_else(|| {
debug!("parsed item has no name");
ExpandError::Other("missing name".into())
})?;
let name_token_id =
token_map.token_by_range(name.syntax().text_range()).unwrap_or_else(TokenId::unspecified);
let name_token = tt::Ident { span: name_token_id, text: name.text().into() };
let mut param_type_set: HashSet<String> = HashSet::new();
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 name = {
let this = param.name();
match this {
Some(x) => {
param_type_set.insert(x.to_string());
mbe::syntax_node_to_token_tree(x.syntax()).0
}
None => tt::Subtree::empty(),
}
};
let bounds = match &param {
ast::TypeOrConstParam::Type(x) => {
x.type_bound_list().map(|x| mbe::syntax_node_to_token_tree(x.syntax()).0)
}
ast::TypeOrConstParam::Const(_) => None,
};
let ty = if let ast::TypeOrConstParam::Const(param) = param {
let ty = param
.ty()
.map(|ty| mbe::syntax_node_to_token_tree(ty.syntax()).0)
@ -105,27 +255,107 @@ fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
Some(ty)
} else {
None
}
};
(name, ty, bounds)
})
.collect();
Ok(BasicAdtInfo { name: name_token, param_types })
let is_associated_type = |p: &PathType| {
if let Some(p) = p.path() {
if let Some(parent) = p.qualifier() {
if let Some(x) = parent.segment() {
if let Some(x) = x.path_type() {
if let Some(x) = x.path() {
if let Some(pname) = x.as_single_name_ref() {
if param_type_set.contains(&pname.to_string()) {
// <T as Trait>::Assoc
return true;
}
}
}
}
}
if let Some(pname) = parent.as_single_name_ref() {
if param_type_set.contains(&pname.to_string()) {
// T::Assoc
return true;
}
}
}
}
false
};
let associated_types = node
.descendants()
.filter_map(PathType::cast)
.filter(is_associated_type)
.map(|x| mbe::syntax_node_to_token_tree(x.syntax()).0)
.collect::<Vec<_>>();
let name_token = name_to_token(&token_map, name)?;
Ok(BasicAdtInfo { name: name_token, shape, param_types, associated_types })
}
fn expand_simple_derive(tt: &tt::Subtree, trait_path: tt::Subtree) -> ExpandResult<tt::Subtree> {
fn name_to_token(token_map: &TokenMap, name: Option<ast::Name>) -> Result<tt::Ident, ExpandError> {
let name = name.ok_or_else(|| {
debug!("parsed item has no name");
ExpandError::Other("missing name".into())
})?;
let name_token_id =
token_map.token_by_range(name.syntax().text_range()).unwrap_or_else(TokenId::unspecified);
let name_token = tt::Ident { span: name_token_id, text: name.text().into() };
Ok(name_token)
}
/// Given that we are deriving a trait `DerivedTrait` for a type like:
///
/// ```ignore (only-for-syntax-highlight)
/// struct Struct<'a, ..., 'z, A, B: DeclaredTrait, C, ..., Z> where C: WhereTrait {
/// a: A,
/// b: B::Item,
/// b1: <B as DeclaredTrait>::Item,
/// c1: <C as WhereTrait>::Item,
/// c2: Option<<C as WhereTrait>::Item>,
/// ...
/// }
/// ```
///
/// create an impl like:
///
/// ```ignore (only-for-syntax-highlight)
/// impl<'a, ..., 'z, A, B: DeclaredTrait, C, ... Z> where
/// C: WhereTrait,
/// A: DerivedTrait + B1 + ... + BN,
/// B: DerivedTrait + B1 + ... + BN,
/// C: DerivedTrait + B1 + ... + BN,
/// B::Item: DerivedTrait + B1 + ... + BN,
/// <C as WhereTrait>::Item: DerivedTrait + B1 + ... + BN,
/// ...
/// {
/// ...
/// }
/// ```
///
/// where B1, ..., BN are the bounds given by `bounds_paths`.'. Z is a phantom type, and
/// therefore does not get bound by the derived trait.
fn expand_simple_derive(
tt: &tt::Subtree,
trait_path: tt::Subtree,
trait_body: impl FnOnce(&BasicAdtInfo) -> tt::Subtree,
) -> ExpandResult<tt::Subtree> {
let info = match parse_adt(tt) {
Ok(info) => info,
Err(e) => return ExpandResult::with_err(tt::Subtree::empty(), e),
Err(e) => return ExpandResult::new(tt::Subtree::empty(), e),
};
let trait_body = trait_body(&info);
let mut where_block = vec![];
let (params, args): (Vec<_>, Vec<_>) = info
.param_types
.into_iter()
.enumerate()
.map(|(idx, param_ty)| {
let ident = tt::Leaf::Ident(tt::Ident {
span: tt::TokenId::unspecified(),
text: format!("T{idx}").into(),
});
.map(|(ident, param_ty, bound)| {
let ident_ = ident.clone();
if let Some(b) = bound {
let ident = ident.clone();
where_block.push(quote! { #ident : #b , });
}
if let Some(ty) = param_ty {
(quote! { const #ident : #ty , }, quote! { #ident_ , })
} else {
@ -134,9 +364,16 @@ fn expand_simple_derive(tt: &tt::Subtree, trait_path: tt::Subtree) -> ExpandResu
}
})
.unzip();
where_block.extend(info.associated_types.iter().map(|x| {
let x = x.clone();
let bound = trait_path.clone();
quote! { #x : #bound , }
}));
let name = info.name;
let expanded = quote! {
impl < ##params > #trait_path for #name < ##args > {}
impl < ##params > #trait_path for #name < ##args > where ##where_block { #trait_body }
};
ExpandResult::ok(expanded)
}
@ -163,7 +400,7 @@ fn copy_expand(
tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> {
let krate = find_builtin_crate(db, id);
expand_simple_derive(tt, quote! { #krate::marker::Copy })
expand_simple_derive(tt, quote! { #krate::marker::Copy }, |_| quote! {})
}
fn clone_expand(
@ -172,7 +409,63 @@ fn clone_expand(
tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> {
let krate = find_builtin_crate(db, id);
expand_simple_derive(tt, quote! { #krate::clone::Clone })
expand_simple_derive(tt, quote! { #krate::clone::Clone }, |adt| {
if matches!(adt.shape, AdtShape::Union) {
let star = tt::Punct {
char: '*',
spacing: ::tt::Spacing::Alone,
span: tt::TokenId::unspecified(),
};
return quote! {
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::TokenId::unspecified(),
};
return quote! {
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, |x| quote! { #x .clone() });
let arms = patterns.into_iter().zip(exprs.into_iter()).map(|(pat, expr)| {
let fat_arrow = fat_arrow();
quote! {
#pat #fat_arrow #expr,
}
});
quote! {
fn clone(&self) -> Self {
match self {
##arms
}
}
}
})
}
/// This function exists since `quote! { => }` doesn't work.
fn fat_arrow() -> ::tt::Subtree<TokenId> {
let eq =
tt::Punct { char: '=', spacing: ::tt::Spacing::Joint, span: tt::TokenId::unspecified() };
quote! { #eq> }
}
/// This function exists since `quote! { && }` doesn't work.
fn and_and() -> ::tt::Subtree<TokenId> {
let and =
tt::Punct { char: '&', spacing: ::tt::Spacing::Joint, span: tt::TokenId::unspecified() };
quote! { #and& }
}
fn default_expand(
@ -180,8 +473,38 @@ fn default_expand(
id: MacroCallId,
tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> {
let krate = find_builtin_crate(db, id);
expand_simple_derive(tt, quote! { #krate::default::Default })
let krate = &find_builtin_crate(db, id);
expand_simple_derive(tt, quote! { #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()))
}
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()),
)
} else {
// FIXME: Return expand error here
quote!()
}
}
AdtShape::Union => {
// FIXME: Return expand error here
quote!()
}
};
quote! {
fn default() -> Self {
#body
}
}
})
}
fn debug_expand(
@ -189,8 +512,79 @@ fn debug_expand(
id: MacroCallId,
tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> {
let krate = find_builtin_crate(db, id);
expand_simple_derive(tt, quote! { #krate::fmt::Debug })
let krate = &find_builtin_crate(db, id);
expand_simple_derive(tt, quote! { #krate::fmt::Debug }, |adt| {
let for_variant = |name: String, v: &VariantShape| match v {
VariantShape::Struct(fields) => {
let for_fields = fields.iter().map(|x| {
let x_string = x.to_string();
quote! {
.field(#x_string, & #x)
}
});
quote! {
f.debug_struct(#name) ##for_fields .finish()
}
}
VariantShape::Tuple(n) => {
let for_fields = tuple_field_iterator(*n).map(|x| {
quote! {
.field( & #x)
}
});
quote! {
f.debug_tuple(#name) ##for_fields .finish()
}
}
VariantShape::Unit => quote! {
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::TokenId::unspecified(),
};
return quote! {
fn fmt(&self, f: &mut #krate::fmt::Formatter) -> #krate::fmt::Result {
match #star self {}
}
};
}
let arms = match &adt.shape {
AdtShape::Struct(fields) => {
let fat_arrow = fat_arrow();
let name = &adt.name;
let pat = fields.as_pattern(quote!(#name));
let expr = for_variant(name.to_string(), fields);
vec![quote! { #pat #fat_arrow #expr }]
}
AdtShape::Enum { variants, .. } => variants
.iter()
.map(|(name, v)| {
let fat_arrow = fat_arrow();
let adt_name = &adt.name;
let pat = v.as_pattern(quote!(#adt_name :: #name));
let expr = for_variant(name.to_string(), v);
quote! {
#pat #fat_arrow #expr ,
}
})
.collect(),
AdtShape::Union => {
// FIXME: Return expand error here
vec![]
}
};
quote! {
fn fmt(&self, f: &mut #krate::fmt::Formatter) -> #krate::fmt::Result {
match self {
##arms
}
}
}
})
}
fn hash_expand(
@ -198,8 +592,47 @@ fn hash_expand(
id: MacroCallId,
tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> {
let krate = find_builtin_crate(db, id);
expand_simple_derive(tt, quote! { #krate::hash::Hash })
let krate = &find_builtin_crate(db, id);
expand_simple_derive(tt, quote! { #krate::hash::Hash }, |adt| {
if matches!(adt.shape, AdtShape::Union) {
// FIXME: Return expand error here
return quote! {};
}
if matches!(&adt.shape, AdtShape::Enum { variants, .. } if variants.is_empty()) {
let star = tt::Punct {
char: '*',
spacing: ::tt::Spacing::Alone,
span: tt::TokenId::unspecified(),
};
return quote! {
fn hash<H: #krate::hash::Hasher>(&self, 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(|x| quote! { #x . hash(state); });
quote! { {
##it
} }
};
let fat_arrow = fat_arrow();
quote! {
#pat #fat_arrow #expr ,
}
},
);
quote! {
fn hash<H: #krate::hash::Hasher>(&self, state: &mut H) {
#krate::mem::discriminant(self).hash(state);
match self {
##arms
}
}
}
})
}
fn eq_expand(
@ -208,7 +641,7 @@ fn eq_expand(
tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> {
let krate = find_builtin_crate(db, id);
expand_simple_derive(tt, quote! { #krate::cmp::Eq })
expand_simple_derive(tt, quote! { #krate::cmp::Eq }, |_| quote! {})
}
fn partial_eq_expand(
@ -217,7 +650,65 @@ fn partial_eq_expand(
tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> {
let krate = find_builtin_crate(db, id);
expand_simple_derive(tt, quote! { #krate::cmp::PartialEq })
expand_simple_derive(tt, quote! { #krate::cmp::PartialEq }, |adt| {
if matches!(adt.shape, AdtShape::Union) {
// FIXME: Return expand error here
return quote! {};
}
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(
|(pat1, pat2, names)| {
let fat_arrow = fat_arrow();
let body = match &*names {
[] => {
quote!(true)
}
[first, rest @ ..] => {
let rest = rest.iter().map(|x| {
let t1 = Ident::new(format!("{}_self", x.text), x.span);
let t2 = Ident::new(format!("{}_other", x.text), x.span);
let and_and = and_and();
quote!(#and_and #t1 .eq( #t2 ))
});
let first = {
let t1 = Ident::new(format!("{}_self", first.text), first.span);
let t2 = Ident::new(format!("{}_other", first.text), first.span);
quote!(#t1 .eq( #t2 ))
};
quote!(#first ##rest)
}
};
quote! { ( #pat1 , #pat2 ) #fat_arrow #body , }
},
);
let fat_arrow = fat_arrow();
quote! {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
##arms
_unused #fat_arrow false
}
}
}
})
}
fn self_and_other_patterns(
adt: &BasicAdtInfo,
name: &tt::Ident,
) -> (Vec<tt::Subtree>, Vec<tt::Subtree>) {
let self_patterns = adt.shape.as_pattern_map(name, |x| {
let t = Ident::new(format!("{}_self", x.text), x.span);
quote!(#t)
});
let other_patterns = adt.shape.as_pattern_map(name, |x| {
let t = Ident::new(format!("{}_other", x.text), x.span);
quote!(#t)
});
(self_patterns, other_patterns)
}
fn ord_expand(
@ -225,8 +716,63 @@ fn ord_expand(
id: MacroCallId,
tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> {
let krate = find_builtin_crate(db, id);
expand_simple_derive(tt, quote! { #krate::cmp::Ord })
let krate = &find_builtin_crate(db, id);
expand_simple_derive(tt, quote! { #krate::cmp::Ord }, |adt| {
fn compare(
krate: &tt::TokenTree,
left: tt::Subtree,
right: tt::Subtree,
rest: tt::Subtree,
) -> tt::Subtree {
let fat_arrow1 = fat_arrow();
let fat_arrow2 = fat_arrow();
quote! {
match #left.cmp(&#right) {
#krate::cmp::Ordering::Equal #fat_arrow1 {
#rest
}
c #fat_arrow2 return c,
}
}
}
if matches!(adt.shape, AdtShape::Union) {
// FIXME: Return expand error here
return quote!();
}
let left = quote!(#krate::intrinsics::discriminant_value(self));
let right = quote!(#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(
|(pat1, pat2, fields)| {
let mut body = quote!(#krate::cmp::Ordering::Equal);
for f in fields.into_iter().rev() {
let t1 = Ident::new(format!("{}_self", f.text), f.span);
let t2 = Ident::new(format!("{}_other", f.text), f.span);
body = compare(krate, quote!(#t1), quote!(#t2), body);
}
let fat_arrow = fat_arrow();
quote! { ( #pat1 , #pat2 ) #fat_arrow #body , }
},
);
let fat_arrow = fat_arrow();
let body = compare(
krate,
left,
right,
quote! {
match (self, other) {
##arms
_unused #fat_arrow #krate::cmp::Ordering::Equal
}
},
);
quote! {
fn cmp(&self, other: &Self) -> #krate::cmp::Ordering {
#body
}
}
})
}
fn partial_ord_expand(
@ -234,6 +780,61 @@ fn partial_ord_expand(
id: MacroCallId,
tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> {
let krate = find_builtin_crate(db, id);
expand_simple_derive(tt, quote! { #krate::cmp::PartialOrd })
let krate = &find_builtin_crate(db, id);
expand_simple_derive(tt, quote! { #krate::cmp::PartialOrd }, |adt| {
fn compare(
krate: &tt::TokenTree,
left: tt::Subtree,
right: tt::Subtree,
rest: tt::Subtree,
) -> tt::Subtree {
let fat_arrow1 = fat_arrow();
let fat_arrow2 = fat_arrow();
quote! {
match #left.partial_cmp(&#right) {
#krate::option::Option::Some(#krate::cmp::Ordering::Equal) #fat_arrow1 {
#rest
}
c #fat_arrow2 return c,
}
}
}
if matches!(adt.shape, AdtShape::Union) {
// FIXME: Return expand error here
return quote!();
}
let left = quote!(#krate::intrinsics::discriminant_value(self));
let right = quote!(#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(
|(pat1, pat2, fields)| {
let mut body = quote!(#krate::option::Option::Some(#krate::cmp::Ordering::Equal));
for f in fields.into_iter().rev() {
let t1 = Ident::new(format!("{}_self", f.text), f.span);
let t2 = Ident::new(format!("{}_other", f.text), f.span);
body = compare(krate, quote!(#t1), quote!(#t2), body);
}
let fat_arrow = fat_arrow();
quote! { ( #pat1 , #pat2 ) #fat_arrow #body , }
},
);
let fat_arrow = fat_arrow();
let body = compare(
krate,
left,
right,
quote! {
match (self, other) {
##arms
_unused #fat_arrow #krate::option::Option::Some(#krate::cmp::Ordering::Equal)
}
},
);
quote! {
fn partial_cmp(&self, other: &Self) -> #krate::option::Option::Option<#krate::cmp::Ordering> {
#body
}
}
})
}

View file

@ -1,9 +1,13 @@
//! Builtin macro
use std::mem;
use ::tt::Ident;
use base_db::{AnchoredPath, Edition, FileId};
use cfg::CfgExpr;
use either::Either;
use mbe::{parse_exprs_with_sep, parse_to_token_tree};
use mbe::{parse_exprs_with_sep, parse_to_token_tree, TokenMap};
use rustc_hash::FxHashMap;
use syntax::{
ast::{self, AstToken},
SmolStr,
@ -67,7 +71,7 @@ macro_rules! register_builtin {
pub struct ExpandedEager {
pub(crate) subtree: tt::Subtree,
/// The included file ID of the include macro.
pub(crate) included_file: Option<FileId>,
pub(crate) included_file: Option<(FileId, TokenMap)>,
}
impl ExpandedEager {
@ -90,11 +94,6 @@ register_builtin! {
(module_path, ModulePath) => module_path_expand,
(assert, Assert) => assert_expand,
(stringify, Stringify) => stringify_expand,
(format_args, FormatArgs) => format_args_expand,
(const_format_args, ConstFormatArgs) => format_args_expand,
// format_args_nl only differs in that it adds a newline in the end,
// so we use the same stub expansion for now
(format_args_nl, FormatArgsNl) => format_args_expand,
(llvm_asm, LlvmAsm) => asm_expand,
(asm, Asm) => asm_expand,
(global_asm, GlobalAsm) => global_asm_expand,
@ -106,6 +105,9 @@ register_builtin! {
(trace_macros, TraceMacros) => trace_macros_expand,
EAGER:
(format_args, FormatArgs) => format_args_expand,
(const_format_args, ConstFormatArgs) => format_args_expand,
(format_args_nl, FormatArgsNl) => format_args_nl_expand,
(compile_error, CompileError) => compile_error_expand,
(concat, Concat) => concat_expand,
(concat_idents, ConcatIdents) => concat_idents_expand,
@ -135,9 +137,8 @@ fn line_expand(
_tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> {
// dummy implementation for type-checking purposes
let line_num = 0;
let expanded = quote! {
#line_num
0 as u32
};
ExpandResult::ok(expanded)
@ -179,9 +180,8 @@ fn column_expand(
_tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> {
// dummy implementation for type-checking purposes
let col_num = 0;
let expanded = quote! {
#col_num
0 as u32
};
ExpandResult::ok(expanded)
@ -234,45 +234,173 @@ fn file_expand(
}
fn format_args_expand(
db: &dyn ExpandDatabase,
id: MacroCallId,
tt: &tt::Subtree,
) -> ExpandResult<ExpandedEager> {
format_args_expand_general(db, id, tt, "")
.map(|x| ExpandedEager { subtree: x, included_file: None })
}
fn format_args_nl_expand(
db: &dyn ExpandDatabase,
id: MacroCallId,
tt: &tt::Subtree,
) -> ExpandResult<ExpandedEager> {
format_args_expand_general(db, id, tt, "\\n")
.map(|x| ExpandedEager { subtree: x, included_file: None })
}
fn format_args_expand_general(
_db: &dyn ExpandDatabase,
_id: MacroCallId,
tt: &tt::Subtree,
end_string: &str,
) -> ExpandResult<tt::Subtree> {
// We expand `format_args!("", a1, a2)` to
// ```
// $crate::fmt::Arguments::new_v1(&[], &[
// $crate::fmt::Argument::new(&arg1,$crate::fmt::Display::fmt),
// $crate::fmt::Argument::new(&arg2,$crate::fmt::Display::fmt),
// ])
// ```,
// which is still not really correct, but close enough for now
let mut args = parse_exprs_with_sep(tt, ',');
let args = parse_exprs_with_sep(tt, ',');
let expand_error =
ExpandResult::new(tt::Subtree::empty(), mbe::ExpandError::NoMatchingRule.into());
if args.is_empty() {
return ExpandResult::with_err(
tt::Subtree::empty(),
mbe::ExpandError::NoMatchingRule.into(),
);
return expand_error;
}
for arg in &mut args {
let mut key_args = FxHashMap::default();
let mut args = args.into_iter().filter_map(|mut arg| {
// Remove `key =`.
if matches!(arg.token_trees.get(1), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=')
{
// but not with `==`
if !matches!(arg.token_trees.get(2), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=' )
if !matches!(arg.token_trees.get(2), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=')
{
arg.token_trees.drain(..2);
let key = arg.token_trees.drain(..2).next().unwrap();
key_args.insert(key.to_string(), arg);
return None;
}
}
}
let _format_string = args.remove(0);
let arg_tts = args.into_iter().flat_map(|arg| {
quote! { #DOLLAR_CRATE::fmt::Argument::new(&(#arg), #DOLLAR_CRATE::fmt::Display::fmt), }
}.token_trees);
let expanded = quote! {
#DOLLAR_CRATE::fmt::Arguments::new_v1(&[], &[##arg_tts])
Some(arg)
}).collect::<Vec<_>>().into_iter();
// ^^^^^^^ we need this collect, to enforce the side effect of the filter_map closure (building the `key_args`)
let format_subtree = args.next().unwrap();
let format_string = (|| {
let token_tree = format_subtree.token_trees.get(0)?;
match token_tree {
tt::TokenTree::Leaf(l) => match l {
tt::Leaf::Literal(l) => {
if let Some(mut text) = l.text.strip_prefix('r') {
let mut raw_sharps = String::new();
while let Some(t) = text.strip_prefix('#') {
text = t;
raw_sharps.push('#');
}
text =
text.strip_suffix(&raw_sharps)?.strip_prefix('"')?.strip_suffix('"')?;
Some((text, l.span, Some(raw_sharps)))
} else {
let text = l.text.strip_prefix('"')?.strip_suffix('"')?;
let span = l.span;
Some((text, span, None))
}
}
_ => None,
},
tt::TokenTree::Subtree(_) => None,
}
})();
let Some((format_string, _format_string_span, raw_sharps)) = format_string else {
return expand_error;
};
ExpandResult::ok(expanded)
let mut format_iter = format_string.chars().peekable();
let mut parts = vec![];
let mut last_part = String::new();
let mut arg_tts = vec![];
let mut err = None;
while let Some(c) = format_iter.next() {
// Parsing the format string. See https://doc.rust-lang.org/std/fmt/index.html#syntax for the grammar and more info
match c {
'{' => {
if format_iter.peek() == Some(&'{') {
format_iter.next();
last_part.push('{');
continue;
}
let mut argument = String::new();
while ![Some(&'}'), Some(&':')].contains(&format_iter.peek()) {
argument.push(match format_iter.next() {
Some(c) => c,
None => return expand_error,
});
}
let format_spec = match format_iter.next().unwrap() {
'}' => "".to_owned(),
':' => {
let mut s = String::new();
while let Some(c) = format_iter.next() {
if c == '}' {
break;
}
s.push(c);
}
s
}
_ => unreachable!(),
};
parts.push(mem::take(&mut last_part));
let arg_tree = if argument.is_empty() {
match args.next() {
Some(x) => x,
None => {
err = Some(mbe::ExpandError::NoMatchingRule.into());
tt::Subtree::empty()
}
}
} else if let Some(tree) = key_args.get(&argument) {
tree.clone()
} else {
// FIXME: we should pick the related substring of the `_format_string_span` as the span. You
// can use `.char_indices()` instead of `.char()` for `format_iter` to find the substring interval.
let ident = Ident::new(argument, tt::TokenId::unspecified());
quote!(#ident)
};
let formatter = match &*format_spec {
"?" => quote!(::core::fmt::Debug::fmt),
"" => quote!(::core::fmt::Display::fmt),
_ => {
// FIXME: implement the rest and return expand error here
quote!(::core::fmt::Display::fmt)
}
};
arg_tts.push(quote! { ::core::fmt::Argument::new(&(#arg_tree), #formatter), });
}
'}' => {
if format_iter.peek() == Some(&'}') {
format_iter.next();
last_part.push('}');
} else {
return expand_error;
}
}
_ => last_part.push(c),
}
}
last_part += end_string;
if !last_part.is_empty() {
parts.push(last_part);
}
let part_tts = parts.into_iter().map(|x| {
let text = if let Some(raw) = &raw_sharps {
format!("r{raw}\"{}\"{raw}", x).into()
} else {
format!("\"{}\"", x).into()
};
let l = tt::Literal { span: tt::TokenId::unspecified(), text };
quote!(#l ,)
});
let arg_tts = arg_tts.into_iter().flat_map(|arg| arg.token_trees);
let expanded = quote! {
::core::fmt::Arguments::new_v1(&[##part_tts], &[##arg_tts])
};
ExpandResult { value: expanded, err }
}
fn asm_expand(
@ -566,16 +694,16 @@ fn include_expand(
let path = parse_string(tt)?;
let file_id = relative_file(db, arg_id, &path, false)?;
let subtree =
parse_to_token_tree(&db.file_text(file_id)).ok_or(mbe::ExpandError::ConversionError)?.0;
Ok((subtree, file_id))
let (subtree, map) =
parse_to_token_tree(&db.file_text(file_id)).ok_or(mbe::ExpandError::ConversionError)?;
Ok((subtree, map, file_id))
})();
match res {
Ok((subtree, file_id)) => {
ExpandResult::ok(ExpandedEager { subtree, included_file: Some(file_id) })
Ok((subtree, map, file_id)) => {
ExpandResult::ok(ExpandedEager { subtree, included_file: Some((file_id, map)) })
}
Err(e) => ExpandResult::with_err(
Err(e) => ExpandResult::new(
ExpandedEager { subtree: tt::Subtree::empty(), included_file: None },
e,
),
@ -588,7 +716,7 @@ fn include_bytes_expand(
tt: &tt::Subtree,
) -> ExpandResult<ExpandedEager> {
if let Err(e) = parse_string(tt) {
return ExpandResult::with_err(
return ExpandResult::new(
ExpandedEager { subtree: tt::Subtree::empty(), included_file: None },
e,
);
@ -613,7 +741,7 @@ fn include_str_expand(
let path = match parse_string(tt) {
Ok(it) => it,
Err(e) => {
return ExpandResult::with_err(
return ExpandResult::new(
ExpandedEager { subtree: tt::Subtree::empty(), included_file: None },
e,
)
@ -650,7 +778,7 @@ fn env_expand(
let key = match parse_string(tt) {
Ok(it) => it,
Err(e) => {
return ExpandResult::with_err(
return ExpandResult::new(
ExpandedEager { subtree: tt::Subtree::empty(), included_file: None },
e,
)
@ -686,16 +814,16 @@ fn option_env_expand(
let key = match parse_string(tt) {
Ok(it) => it,
Err(e) => {
return ExpandResult::with_err(
return ExpandResult::new(
ExpandedEager { subtree: tt::Subtree::empty(), included_file: None },
e,
)
}
};
// FIXME: Use `DOLLAR_CRATE` when that works in eager macros.
let expanded = match get_env_inner(db, arg_id, &key) {
None => quote! { #DOLLAR_CRATE::option::Option::None::<&str> },
Some(s) => quote! { #DOLLAR_CRATE::option::Option::Some(#s) },
None => quote! { ::core::option::Option::None::<&str> },
Some(s) => quote! { ::core::option::Option::Some(#s) },
};
ExpandResult::ok(ExpandedEager::new(expanded))

View file

@ -1,22 +1,22 @@
//! Defines database & queries for macro expansion.
use std::sync::Arc;
use base_db::{salsa, SourceDatabase};
use base_db::{salsa, Edition, SourceDatabase};
use either::Either;
use limit::Limit;
use mbe::syntax_node_to_token_tree;
use rustc_hash::FxHashSet;
use syntax::{
ast::{self, HasAttrs, HasDocComments},
AstNode, GreenNode, Parse, SyntaxNode, SyntaxToken, T,
AstNode, GreenNode, Parse, SyntaxError, SyntaxNode, SyntaxToken, T,
};
use triomphe::Arc;
use crate::{
ast_id_map::AstIdMap, builtin_attr_macro::pseudo_derive_attr_expansion, fixup,
hygiene::HygieneFrame, tt, BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander,
ExpandError, ExpandResult, ExpandTo, HirFileId, HirFileIdRepr, MacroCallId, MacroCallKind,
MacroCallLoc, MacroDefId, MacroDefKind, MacroFile, ProcMacroExpander,
ast_id_map::AstIdMap, builtin_attr_macro::pseudo_derive_attr_expansion,
builtin_fn_macro::EagerExpander, fixup, hygiene::HygieneFrame, tt, BuiltinAttrExpander,
BuiltinDeriveExpander, BuiltinFnLikeExpander, ExpandError, ExpandResult, ExpandTo, HirFileId,
HirFileIdRepr, MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind, MacroFile,
ProcMacroExpander,
};
/// Total limit on the number of tokens produced by any macro invocation.
@ -33,6 +33,8 @@ pub enum TokenExpander {
DeclarativeMacro { mac: mbe::DeclarativeMacro, def_site_token_map: mbe::TokenMap },
/// Stuff like `line!` and `file!`.
Builtin(BuiltinFnLikeExpander),
/// Built-in eagerly expanded fn-like macros (`include!`, `concat!`, etc.)
BuiltinEager(EagerExpander),
/// `global_allocator` and such.
BuiltinAttr(BuiltinAttrExpander),
/// `derive(Copy)` and such.
@ -51,6 +53,9 @@ impl TokenExpander {
match self {
TokenExpander::DeclarativeMacro { mac, .. } => mac.expand(tt).map_err(Into::into),
TokenExpander::Builtin(it) => it.expand(db, id, tt).map_err(Into::into),
TokenExpander::BuiltinEager(it) => {
it.expand(db, id, tt).map_err(Into::into).map(|res| res.subtree)
}
TokenExpander::BuiltinAttr(it) => it.expand(db, id, tt),
TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt),
TokenExpander::ProcMacro(_) => {
@ -66,6 +71,7 @@ impl TokenExpander {
match self {
TokenExpander::DeclarativeMacro { mac, .. } => mac.map_id_down(id),
TokenExpander::Builtin(..)
| TokenExpander::BuiltinEager(..)
| TokenExpander::BuiltinAttr(..)
| TokenExpander::BuiltinDerive(..)
| TokenExpander::ProcMacro(..) => id,
@ -76,6 +82,7 @@ impl TokenExpander {
match self {
TokenExpander::DeclarativeMacro { mac, .. } => mac.map_id_up(id),
TokenExpander::Builtin(..)
| TokenExpander::BuiltinEager(..)
| TokenExpander::BuiltinAttr(..)
| TokenExpander::BuiltinDerive(..)
| TokenExpander::ProcMacro(..) => (id, mbe::Origin::Call),
@ -90,12 +97,15 @@ pub trait ExpandDatabase: SourceDatabase {
/// Main public API -- parses a hir file, not caring whether it's a real
/// file or a macro expansion.
#[salsa::transparent]
fn parse_or_expand(&self, file_id: HirFileId) -> Option<SyntaxNode>;
fn parse_or_expand(&self, file_id: HirFileId) -> SyntaxNode;
#[salsa::transparent]
fn parse_or_expand_with_err(&self, file_id: HirFileId) -> ExpandResult<Parse<SyntaxNode>>;
/// Implementation for the macro case.
// This query is LRU cached
fn parse_macro_expansion(
&self,
macro_file: MacroFile,
) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>>;
) -> ExpandResult<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>;
/// Macro ids. That's probably the tricksiest bit in rust-analyzer, and the
/// reason why we use salsa at all.
@ -119,15 +129,19 @@ pub trait ExpandDatabase: SourceDatabase {
/// just fetches procedural ones.
fn macro_def(&self, id: MacroDefId) -> Result<Arc<TokenExpander>, mbe::ParseError>;
/// Expand macro call to a token tree. This query is LRUed (we keep 128 or so results in memory)
fn macro_expand(&self, macro_call: MacroCallId) -> ExpandResult<Option<Arc<tt::Subtree>>>;
/// Expand macro call to a token tree.
// This query is LRU cached
fn macro_expand(&self, macro_call: MacroCallId) -> ExpandResult<Arc<tt::Subtree>>;
/// Special case of the previous query for procedural macros. We can't LRU
/// proc macros, since they are not deterministic in general, and
/// non-determinism breaks salsa in a very, very, very bad way. @edwin0cheng
/// heroically debugged this once!
/// non-determinism breaks salsa in a very, very, very bad way.
/// @edwin0cheng heroically debugged this once!
fn expand_proc_macro(&self, call: MacroCallId) -> ExpandResult<tt::Subtree>;
/// Firewall query that returns the error from the `macro_expand` query.
fn macro_expand_error(&self, macro_call: MacroCallId) -> Option<ExpandError>;
/// Firewall query that returns the errors from the `parse_macro_expansion` query.
fn parse_macro_expansion_error(
&self,
macro_call: MacroCallId,
) -> ExpandResult<Box<[SyntaxError]>>;
fn hygiene_frame(&self, file_id: HirFileId) -> Arc<HygieneFrame>;
}
@ -159,8 +173,8 @@ pub fn expand_speculative(
);
let (attr_arg, token_id) = match loc.kind {
MacroCallKind::Attr { invoc_attr_index, is_derive, .. } => {
let attr = if is_derive {
MacroCallKind::Attr { invoc_attr_index, .. } => {
let attr = if loc.def.is_attribute_derive() {
// for pseudo-derive expansion we actually pass the attribute itself only
ast::Attr::cast(speculative_args.clone())
} else {
@ -236,17 +250,26 @@ pub fn expand_speculative(
}
fn ast_id_map(db: &dyn ExpandDatabase, file_id: HirFileId) -> Arc<AstIdMap> {
let map = db.parse_or_expand(file_id).map(|it| AstIdMap::from_source(&it)).unwrap_or_default();
Arc::new(map)
Arc::new(AstIdMap::from_source(&db.parse_or_expand(file_id)))
}
fn parse_or_expand(db: &dyn ExpandDatabase, file_id: HirFileId) -> Option<SyntaxNode> {
fn parse_or_expand(db: &dyn ExpandDatabase, file_id: HirFileId) -> SyntaxNode {
match file_id.repr() {
HirFileIdRepr::FileId(file_id) => Some(db.parse(file_id).tree().syntax().clone()),
HirFileIdRepr::FileId(file_id) => db.parse(file_id).tree().syntax().clone(),
HirFileIdRepr::MacroFile(macro_file) => {
// FIXME: Note how we convert from `Parse` to `SyntaxNode` here,
// forgetting about parse errors.
db.parse_macro_expansion(macro_file).value.map(|(it, _)| it.syntax_node())
db.parse_macro_expansion(macro_file).value.0.syntax_node()
}
}
}
fn parse_or_expand_with_err(
db: &dyn ExpandDatabase,
file_id: HirFileId,
) -> ExpandResult<Parse<SyntaxNode>> {
match file_id.repr() {
HirFileIdRepr::FileId(file_id) => ExpandResult::ok(db.parse(file_id).to_syntax()),
HirFileIdRepr::MacroFile(macro_file) => {
db.parse_macro_expansion(macro_file).map(|(it, _)| it)
}
}
}
@ -254,35 +277,34 @@ fn parse_or_expand(db: &dyn ExpandDatabase, file_id: HirFileId) -> Option<Syntax
fn parse_macro_expansion(
db: &dyn ExpandDatabase,
macro_file: MacroFile,
) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>> {
) -> ExpandResult<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> {
let _p = profile::span("parse_macro_expansion");
let mbe::ValueResult { value, err } = db.macro_expand(macro_file.macro_call_id);
let mbe::ValueResult { value: tt, err } = db.macro_expand(macro_file.macro_call_id);
if let Some(err) = &err {
// Note:
// The final goal we would like to make all parse_macro success,
// such that the following log will not call anyway.
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
let node = loc.kind.to_node(db);
if tracing::enabled!(tracing::Level::DEBUG) {
// Note:
// The final goal we would like to make all parse_macro success,
// such that the following log will not call anyway.
let loc = db.lookup_intern_macro_call(macro_file.macro_call_id);
let node = loc.to_node(db);
// collect parent information for warning log
let parents =
std::iter::successors(loc.kind.file_id().call_node(db), |it| it.file_id.call_node(db))
.map(|n| format!("{:#}", n.value))
.collect::<Vec<_>>()
.join("\n");
// collect parent information for warning log
let parents = std::iter::successors(loc.kind.file_id().call_node(db), |it| {
it.file_id.call_node(db)
})
.map(|n| format!("{:#}", n.value))
.collect::<Vec<_>>()
.join("\n");
tracing::debug!(
"fail on macro_parse: (reason: {:?} macro_call: {:#}) parents: {}",
err,
node.value,
parents
);
tracing::debug!(
"fail on macro_parse: (reason: {:?} macro_call: {:#}) parents: {}",
err,
node.value,
parents
);
}
}
let tt = match value {
Some(tt) => tt,
None => return ExpandResult { value: None, err },
};
let expand_to = macro_expand_to(db, macro_file.macro_call_id);
@ -291,7 +313,7 @@ fn parse_macro_expansion(
let (parse, rev_token_map) = token_tree_to_syntax_node(&tt, expand_to);
ExpandResult { value: Some((parse, Arc::new(rev_token_map))), err }
ExpandResult { value: (parse, Arc::new(rev_token_map)), err }
}
fn macro_arg(
@ -339,7 +361,7 @@ fn censor_for_macro_input(loc: &MacroCallLoc, node: &SyntaxNode) -> FxHashSet<Sy
.map(|it| it.syntax().clone())
.collect()
}
MacroCallKind::Attr { is_derive: true, .. } => return None,
MacroCallKind::Attr { .. } if loc.def.is_attribute_derive() => return None,
MacroCallKind::Attr { invoc_attr_index, .. } => {
cov_mark::hit!(attribute_macro_attr_censoring);
ast::Item::cast(node.clone())?
@ -385,13 +407,14 @@ fn macro_def(
) -> Result<Arc<TokenExpander>, mbe::ParseError> {
match id.kind {
MacroDefKind::Declarative(ast_id) => {
let is_2021 = db.crate_graph()[id.krate].edition >= Edition::Edition2021;
let (mac, def_site_token_map) = match ast_id.to_node(db) {
ast::Macro::MacroRules(macro_rules) => {
let arg = macro_rules
.token_tree()
.ok_or_else(|| mbe::ParseError::Expected("expected a token tree".into()))?;
let (tt, def_site_token_map) = mbe::syntax_node_to_token_tree(arg.syntax());
let mac = mbe::DeclarativeMacro::parse_macro_rules(&tt)?;
let mac = mbe::DeclarativeMacro::parse_macro_rules(&tt, is_2021)?;
(mac, def_site_token_map)
}
ast::Macro::MacroDef(macro_def) => {
@ -399,7 +422,7 @@ fn macro_def(
.body()
.ok_or_else(|| mbe::ParseError::Expected("expected a token tree".into()))?;
let (tt, def_site_token_map) = mbe::syntax_node_to_token_tree(arg.syntax());
let mac = mbe::DeclarativeMacro::parse_macro2(&tt)?;
let mac = mbe::DeclarativeMacro::parse_macro2(&tt, is_2021)?;
(mac, def_site_token_map)
}
};
@ -412,82 +435,96 @@ fn macro_def(
MacroDefKind::BuiltInDerive(expander, _) => {
Ok(Arc::new(TokenExpander::BuiltinDerive(expander)))
}
MacroDefKind::BuiltInEager(..) => {
// FIXME: Return a random error here just to make the types align.
// This obviously should do something real instead.
Err(mbe::ParseError::UnexpectedToken("unexpected eager macro".into()))
MacroDefKind::BuiltInEager(expander, ..) => {
Ok(Arc::new(TokenExpander::BuiltinEager(expander)))
}
MacroDefKind::ProcMacro(expander, ..) => Ok(Arc::new(TokenExpander::ProcMacro(expander))),
}
}
fn macro_expand(
db: &dyn ExpandDatabase,
id: MacroCallId,
) -> ExpandResult<Option<Arc<tt::Subtree>>> {
fn macro_expand(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<Arc<tt::Subtree>> {
let _p = profile::span("macro_expand");
let loc: MacroCallLoc = db.lookup_intern_macro_call(id);
let loc = db.lookup_intern_macro_call(id);
if let Some(eager) = &loc.eager {
return ExpandResult {
value: Some(eager.arg_or_expansion.clone()),
// FIXME: There could be errors here!
err: None,
};
return ExpandResult { value: eager.arg_or_expansion.clone(), err: eager.error.clone() };
}
let macro_arg = match db.macro_arg(id) {
Some(it) => it,
None => {
return ExpandResult::only_err(ExpandError::Other(
"Failed to lower macro args to token tree".into(),
))
}
};
let expander = match db.macro_def(loc.def) {
Ok(it) => it,
// FIXME: This is weird -- we effectively report macro *definition*
// errors lazily, when we try to expand the macro. Instead, they should
// be reported at the definition site (when we construct a def map).
// be reported at the definition site when we construct a def map.
// (Note we do report them also at the definition site in the late diagnostic pass)
Err(err) => {
return ExpandResult::only_err(ExpandError::Other(
format!("invalid macro definition: {err}").into(),
))
return ExpandResult {
value: Arc::new(tt::Subtree {
delimiter: tt::Delimiter::UNSPECIFIED,
token_trees: vec![],
}),
err: Some(ExpandError::Other(format!("invalid macro definition: {err}").into())),
}
}
};
let Some(macro_arg) = db.macro_arg(id) else {
return ExpandResult {
value: Arc::new(
tt::Subtree {
delimiter: tt::Delimiter::UNSPECIFIED,
token_trees: Vec::new(),
},
),
err: Some(ExpandError::Other(
"invalid token tree"
.into(),
)),
};
};
let ExpandResult { value: mut tt, err } = expander.expand(db, id, &macro_arg.0);
// Set a hard limit for the expanded tt
let count = tt.count();
if TOKEN_LIMIT.check(count).is_err() {
return ExpandResult::only_err(ExpandError::Other(
format!(
"macro invocation exceeds token limit: produced {} tokens, limit is {}",
count,
TOKEN_LIMIT.inner(),
)
.into(),
));
return ExpandResult {
value: Arc::new(tt::Subtree {
delimiter: tt::Delimiter::UNSPECIFIED,
token_trees: vec![],
}),
err: Some(ExpandError::Other(
format!(
"macro invocation exceeds token limit: produced {} tokens, limit is {}",
count,
TOKEN_LIMIT.inner(),
)
.into(),
)),
};
}
fixup::reverse_fixups(&mut tt, &macro_arg.1, &macro_arg.2);
ExpandResult { value: Some(Arc::new(tt)), err }
ExpandResult { value: Arc::new(tt), err }
}
fn macro_expand_error(db: &dyn ExpandDatabase, macro_call: MacroCallId) -> Option<ExpandError> {
db.macro_expand(macro_call).err
fn parse_macro_expansion_error(
db: &dyn ExpandDatabase,
macro_call_id: MacroCallId,
) -> ExpandResult<Box<[SyntaxError]>> {
db.parse_macro_expansion(MacroFile { macro_call_id })
.map(|it| it.0.errors().to_vec().into_boxed_slice())
}
fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<tt::Subtree> {
let loc: MacroCallLoc = db.lookup_intern_macro_call(id);
let macro_arg = match db.macro_arg(id) {
Some(it) => it,
None => {
return ExpandResult::with_err(
tt::Subtree::empty(),
ExpandError::Other("No arguments for proc-macro".into()),
)
}
let loc = db.lookup_intern_macro_call(id);
let Some(macro_arg) = db.macro_arg(id) else {
return ExpandResult {
value: tt::Subtree {
delimiter: tt::Delimiter::UNSPECIFIED,
token_trees: Vec::new(),
},
err: Some(ExpandError::Other(
"invalid token tree"
.into(),
)),
};
};
let expander = match loc.def.kind {
@ -512,8 +549,7 @@ fn hygiene_frame(db: &dyn ExpandDatabase, file_id: HirFileId) -> Arc<HygieneFram
}
fn macro_expand_to(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandTo {
let loc: MacroCallLoc = db.lookup_intern_macro_call(id);
loc.kind.expand_to()
db.lookup_intern_macro_call(id).expand_to()
}
fn token_tree_to_syntax_node(

View file

@ -18,10 +18,9 @@
//!
//!
//! See the full discussion : <https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/Eager.20expansion.20of.20built-in.20macros>
use std::sync::Arc;
use base_db::CrateId;
use syntax::{ted, SyntaxNode};
use syntax::{ted, Parse, SyntaxNode};
use triomphe::Arc;
use crate::{
ast::{self, AstNode},
@ -32,77 +31,16 @@ use crate::{
MacroCallLoc, MacroDefId, MacroDefKind, UnresolvedMacro,
};
#[derive(Debug)]
pub struct ErrorEmitted {
_private: (),
}
pub trait ErrorSink {
fn emit(&mut self, err: ExpandError);
fn option<T>(
&mut self,
opt: Option<T>,
error: impl FnOnce() -> ExpandError,
) -> Result<T, ErrorEmitted> {
match opt {
Some(it) => Ok(it),
None => {
self.emit(error());
Err(ErrorEmitted { _private: () })
}
}
}
fn option_with<T>(
&mut self,
opt: impl FnOnce() -> Option<T>,
error: impl FnOnce() -> ExpandError,
) -> Result<T, ErrorEmitted> {
self.option(opt(), error)
}
fn result<T>(&mut self, res: Result<T, ExpandError>) -> Result<T, ErrorEmitted> {
match res {
Ok(it) => Ok(it),
Err(e) => {
self.emit(e);
Err(ErrorEmitted { _private: () })
}
}
}
fn expand_result_option<T>(&mut self, res: ExpandResult<Option<T>>) -> Result<T, ErrorEmitted> {
match (res.value, res.err) {
(None, Some(err)) => {
self.emit(err);
Err(ErrorEmitted { _private: () })
}
(Some(value), opt_err) => {
if let Some(err) = opt_err {
self.emit(err);
}
Ok(value)
}
(None, None) => unreachable!("`ExpandResult` without value or error"),
}
}
}
impl ErrorSink for &'_ mut dyn FnMut(ExpandError) {
fn emit(&mut self, err: ExpandError) {
self(err);
}
}
pub fn expand_eager_macro(
db: &dyn ExpandDatabase,
krate: CrateId,
macro_call: InFile<ast::MacroCall>,
def: MacroDefId,
resolver: &dyn Fn(ModPath) -> Option<MacroDefId>,
diagnostic_sink: &mut dyn FnMut(ExpandError),
) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro> {
) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro> {
let MacroDefKind::BuiltInEager(eager, _) = def.kind else {
panic!("called `expand_eager_macro` on non-eager macro def {def:?}")
};
let hygiene = Hygiene::new(db, macro_call.file_id);
let parsed_args = macro_call
.value
@ -115,60 +53,54 @@ pub fn expand_eager_macro(
let expand_to = ExpandTo::from_call_site(&macro_call.value);
// Note:
// When `lazy_expand` is called, its *parent* file must be already exists.
// Here we store an eager macro id for the argument expanded subtree here
// When `lazy_expand` is called, its *parent* file must already exist.
// Here we store an eager macro id for the argument expanded subtree
// for that purpose.
let arg_id = db.intern_macro_call(MacroCallLoc {
def,
krate,
eager: Some(EagerCallInfo {
eager: Some(Box::new(EagerCallInfo {
arg_or_expansion: Arc::new(parsed_args.clone()),
included_file: None,
}),
error: None,
})),
kind: MacroCallKind::FnLike { ast_id: call_id, expand_to: ExpandTo::Expr },
});
let parsed_args = mbe::token_tree_to_syntax_node(&parsed_args, mbe::TopEntryPoint::Expr).0;
let result = match eager_macro_recur(
let ExpandResult { value, mut err } = eager_macro_recur(
db,
&hygiene,
InFile::new(arg_id.as_file(), parsed_args.syntax_node()),
krate,
resolver,
diagnostic_sink,
) {
Ok(Ok(it)) => it,
Ok(Err(err)) => return Ok(Err(err)),
Err(err) => return Err(err),
)?;
let Some(value ) = value else {
return Ok(ExpandResult { value: None, err })
};
let subtree = {
let mut subtree = mbe::syntax_node_to_token_tree(&value).0;
subtree.delimiter = crate::tt::Delimiter::unspecified();
subtree
};
let subtree = to_subtree(&result);
if let MacroDefKind::BuiltInEager(eager, _) = def.kind {
let res = eager.expand(db, arg_id, &subtree);
if let Some(err) = res.err {
diagnostic_sink(err);
}
let loc = MacroCallLoc {
def,
krate,
eager: Some(EagerCallInfo {
arg_or_expansion: Arc::new(res.value.subtree),
included_file: res.value.included_file,
}),
kind: MacroCallKind::FnLike { ast_id: call_id, expand_to },
};
Ok(Ok(db.intern_macro_call(loc)))
} else {
panic!("called `expand_eager_macro` on non-eager macro def {def:?}");
let res = eager.expand(db, arg_id, &subtree);
if err.is_none() {
err = res.err;
}
}
fn to_subtree(node: &SyntaxNode) -> crate::tt::Subtree {
let mut subtree = mbe::syntax_node_to_token_tree(node).0;
subtree.delimiter = crate::tt::Delimiter::unspecified();
subtree
let loc = MacroCallLoc {
def,
krate,
eager: Some(Box::new(EagerCallInfo {
arg_or_expansion: Arc::new(res.value.subtree),
included_file: res.value.included_file,
error: err.clone(),
})),
kind: MacroCallKind::FnLike { ast_id: call_id, expand_to },
};
Ok(ExpandResult { value: Some(db.intern_macro_call(loc)), err })
}
fn lazy_expand(
@ -176,7 +108,7 @@ fn lazy_expand(
def: &MacroDefId,
macro_call: InFile<ast::MacroCall>,
krate: CrateId,
) -> ExpandResult<Option<InFile<SyntaxNode>>> {
) -> ExpandResult<InFile<Parse<SyntaxNode>>> {
let ast_id = db.ast_id_map(macro_call.file_id).ast_id(&macro_call.value);
let expand_to = ExpandTo::from_call_site(&macro_call.value);
@ -186,10 +118,8 @@ fn lazy_expand(
MacroCallKind::FnLike { ast_id: macro_call.with_value(ast_id), expand_to },
);
let err = db.macro_expand_error(id);
let value = db.parse_or_expand(id.as_file()).map(|node| InFile::new(id.as_file(), node));
ExpandResult { value, err }
let file_id = id.as_file();
db.parse_or_expand_with_err(file_id).map(|parse| InFile::new(file_id, parse))
}
fn eager_macro_recur(
@ -198,23 +128,25 @@ fn eager_macro_recur(
curr: InFile<SyntaxNode>,
krate: CrateId,
macro_resolver: &dyn Fn(ModPath) -> Option<MacroDefId>,
mut diagnostic_sink: &mut dyn FnMut(ExpandError),
) -> Result<Result<SyntaxNode, ErrorEmitted>, UnresolvedMacro> {
) -> Result<ExpandResult<Option<SyntaxNode>>, UnresolvedMacro> {
let original = curr.value.clone_for_update();
let children = original.descendants().filter_map(ast::MacroCall::cast);
let mut replacements = Vec::new();
// Note: We only report a single error inside of eager expansions
let mut error = None;
// Collect replacement
for child in children {
let def = match child.path().and_then(|path| ModPath::from_src(db, path, hygiene)) {
Some(path) => macro_resolver(path.clone()).ok_or(UnresolvedMacro { path })?,
None => {
diagnostic_sink(ExpandError::Other("malformed macro invocation".into()));
error = Some(ExpandError::Other("malformed macro invocation".into()));
continue;
}
};
let insert = match def.kind {
let ExpandResult { value, err } = match def.kind {
MacroDefKind::BuiltInEager(..) => {
let id = match expand_eager_macro(
db,
@ -222,45 +154,49 @@ fn eager_macro_recur(
curr.with_value(child.clone()),
def,
macro_resolver,
diagnostic_sink,
) {
Ok(Ok(it)) => it,
Ok(Err(err)) => return Ok(Err(err)),
Ok(it) => it,
Err(err) => return Err(err),
};
db.parse_or_expand(id.as_file())
.expect("successful macro expansion should be parseable")
.clone_for_update()
id.map(|call| {
call.map(|call| db.parse_or_expand(call.as_file()).clone_for_update())
})
}
MacroDefKind::Declarative(_)
| MacroDefKind::BuiltIn(..)
| MacroDefKind::BuiltInAttr(..)
| MacroDefKind::BuiltInDerive(..)
| MacroDefKind::ProcMacro(..) => {
let res = lazy_expand(db, &def, curr.with_value(child.clone()), krate);
let val = match diagnostic_sink.expand_result_option(res) {
Ok(it) => it,
Err(err) => return Ok(Err(err)),
};
let ExpandResult { value, err } =
lazy_expand(db, &def, curr.with_value(child.clone()), krate);
// replace macro inside
let hygiene = Hygiene::new(db, val.file_id);
match eager_macro_recur(db, &hygiene, val, krate, macro_resolver, diagnostic_sink) {
Ok(Ok(it)) => it,
Ok(Err(err)) => return Ok(Err(err)),
Err(err) => return Err(err),
}
let hygiene = Hygiene::new(db, value.file_id);
let ExpandResult { value, err: error } = eager_macro_recur(
db,
&hygiene,
// FIXME: We discard parse errors here
value.map(|it| it.syntax_node()),
krate,
macro_resolver,
)?;
let err = if err.is_none() { error } else { err };
ExpandResult { value, err }
}
};
if err.is_some() {
error = err;
}
// check if the whole original syntax is replaced
if child.syntax() == &original {
return Ok(Ok(insert));
return Ok(ExpandResult { value, err: error });
}
replacements.push((child, insert));
if let Some(insert) = value {
replacements.push((child, insert));
}
}
replacements.into_iter().rev().for_each(|(old, new)| ted::replace(old.syntax(), new));
Ok(Ok(original))
Ok(ExpandResult { value: Some(original), err: error })
}

View file

@ -14,7 +14,7 @@ use tt::token_id::Subtree;
/// The result of calculating fixes for a syntax node -- a bunch of changes
/// (appending to and replacing nodes), the information that is needed to
/// reverse those changes afterwards, and a token map.
#[derive(Debug)]
#[derive(Debug, Default)]
pub(crate) struct SyntaxFixups {
pub(crate) append: FxHashMap<SyntaxElement, Vec<SyntheticToken>>,
pub(crate) replace: FxHashMap<SyntaxElement, Vec<SyntheticToken>>,
@ -24,7 +24,7 @@ pub(crate) struct SyntaxFixups {
}
/// This is the information needed to reverse the fixups.
#[derive(Debug, PartialEq, Eq)]
#[derive(Debug, Default, PartialEq, Eq)]
pub struct SyntaxFixupUndoInfo {
original: Vec<Subtree>,
}

View file

@ -2,8 +2,6 @@
//!
//! Specifically, `ast` + `Hygiene` allows you to create a `Name`. Note that, at
//! this moment, this is horribly incomplete and handles only `$crate`.
use std::sync::Arc;
use base_db::CrateId;
use db::TokenExpander;
use either::Either;
@ -12,6 +10,7 @@ use syntax::{
ast::{self, HasDocComments},
AstNode, SyntaxKind, SyntaxNode, TextRange, TextSize,
};
use triomphe::Arc;
use crate::{
db::{self, ExpandDatabase},
@ -200,8 +199,14 @@ fn make_hygiene_info(
});
let macro_def = db.macro_def(loc.def).ok()?;
let (_, exp_map) = db.parse_macro_expansion(macro_file).value?;
let macro_arg = db.macro_arg(macro_file.macro_call_id)?;
let (_, exp_map) = db.parse_macro_expansion(macro_file).value;
let macro_arg = db.macro_arg(macro_file.macro_call_id).unwrap_or_else(|| {
Arc::new((
tt::Subtree { delimiter: tt::Delimiter::UNSPECIFIED, token_trees: Vec::new() },
Default::default(),
Default::default(),
))
});
Some(HygieneInfo {
file: macro_file,

View file

@ -20,11 +20,13 @@ pub mod mod_path;
pub mod attrs;
mod fixup;
use mbe::TokenMap;
pub use mbe::{Origin, ValueResult};
use ::tt::token_id as tt;
use triomphe::Arc;
use std::{fmt, hash::Hash, iter, sync::Arc};
use std::{fmt, hash::Hash, iter};
use base_db::{
impl_intern_key,
@ -51,11 +53,11 @@ use crate::{
pub type ExpandResult<T> = ValueResult<T, ExpandError>;
#[derive(Debug, PartialEq, Eq, Clone)]
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub enum ExpandError {
UnresolvedProcMacro(CrateId),
Mbe(mbe::ExpandError),
RecursionOverflowPosioned,
RecursionOverflowPoisoned,
Other(Box<str>),
}
@ -70,7 +72,7 @@ impl fmt::Display for ExpandError {
match self {
ExpandError::UnresolvedProcMacro(_) => f.write_str("unresolved proc-macro"),
ExpandError::Mbe(it) => it.fmt(f),
ExpandError::RecursionOverflowPosioned => {
ExpandError::RecursionOverflowPoisoned => {
f.write_str("overflow expanding the original macro")
}
ExpandError::Other(it) => f.write_str(it),
@ -113,7 +115,7 @@ impl_intern_key!(MacroCallId);
pub struct MacroCallLoc {
pub def: MacroDefId,
pub(crate) krate: CrateId,
eager: Option<EagerCallInfo>,
eager: Option<Box<EagerCallInfo>>,
pub kind: MacroCallKind,
}
@ -139,7 +141,8 @@ pub enum MacroDefKind {
struct EagerCallInfo {
/// NOTE: This can be *either* the expansion result, *or* the argument to the eager macro!
arg_or_expansion: Arc<tt::Subtree>,
included_file: Option<FileId>,
included_file: Option<(FileId, TokenMap)>,
error: Option<ExpandError>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@ -166,8 +169,6 @@ pub enum MacroCallKind {
/// Outer attributes are counted first, then inner attributes. This does not support
/// out-of-line modules, which may have attributes spread across 2 files!
invoc_attr_index: AttrId,
/// Whether this attribute is the `#[derive]` attribute.
is_derive: bool,
},
}
@ -205,8 +206,8 @@ impl HirFileId {
HirFileIdRepr::FileId(id) => break id,
HirFileIdRepr::MacroFile(MacroFile { macro_call_id }) => {
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_call_id);
file_id = match loc.eager {
Some(EagerCallInfo { included_file: Some(file), .. }) => file.into(),
file_id = match loc.eager.as_deref() {
Some(&EagerCallInfo { included_file: Some((file, _)), .. }) => file.into(),
_ => loc.kind.file_id(),
};
}
@ -230,18 +231,17 @@ impl HirFileId {
pub fn call_node(self, db: &dyn db::ExpandDatabase) -> Option<InFile<SyntaxNode>> {
let macro_file = self.macro_file()?;
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
Some(loc.kind.to_node(db))
Some(loc.to_node(db))
}
/// If this is a macro call, returns the syntax node of the very first macro call this file resides in.
pub fn original_call_node(self, db: &dyn db::ExpandDatabase) -> Option<(FileId, SyntaxNode)> {
let mut call =
db.lookup_intern_macro_call(self.macro_file()?.macro_call_id).kind.to_node(db);
let mut call = db.lookup_intern_macro_call(self.macro_file()?.macro_call_id).to_node(db);
loop {
match call.file_id.repr() {
HirFileIdRepr::FileId(file_id) => break Some((file_id, call.value)),
HirFileIdRepr::MacroFile(MacroFile { macro_call_id }) => {
call = db.lookup_intern_macro_call(macro_call_id).kind.to_node(db);
call = db.lookup_intern_macro_call(macro_call_id).to_node(db);
}
}
}
@ -255,8 +255,14 @@ impl HirFileId {
let arg_tt = loc.kind.arg(db)?;
let macro_def = db.macro_def(loc.def).ok()?;
let (parse, exp_map) = db.parse_macro_expansion(macro_file).value?;
let macro_arg = db.macro_arg(macro_file.macro_call_id)?;
let (parse, exp_map) = db.parse_macro_expansion(macro_file).value;
let macro_arg = db.macro_arg(macro_file.macro_call_id).unwrap_or_else(|| {
Arc::new((
tt::Subtree { delimiter: tt::Delimiter::UNSPECIFIED, token_trees: Vec::new() },
Default::default(),
Default::default(),
))
});
let def = loc.def.ast_id().left().and_then(|id| {
let def_tt = match id.to_node(db) {
@ -298,7 +304,7 @@ impl HirFileId {
let macro_file = self.macro_file()?;
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
let attr = match loc.def.kind {
MacroDefKind::BuiltInDerive(..) => loc.kind.to_node(db),
MacroDefKind::BuiltInDerive(..) => loc.to_node(db),
_ => return None,
};
Some(attr.with_value(ast::Attr::cast(attr.value.clone())?))
@ -319,7 +325,7 @@ impl HirFileId {
match self.macro_file() {
Some(macro_file) => {
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
matches!(loc.eager, Some(EagerCallInfo { included_file: Some(_), .. }))
matches!(loc.eager.as_deref(), Some(EagerCallInfo { included_file: Some(..), .. }))
}
_ => false,
}
@ -342,7 +348,7 @@ impl HirFileId {
match self.macro_file() {
Some(macro_file) => {
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
matches!(loc.kind, MacroCallKind::Attr { is_derive: true, .. })
loc.def.is_attribute_derive()
}
None => false,
}
@ -413,22 +419,15 @@ impl MacroDefId {
MacroDefKind::BuiltInAttr(..) | MacroDefKind::ProcMacro(_, ProcMacroKind::Attr, _)
)
}
pub fn is_attribute_derive(&self) -> bool {
matches!(self.kind, MacroDefKind::BuiltInAttr(expander, ..) if expander.is_derive())
}
}
// FIXME: attribute indices do not account for nested `cfg_attr`
impl MacroCallKind {
/// Returns the file containing the macro invocation.
fn file_id(&self) -> HirFileId {
match *self {
MacroCallKind::FnLike { ast_id: InFile { file_id, .. }, .. }
| MacroCallKind::Derive { ast_id: InFile { file_id, .. }, .. }
| MacroCallKind::Attr { ast_id: InFile { file_id, .. }, .. } => file_id,
}
}
impl MacroCallLoc {
pub fn to_node(&self, db: &dyn db::ExpandDatabase) -> InFile<SyntaxNode> {
match self {
match self.kind {
MacroCallKind::FnLike { ast_id, .. } => {
ast_id.with_value(ast_id.to_node(db).syntax().clone())
}
@ -444,23 +443,49 @@ impl MacroCallKind {
.unwrap_or_else(|| it.syntax().clone())
})
}
MacroCallKind::Attr { ast_id, is_derive: true, invoc_attr_index, .. } => {
// FIXME: handle `cfg_attr`
ast_id.with_value(ast_id.to_node(db)).map(|it| {
it.doc_comments_and_attrs()
.nth(invoc_attr_index.ast_index())
.and_then(|it| match it {
Either::Left(attr) => Some(attr.syntax().clone()),
Either::Right(_) => None,
})
.unwrap_or_else(|| it.syntax().clone())
})
MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => {
if self.def.is_attribute_derive() {
// FIXME: handle `cfg_attr`
ast_id.with_value(ast_id.to_node(db)).map(|it| {
it.doc_comments_and_attrs()
.nth(invoc_attr_index.ast_index())
.and_then(|it| match it {
Either::Left(attr) => Some(attr.syntax().clone()),
Either::Right(_) => None,
})
.unwrap_or_else(|| it.syntax().clone())
})
} else {
ast_id.with_value(ast_id.to_node(db).syntax().clone())
}
}
MacroCallKind::Attr { ast_id, .. } => {
ast_id.with_value(ast_id.to_node(db).syntax().clone())
}
}
fn expand_to(&self) -> ExpandTo {
match self.kind {
MacroCallKind::FnLike { expand_to, .. } => expand_to,
MacroCallKind::Derive { .. } => ExpandTo::Items,
MacroCallKind::Attr { .. } if self.def.is_attribute_derive() => ExpandTo::Statements,
MacroCallKind::Attr { .. } => {
// is this always correct?
ExpandTo::Items
}
}
}
}
// FIXME: attribute indices do not account for nested `cfg_attr`
impl MacroCallKind {
/// Returns the file containing the macro invocation.
fn file_id(&self) -> HirFileId {
match *self {
MacroCallKind::FnLike { ast_id: InFile { file_id, .. }, .. }
| MacroCallKind::Derive { ast_id: InFile { file_id, .. }, .. }
| MacroCallKind::Attr { ast_id: InFile { file_id, .. }, .. } => file_id,
}
}
/// Returns the original file range that best describes the location of this macro call.
///
@ -538,15 +563,6 @@ impl MacroCallKind {
MacroCallKind::Attr { ast_id, .. } => Some(ast_id.to_node(db).syntax().clone()),
}
}
fn expand_to(&self) -> ExpandTo {
match self {
MacroCallKind::FnLike { expand_to, .. } => *expand_to,
MacroCallKind::Derive { .. } => ExpandTo::Items,
MacroCallKind::Attr { is_derive: true, .. } => ExpandTo::Statements,
MacroCallKind::Attr { .. } => ExpandTo::Items, // is this always correct?
}
}
}
impl MacroCallId {
@ -610,7 +626,7 @@ impl ExpansionInfo {
let token_range = token.value.text_range();
match &loc.kind {
MacroCallKind::Attr { attr_args, invoc_attr_index, is_derive, .. } => {
MacroCallKind::Attr { attr_args, invoc_attr_index, .. } => {
// FIXME: handle `cfg_attr`
let attr = item
.doc_comments_and_attrs()
@ -626,7 +642,8 @@ impl ExpansionInfo {
token.value.text_range().checked_sub(attr_input_start)?;
// shift by the item's tree's max id
let token_id = attr_args.1.token_by_range(relative_range)?;
let token_id = if *is_derive {
let token_id = if loc.def.is_attribute_derive() {
// we do not shift for `#[derive]`, as we only need to downmap the derive attribute tokens
token_id
} else {
@ -677,20 +694,31 @@ impl ExpansionInfo {
let call_id = self.expanded.file_id.macro_file()?.macro_call_id;
let loc = db.lookup_intern_macro_call(call_id);
if let Some((file, map)) = loc.eager.and_then(|e| e.included_file) {
// Special case: map tokens from `include!` expansions to the included file
let range = map.first_range_by_token(token_id, token.value.kind())?;
let source = db.parse(file);
let token = source.syntax_node().covering_element(range).into_token()?;
return Some((InFile::new(file.into(), token), Origin::Call));
}
// Attributes are a bit special for us, they have two inputs, the input tokentree and the annotated item.
let (token_map, tt) = match &loc.kind {
MacroCallKind::Attr { attr_args, is_derive: true, .. } => {
(&attr_args.1, self.attr_input_or_mac_def.clone()?.syntax().cloned())
}
MacroCallKind::Attr { attr_args, .. } => {
// try unshifting the the token id, if unshifting fails, the token resides in the non-item attribute input
// note that the `TokenExpander::map_id_up` earlier only unshifts for declarative macros, so we don't double unshift with this
match self.macro_arg_shift.unshift(token_id) {
Some(unshifted) => {
token_id = unshifted;
(&attr_args.1, self.attr_input_or_mac_def.clone()?.syntax().cloned())
if loc.def.is_attribute_derive() {
(&attr_args.1, self.attr_input_or_mac_def.clone()?.syntax().cloned())
} else {
// try unshifting the token id, if unshifting fails, the token resides in the non-item attribute input
// note that the `TokenExpander::map_id_up` earlier only unshifts for declarative macros, so we don't double unshift with this
match self.macro_arg_shift.unshift(token_id) {
Some(unshifted) => {
token_id = unshifted;
(&attr_args.1, self.attr_input_or_mac_def.clone()?.syntax().cloned())
}
None => (&self.macro_arg.1, self.arg.clone()),
}
None => (&self.macro_arg.1, self.arg.clone()),
}
}
_ => match origin {
@ -718,7 +746,7 @@ pub type AstId<N> = InFile<FileAstId<N>>;
impl<N: AstNode> AstId<N> {
pub fn to_node(&self, db: &dyn db::ExpandDatabase) -> N {
let root = db.parse_or_expand(self.file_id).unwrap();
let root = db.parse_or_expand(self.file_id);
db.ast_id_map(self.file_id).get(self.value).to_node(&root)
}
}
@ -754,7 +782,7 @@ impl<T> InFile<T> {
}
pub fn file_syntax(&self, db: &dyn db::ExpandDatabase) -> SyntaxNode {
db.parse_or_expand(self.file_id).expect("source created from invalid file")
db.parse_or_expand(self.file_id)
}
}
@ -950,6 +978,7 @@ fn ascend_node_border_tokens(
let first_token = |node: &SyntaxNode| skip_trivia_token(node.first_token()?, Direction::Next);
let last_token = |node: &SyntaxNode| skip_trivia_token(node.last_token()?, Direction::Prev);
// FIXME: Once the token map rewrite is done, this shouldnt need to rely on syntax nodes and tokens anymore
let first = first_token(node)?;
let last = last_token(node)?;
let first = ascend_call_token(db, &expansion, InFile::new(file_id, first))?;
@ -977,6 +1006,7 @@ impl<N: AstNode> InFile<N> {
self.value.syntax().descendants().filter_map(T::cast).map(move |n| self.with_value(n))
}
// FIXME: this should return `Option<InFileNotHirFile<N>>`
pub fn original_ast_node(self, db: &dyn db::ExpandDatabase) -> Option<InFile<N>> {
// This kind of upmapping can only be achieved in attribute expanded files,
// as we don't have node inputs otherwise and therefore can't find an `N` node in the input

View file

@ -1,7 +1,7 @@
//! A lowering for `use`-paths (more generally, paths without angle-bracketed segments).
use std::{
fmt::{self, Display},
fmt::{self, Display as _},
iter,
};
@ -24,6 +24,12 @@ pub struct ModPath {
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct UnescapedModPath<'a>(&'a ModPath);
impl<'a> UnescapedModPath<'a> {
pub fn display(&'a self, db: &'a dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a {
UnescapedDisplay { db, path: self }
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum PathKind {
Plain,
@ -110,52 +116,30 @@ impl ModPath {
UnescapedModPath(self)
}
fn _fmt(&self, f: &mut fmt::Formatter<'_>, escaped: bool) -> fmt::Result {
let mut first_segment = true;
let mut add_segment = |s| -> fmt::Result {
if !first_segment {
f.write_str("::")?;
}
first_segment = false;
f.write_str(s)?;
Ok(())
};
match self.kind {
PathKind::Plain => {}
PathKind::Super(0) => add_segment("self")?,
PathKind::Super(n) => {
for _ in 0..n {
add_segment("super")?;
}
}
PathKind::Crate => add_segment("crate")?,
PathKind::Abs => add_segment("")?,
PathKind::DollarCrate(_) => add_segment("$crate")?,
}
for segment in &self.segments {
if !first_segment {
f.write_str("::")?;
}
first_segment = false;
if escaped {
segment.fmt(f)?
} else {
segment.unescaped().fmt(f)?
};
}
Ok(())
pub fn display<'a>(&'a self, db: &'a dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a {
Display { db, path: self }
}
}
impl Display for ModPath {
struct Display<'a> {
db: &'a dyn ExpandDatabase,
path: &'a ModPath,
}
impl<'a> fmt::Display for Display<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self._fmt(f, true)
display_fmt_path(self.db, self.path, f, true)
}
}
impl<'a> Display for UnescapedModPath<'a> {
struct UnescapedDisplay<'a> {
db: &'a dyn ExpandDatabase,
path: &'a UnescapedModPath<'a>,
}
impl<'a> fmt::Display for UnescapedDisplay<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0._fmt(f, false)
display_fmt_path(self.db, self.path.0, f, false)
}
}
@ -164,6 +148,46 @@ impl From<Name> for ModPath {
ModPath::from_segments(PathKind::Plain, iter::once(name))
}
}
fn display_fmt_path(
db: &dyn ExpandDatabase,
path: &ModPath,
f: &mut fmt::Formatter<'_>,
escaped: bool,
) -> fmt::Result {
let mut first_segment = true;
let mut add_segment = |s| -> fmt::Result {
if !first_segment {
f.write_str("::")?;
}
first_segment = false;
f.write_str(s)?;
Ok(())
};
match path.kind {
PathKind::Plain => {}
PathKind::Super(0) => add_segment("self")?,
PathKind::Super(n) => {
for _ in 0..n {
add_segment("super")?;
}
}
PathKind::Crate => add_segment("crate")?,
PathKind::Abs => add_segment("")?,
PathKind::DollarCrate(_) => add_segment("$crate")?,
}
for segment in &path.segments {
if !first_segment {
f.write_str("::")?;
}
first_segment = false;
if escaped {
segment.display(db).fmt(f)?;
} else {
segment.unescaped().display(db).fmt(f)?;
}
}
Ok(())
}
fn convert_path(
db: &dyn ExpandDatabase,

View file

@ -24,27 +24,6 @@ enum Repr {
TupleField(usize),
}
impl fmt::Display for Name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.0 {
Repr::Text(text) => fmt::Display::fmt(&text, f),
Repr::TupleField(idx) => fmt::Display::fmt(&idx, f),
}
}
}
impl<'a> fmt::Display for UnescapedName<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.0 .0 {
Repr::Text(text) => {
let text = text.strip_prefix("r#").unwrap_or(text);
fmt::Display::fmt(&text, f)
}
Repr::TupleField(idx) => fmt::Display::fmt(&idx, f),
}
}
}
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.
@ -60,6 +39,11 @@ impl<'a> UnescapedName<'a> {
Repr::TupleField(it) => SmolStr::new(it.to_string()),
}
}
pub fn display(&'a self, db: &dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a {
_ = db;
UnescapedDisplay { name: self }
}
}
impl Name {
@ -78,7 +62,7 @@ impl Name {
Self::new_text(lt.text().into())
}
/// Shortcut to create inline plain text name
/// Shortcut to create inline plain text name. Panics if `text.len() > 22`
const fn new_inline(text: &str) -> Name {
Name::new_text(SmolStr::new_inline(text))
}
@ -112,6 +96,17 @@ impl Name {
Name::new_inline("[missing name]")
}
/// Generates a new name which is only equal to itself, by incrementing a counter. Due
/// its implementation, it should not be used in things that salsa considers, like
/// type names or field names, and it should be only used in names of local variables
/// and labels and similar things.
pub fn generate_new_name() -> Name {
use std::sync::atomic::{AtomicUsize, Ordering};
static CNT: AtomicUsize = AtomicUsize::new(0);
let c = CNT.fetch_add(1, Ordering::Relaxed);
Name::new_text(format!("<ra@gennew>{c}").into())
}
/// Returns the tuple index this name represents if it is a tuple field.
pub fn as_tuple_index(&self) -> Option<usize> {
match self.0 {
@ -156,6 +151,40 @@ impl Name {
Repr::TupleField(_) => false,
}
}
pub fn display<'a>(&'a self, db: &dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a {
_ = db;
Display { name: self }
}
}
struct Display<'a> {
name: &'a Name,
}
impl<'a> fmt::Display for Display<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.name.0 {
Repr::Text(text) => fmt::Display::fmt(&text, f),
Repr::TupleField(idx) => fmt::Display::fmt(&idx, f),
}
}
}
struct UnescapedDisplay<'a> {
name: &'a UnescapedName<'a>,
}
impl<'a> fmt::Display for UnescapedDisplay<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.name.0 .0 {
Repr::Text(text) => {
let text = text.strip_prefix("r#").unwrap_or(text);
fmt::Display::fmt(&text, f)
}
Repr::TupleField(idx) => fmt::Display::fmt(&idx, f),
}
}
}
pub trait AsName {
@ -337,18 +366,24 @@ pub mod known {
crate_type,
derive,
global_allocator,
no_core,
no_std,
test,
test_case,
recursion_limit,
feature,
// known methods of lang items
call_once,
call_mut,
call,
eq,
ne,
ge,
gt,
le,
lt,
// known fields of lang items
pieces,
// lang items
add_assign,
add,
@ -363,6 +398,7 @@ pub mod known {
deref,
div_assign,
div,
drop,
fn_mut,
fn_once,
future_trait,

View file

@ -7,20 +7,23 @@ use crate::{db::ExpandDatabase, tt, ExpandError, ExpandResult};
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct ProcMacroExpander {
proc_macro_id: Option<ProcMacroId>,
proc_macro_id: ProcMacroId,
}
const DUMMY_ID: u32 = !0;
impl ProcMacroExpander {
pub fn new(proc_macro_id: ProcMacroId) -> Self {
Self { proc_macro_id: Some(proc_macro_id) }
assert_ne!(proc_macro_id.0, DUMMY_ID);
Self { proc_macro_id }
}
pub fn dummy() -> Self {
Self { proc_macro_id: None }
Self { proc_macro_id: ProcMacroId(DUMMY_ID) }
}
pub fn is_dummy(&self) -> bool {
self.proc_macro_id.is_none()
self.proc_macro_id.0 == DUMMY_ID
}
pub fn expand(
@ -32,33 +35,37 @@ impl ProcMacroExpander {
attr_arg: Option<&tt::Subtree>,
) -> ExpandResult<tt::Subtree> {
match self.proc_macro_id {
Some(id) => {
let krate_graph = db.crate_graph();
let proc_macros = match &krate_graph[def_crate].proc_macro {
Ok(proc_macros) => proc_macros,
Err(_) => {
ProcMacroId(DUMMY_ID) => {
ExpandResult::new(tt::Subtree::empty(), ExpandError::UnresolvedProcMacro(def_crate))
}
ProcMacroId(id) => {
let proc_macros = db.proc_macros();
let proc_macros = match proc_macros.get(&def_crate) {
Some(Ok(proc_macros)) => proc_macros,
Some(Err(_)) | None => {
never!("Non-dummy expander even though there are no proc macros");
return ExpandResult::with_err(
return ExpandResult::new(
tt::Subtree::empty(),
ExpandError::Other("Internal error".into()),
);
}
};
let proc_macro = match proc_macros.get(id.0 as usize) {
let proc_macro = match proc_macros.get(id as usize) {
Some(proc_macro) => proc_macro,
None => {
never!(
"Proc macro index out of bounds: the length is {} but the index is {}",
proc_macros.len(),
id.0
id
);
return ExpandResult::with_err(
return ExpandResult::new(
tt::Subtree::empty(),
ExpandError::Other("Internal error".into()),
);
}
};
let krate_graph = db.crate_graph();
// Proc macros have access to the environment variables of the invoking crate.
let env = &krate_graph[calling_crate].env;
match proc_macro.expander.expand(tt, attr_arg, env) {
@ -74,17 +81,12 @@ impl ProcMacroExpander {
}
}
ProcMacroExpansionError::System(text)
| ProcMacroExpansionError::Panic(text) => ExpandResult::with_err(
tt::Subtree::empty(),
ExpandError::Other(text.into()),
),
| ProcMacroExpansionError::Panic(text) => {
ExpandResult::new(tt::Subtree::empty(), ExpandError::Other(text.into()))
}
},
}
}
None => ExpandResult::with_err(
tt::Subtree::empty(),
ExpandError::UnresolvedProcMacro(def_crate),
),
}
}
}

View file

@ -162,6 +162,12 @@ impl ToTokenTree for crate::tt::TokenTree {
}
}
impl ToTokenTree for &crate::tt::TokenTree {
fn to_token(self) -> crate::tt::TokenTree {
self.clone()
}
}
impl ToTokenTree for crate::tt::Subtree {
fn to_token(self) -> crate::tt::TokenTree {
self.into()