use std::{collections::hash_map::Entry, mem, sync::Arc};
use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, name::known, HirFileId};
-use smallvec::SmallVec;
use syntax::{
- ast::{self, ModuleItemOwner},
+ ast::{self, HasModuleItem},
SyntaxNode, WalkEvent,
};
use crate::{
generics::{GenericParams, TypeParamData, TypeParamProvenance},
- type_ref::{LifetimeRef, TraitRef},
+ type_ref::{LifetimeRef, TraitBoundModifier, TraitRef},
};
use super::*;
FileItemTreeId { index, _p: PhantomData }
}
-struct ModItems(SmallVec<[ModItem; 1]>);
-
-impl<T> From<T> for ModItems
-where
- T: Into<ModItem>,
-{
- fn from(t: T) -> Self {
- ModItems(SmallVec::from_buf([t.into(); 1]))
- }
-}
-
pub(super) struct Ctx<'a> {
db: &'a dyn DefDatabase,
tree: ItemTree,
hygiene: Hygiene,
- file: HirFileId,
source_ast_id_map: Arc<AstIdMap>,
body_ctx: crate::body::LowerCtx<'a>,
forced_visibility: Option<RawVisibilityId>,
db,
tree: ItemTree::default(),
hygiene,
- file,
source_ast_id_map: db.ast_id_map(file),
body_ctx: crate::body::LowerCtx::new(db, file),
forced_visibility: None,
}
}
- pub(super) fn lower_module_items(mut self, item_owner: &dyn ModuleItemOwner) -> ItemTree {
- self.tree.top_level = item_owner
- .items()
- .flat_map(|item| self.lower_mod_item(&item, false))
- .flat_map(|items| items.0)
- .collect();
+ pub(super) fn lower_module_items(mut self, item_owner: &dyn HasModuleItem) -> ItemTree {
+ self.tree.top_level =
+ item_owner.items().flat_map(|item| self.lower_mod_item(&item, false)).collect();
self.tree
}
.statements()
.filter_map(|stmt| match stmt {
ast::Stmt::Item(item) => Some(item),
+ // Macro calls can be both items and expressions. The syntax library always treats
+ // them as expressions here, so we undo that.
+ ast::Stmt::ExprStmt(es) => match es.expr()? {
+ ast::Expr::MacroCall(call) => {
+ cov_mark::hit!(macro_call_in_macro_stmts_is_added_to_item_tree);
+ Some(call.into())
+ }
+ _ => None,
+ },
_ => None,
})
.flat_map(|item| self.lower_mod_item(&item, false))
- .flat_map(|items| items.0)
.collect();
// Non-items need to have their inner items collected.
self.tree.data_mut()
}
- fn lower_mod_item(&mut self, item: &ast::Item, inner: bool) -> Option<ModItems> {
+ fn lower_mod_item(&mut self, item: &ast::Item, inner: bool) -> Option<ModItem> {
// Collect inner items for 1-to-1-lowered items.
match item {
ast::Item::Struct(_)
};
let attrs = RawAttrs::new(self.db, item, &self.hygiene);
- let items = match item {
- ast::Item::Struct(ast) => self.lower_struct(ast).map(Into::into),
- ast::Item::Union(ast) => self.lower_union(ast).map(Into::into),
- ast::Item::Enum(ast) => self.lower_enum(ast).map(Into::into),
- ast::Item::Fn(ast) => self.lower_function(ast).map(Into::into),
- ast::Item::TypeAlias(ast) => self.lower_type_alias(ast).map(Into::into),
- ast::Item::Static(ast) => self.lower_static(ast).map(Into::into),
- ast::Item::Const(ast) => Some(self.lower_const(ast).into()),
- ast::Item::Module(ast) => self.lower_module(ast).map(Into::into),
- ast::Item::Trait(ast) => self.lower_trait(ast).map(Into::into),
- ast::Item::Impl(ast) => self.lower_impl(ast).map(Into::into),
- ast::Item::Use(ast) => Some(ModItems(
- self.lower_use(ast).into_iter().map(Into::into).collect::<SmallVec<_>>(),
- )),
- ast::Item::ExternCrate(ast) => self.lower_extern_crate(ast).map(Into::into),
- ast::Item::MacroCall(ast) => self.lower_macro_call(ast).map(Into::into),
- ast::Item::MacroRules(ast) => self.lower_macro_rules(ast).map(Into::into),
- ast::Item::MacroDef(ast) => self.lower_macro_def(ast).map(Into::into),
- ast::Item::ExternBlock(ast) => Some(self.lower_extern_block(ast).into()),
+ let item: ModItem = match item {
+ ast::Item::Struct(ast) => self.lower_struct(ast)?.into(),
+ ast::Item::Union(ast) => self.lower_union(ast)?.into(),
+ ast::Item::Enum(ast) => self.lower_enum(ast)?.into(),
+ ast::Item::Fn(ast) => self.lower_function(ast)?.into(),
+ ast::Item::TypeAlias(ast) => self.lower_type_alias(ast)?.into(),
+ ast::Item::Static(ast) => self.lower_static(ast)?.into(),
+ ast::Item::Const(ast) => self.lower_const(ast).into(),
+ ast::Item::Module(ast) => self.lower_module(ast)?.into(),
+ ast::Item::Trait(ast) => self.lower_trait(ast)?.into(),
+ ast::Item::Impl(ast) => self.lower_impl(ast)?.into(),
+ ast::Item::Use(ast) => self.lower_use(ast)?.into(),
+ ast::Item::ExternCrate(ast) => self.lower_extern_crate(ast)?.into(),
+ ast::Item::MacroCall(ast) => self.lower_macro_call(ast)?.into(),
+ ast::Item::MacroRules(ast) => self.lower_macro_rules(ast)?.into(),
+ ast::Item::MacroDef(ast) => self.lower_macro_def(ast)?.into(),
+ ast::Item::ExternBlock(ast) => self.lower_extern_block(ast).into(),
};
- if !attrs.is_empty() {
- for item in items.iter().flat_map(|items| &items.0) {
- self.add_attrs((*item).into(), attrs.clone());
- }
- }
+ self.add_attrs(item.into(), attrs);
- items
+ Some(item)
}
fn add_attrs(&mut self, item: AttrOwner, attrs: RawAttrs) {
},
ast::Item(item) => {
// FIXME: This triggers for macro calls in expression/pattern/type position
- let mod_items = self.lower_mod_item(&item, true);
+ let mod_item = self.lower_mod_item(&item, true);
let current_block = block_stack.last();
- if let (Some(mod_items), Some(block)) = (mod_items, current_block) {
- if !mod_items.0.is_empty() {
- self.data().inner_items.entry(*block).or_default().extend(mod_items.0.iter().copied());
- }
+ if let (Some(mod_item), Some(block)) = (mod_item, current_block) {
+ self.data().inner_items.entry(*block).or_default().push(mod_item);
}
},
_ => {}
let name = Name::new_tuple_field(idx);
let visibility = self.lower_visibility(field);
let type_ref = self.lower_type_ref_opt(field.ty());
- let res = Field { name, type_ref, visibility };
- res
+ Field { name, type_ref, visibility }
}
fn lower_union(&mut self, union: &ast::Union) -> Option<FileItemTreeId<Union>> {
let visibility = self.lower_visibility(enum_);
let name = enum_.name()?.as_name();
let generic_params = self.lower_generic_params(GenericsOwner::Enum, enum_);
- let variants = match &enum_.variant_list() {
- Some(variant_list) => self.lower_variants(variant_list),
- None => IdRange::new(self.next_variant_idx()..self.next_variant_idx()),
- };
+ let variants =
+ self.with_inherited_visibility(visibility, |this| match &enum_.variant_list() {
+ Some(variant_list) => this.lower_variants(variant_list),
+ None => IdRange::new(this.next_variant_idx()..this.next_variant_idx()),
+ });
let ast_id = self.source_ast_id_map.ast_id(enum_);
let res = Enum { name, visibility, generic_params, variants, ast_id };
Some(id(self.data().enums.alloc(res)))
_ => TypeRef::unit(),
};
- let ret_type = if func.async_token().is_some() {
+ let (ret_type, async_ret_type) = if func.async_token().is_some() {
+ let async_ret_type = ret_type.clone();
let future_impl = desugar_future_path(ret_type);
- let ty_bound = TypeBound::Path(future_impl);
- TypeRef::ImplTrait(vec![ty_bound])
+ let ty_bound = Interned::new(TypeBound::Path(future_impl, TraitBoundModifier::None));
+ (TypeRef::ImplTrait(vec![ty_bound]), Some(async_ret_type))
} else {
- ret_type
+ (ret_type, None)
};
let abi = func.abi().map(lower_abi);
let mut res = Function {
name,
visibility,
- generic_params: Interned::new(GenericParams::default()),
+ explicit_generic_params: Interned::new(GenericParams::default()),
abi,
params,
ret_type: Interned::new(ret_type),
+ async_ret_type: async_ret_type.map(Interned::new),
ast_id,
flags,
};
- res.generic_params = self.lower_generic_params(GenericsOwner::Function(&res), func);
+ res.explicit_generic_params =
+ self.lower_generic_params(GenericsOwner::Function(&res), func);
Some(id(self.data().functions.alloc(res)))
}
}
fn lower_const(&mut self, konst: &ast::Const) -> FileItemTreeId<Const> {
- let name = konst.name().map(|it| it.as_name());
+ let mut name = konst.name().map(|it| it.as_name());
+ if name.as_ref().map_or(false, |n| n.to_string().starts_with("_DERIVE_")) {
+ // FIXME: this is a hack to treat consts generated by synstructure as unnamed
+ // remove this some time in the future
+ name = None;
+ }
let type_ref = self.lower_type_ref_opt(konst.ty());
let visibility = self.lower_visibility(konst);
let ast_id = self.source_ast_id_map.ast_id(konst);
items: module
.item_list()
.map(|list| {
- list.items()
- .flat_map(|item| self.lower_mod_item(&item, false))
- .flat_map(|items| items.0)
- .collect()
+ list.items().flat_map(|item| self.lower_mod_item(&item, false)).collect()
})
.unwrap_or_else(|| {
cov_mark::hit!(name_res_works_for_broken_modules);
self.lower_generic_params_and_inner_items(GenericsOwner::Trait(trait_def), trait_def);
let is_auto = trait_def.auto_token().is_some();
let is_unsafe = trait_def.unsafe_token().is_some();
- let bounds = self.lower_type_bounds(trait_def);
let items = trait_def.assoc_item_list().map(|list| {
let db = self.db;
self.with_inherited_visibility(visibility, |this| {
generic_params,
is_auto,
is_unsafe,
- bounds: bounds.into(),
items: items.unwrap_or_default(),
ast_id,
};
Some(id(self.data().impls.alloc(res)))
}
- fn lower_use(&mut self, use_item: &ast::Use) -> Vec<FileItemTreeId<Import>> {
+ fn lower_use(&mut self, use_item: &ast::Use) -> Option<FileItemTreeId<Import>> {
let visibility = self.lower_visibility(use_item);
let ast_id = self.source_ast_id_map.ast_id(use_item);
+ let (use_tree, _) = lower_use_tree(self.db, &self.hygiene, use_item.use_tree()?)?;
- // Every use item can expand to many `Import`s.
- let mut imports = Vec::new();
- let tree = self.tree.data_mut();
- ModPath::expand_use_item(
- self.db,
- InFile::new(self.file, use_item.clone()),
- &self.hygiene,
- |path, _use_tree, is_glob, alias| {
- imports.push(id(tree.imports.alloc(Import {
- path: Interned::new(path),
- alias,
- visibility,
- is_glob,
- ast_id,
- index: imports.len(),
- })));
- },
- );
-
- imports
+ let res = Import { visibility, ast_id, use_tree };
+ Some(id(self.data().imports.alloc(res)))
}
fn lower_extern_crate(
fn lower_macro_call(&mut self, m: &ast::MacroCall) -> Option<FileItemTreeId<MacroCall>> {
let path = Interned::new(ModPath::from_src(self.db, m.path()?, &self.hygiene)?);
let ast_id = self.source_ast_id_map.ast_id(m);
- let fragment = hir_expand::to_fragment_kind(m);
- let res = MacroCall { path, ast_id, fragment };
+ let expand_to = hir_expand::ExpandTo::from_call_site(m);
+ let res = MacroCall { path, ast_id, expand_to };
Some(id(self.data().macro_calls.alloc(res)))
}
fn lower_generic_params_and_inner_items(
&mut self,
owner: GenericsOwner<'_>,
- node: &impl ast::GenericParamsOwner,
+ node: &dyn ast::HasGenericParams,
) -> Interned<GenericParams> {
// Generics are part of item headers and may contain inner items we need to collect.
if let Some(params) = node.generic_param_list() {
fn lower_generic_params(
&mut self,
owner: GenericsOwner<'_>,
- node: &impl ast::GenericParamsOwner,
+ node: &dyn ast::HasGenericParams,
) -> Interned<GenericParams> {
- let mut sm = &mut Default::default();
let mut generics = GenericParams::default();
match owner {
- GenericsOwner::Function(func) => {
- generics.fill(&self.body_ctx, sm, node);
- // lower `impl Trait` in arguments
- for id in func.params.clone() {
- if let Param::Normal(ty) = &self.data().params[id] {
- generics.fill_implicit_impl_trait_args(ty);
- }
- }
- }
- GenericsOwner::Struct
+ GenericsOwner::Function(_)
+ | GenericsOwner::Struct
| GenericsOwner::Enum
| GenericsOwner::Union
| GenericsOwner::TypeAlias => {
- generics.fill(&self.body_ctx, sm, node);
+ generics.fill(&self.body_ctx, node);
}
GenericsOwner::Trait(trait_def) => {
// traits get the Self type as an implicit first type parameter
- let self_param_id = generics.types.alloc(TypeParamData {
+ generics.types.alloc(TypeParamData {
name: Some(name![Self]),
default: None,
provenance: TypeParamProvenance::TraitSelf,
});
- sm.type_params.insert(self_param_id, Either::Left(trait_def.clone()));
// add super traits as bounds on Self
// i.e., trait Foo: Bar is equivalent to trait Foo where Self: Bar
let self_param = TypeRef::Path(name![Self].into());
generics.fill_bounds(&self.body_ctx, trait_def, Either::Left(self_param));
- generics.fill(&self.body_ctx, &mut sm, node);
+ generics.fill(&self.body_ctx, node);
}
GenericsOwner::Impl => {
// Note that we don't add `Self` here: in `impl`s, `Self` is not a
// type-parameter, but rather is a type-alias for impl's target
// type, so this is handled by the resolver.
- generics.fill(&self.body_ctx, &mut sm, node);
+ generics.fill(&self.body_ctx, node);
}
}
Interned::new(generics)
}
- fn lower_type_bounds(&mut self, node: &impl ast::TypeBoundsOwner) -> Vec<TypeBound> {
+ fn lower_type_bounds(&mut self, node: &dyn ast::HasTypeBounds) -> Vec<Interned<TypeBound>> {
match node.type_bound_list() {
- Some(bound_list) => {
- bound_list.bounds().map(|it| TypeBound::from_ast(&self.body_ctx, it)).collect()
- }
+ Some(bound_list) => bound_list
+ .bounds()
+ .map(|it| Interned::new(TypeBound::from_ast(&self.body_ctx, it)))
+ .collect(),
None => Vec::new(),
}
}
- fn lower_visibility(&mut self, item: &impl ast::VisibilityOwner) -> RawVisibilityId {
+ fn lower_visibility(&mut self, item: &dyn ast::HasVisibility) -> RawVisibilityId {
let vis = match self.forced_visibility {
Some(vis) => return vis,
None => RawVisibility::from_ast_with_hygiene(self.db, item.visibility(), &self.hygiene),
let binding =
AssociatedTypeBinding { name: name![Output], type_ref: Some(orig), bounds: Vec::new() };
last.bindings.push(binding);
- generic_args.push(Some(Arc::new(last)));
+ generic_args.push(Some(Interned::new(last)));
Path::from_known_path(path, generic_args)
}
/// Returns `true` if the given intrinsic is unsafe to call, or false otherwise.
fn is_intrinsic_fn_unsafe(name: &Name) -> bool {
- // Should be kept in sync with https://github.com/rust-lang/rust/blob/c6e4db620a7d2f569f11dcab627430921ea8aacf/compiler/rustc_typeck/src/check/intrinsic.rs#L68
+ // Should be kept in sync with https://github.com/rust-lang/rust/blob/0cd0709f19d316c4796fa71c5f52c8612a5f3771/compiler/rustc_typeck/src/check/intrinsic.rs#L72-L105
![
known::abort,
- known::min_align_of,
- known::needs_drop,
- known::caller_location,
- known::size_of_val,
- known::min_align_of_val,
known::add_with_overflow,
- known::sub_with_overflow,
- known::mul_with_overflow,
- known::wrapping_add,
- known::wrapping_sub,
- known::wrapping_mul,
- known::saturating_add,
- known::saturating_sub,
- known::rotate_left,
- known::rotate_right,
- known::ctpop,
+ known::bitreverse,
+ known::bswap,
+ known::caller_location,
known::ctlz,
+ known::ctpop,
known::cttz,
- known::bswap,
- known::bitreverse,
known::discriminant_value,
- known::type_id,
+ known::forget,
known::likely,
- known::unlikely,
- known::ptr_guaranteed_eq,
- known::ptr_guaranteed_ne,
+ known::maxnumf32,
+ known::maxnumf64,
+ known::min_align_of,
known::minnumf32,
known::minnumf64,
- known::maxnumf32,
+ known::mul_with_overflow,
+ known::needs_drop,
+ known::ptr_guaranteed_eq,
+ known::ptr_guaranteed_ne,
+ known::rotate_left,
+ known::rotate_right,
known::rustc_peek,
- known::maxnumf64,
+ known::saturating_add,
+ known::saturating_sub,
+ known::size_of,
+ known::sub_with_overflow,
+ known::type_id,
known::type_name,
+ known::unlikely,
known::variant_count,
+ known::wrapping_add,
+ known::wrapping_mul,
+ known::wrapping_sub,
]
- .contains(&name)
+ .contains(name)
}
fn lower_abi(abi: ast::Abi) -> Interned<str> {
}
}
}
+
+struct UseTreeLowering<'a> {
+ db: &'a dyn DefDatabase,
+ hygiene: &'a Hygiene,
+ mapping: Arena<ast::UseTree>,
+}
+
+impl UseTreeLowering<'_> {
+ fn lower_use_tree(&mut self, tree: ast::UseTree) -> Option<UseTree> {
+ if let Some(use_tree_list) = tree.use_tree_list() {
+ let prefix = match tree.path() {
+ // E.g. use something::{{{inner}}};
+ None => None,
+ // E.g. `use something::{inner}` (prefix is `None`, path is `something`)
+ // or `use something::{path::{inner::{innerer}}}` (prefix is `something::path`, path is `inner`)
+ Some(path) => {
+ match ModPath::from_src(self.db, path, self.hygiene) {
+ Some(it) => Some(it),
+ None => return None, // FIXME: report errors somewhere
+ }
+ }
+ };
+
+ let list =
+ use_tree_list.use_trees().filter_map(|tree| self.lower_use_tree(tree)).collect();
+
+ Some(
+ self.use_tree(
+ UseTreeKind::Prefixed { prefix: prefix.map(Interned::new), list },
+ tree,
+ ),
+ )
+ } else {
+ let is_glob = tree.star_token().is_some();
+ let path = match tree.path() {
+ Some(path) => Some(ModPath::from_src(self.db, path, self.hygiene)?),
+ None => None,
+ };
+ let alias = tree.rename().map(|a| {
+ a.name().map(|it| it.as_name()).map_or(ImportAlias::Underscore, ImportAlias::Alias)
+ });
+ if alias.is_some() && is_glob {
+ return None;
+ }
+
+ match (path, alias, is_glob) {
+ (path, None, true) => {
+ if path.is_none() {
+ cov_mark::hit!(glob_enum_group);
+ }
+ Some(self.use_tree(UseTreeKind::Glob { path: path.map(Interned::new) }, tree))
+ }
+ // Globs can't be renamed
+ (_, Some(_), true) | (None, None, false) => None,
+ // `bla::{ as Name}` is invalid
+ (None, Some(_), false) => None,
+ (Some(path), alias, false) => Some(
+ self.use_tree(UseTreeKind::Single { path: Interned::new(path), alias }, tree),
+ ),
+ }
+ }
+ }
+
+ fn use_tree(&mut self, kind: UseTreeKind, ast: ast::UseTree) -> UseTree {
+ let index = self.mapping.alloc(ast);
+ UseTree { index, kind }
+ }
+}
+
+pub(super) fn lower_use_tree(
+ db: &dyn DefDatabase,
+ hygiene: &Hygiene,
+ tree: ast::UseTree,
+) -> Option<(UseTree, Arena<ast::UseTree>)> {
+ let mut lowering = UseTreeLowering { db, hygiene, mapping: Arena::new() };
+ let tree = lowering.lower_use_tree(tree)?;
+ Some((tree, lowering.mapping))
+}