From 16e620c052016010b2f17070a98bdc1e7e849ab3 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 30 Oct 2019 16:12:55 +0300 Subject: move raw_items to hir_def --- crates/ra_hir_def/src/attr.rs | 91 ++++++++ crates/ra_hir_def/src/db.rs | 18 ++ crates/ra_hir_def/src/either.rs | 54 +++++ crates/ra_hir_def/src/lib.rs | 95 +++++++- crates/ra_hir_def/src/name.rs | 142 ++++++++++++ crates/ra_hir_def/src/nameres.rs | 1 + crates/ra_hir_def/src/nameres/raw.rs | 407 +++++++++++++++++++++++++++++++++ crates/ra_hir_def/src/path.rs | 423 +++++++++++++++++++++++++++++++++++ crates/ra_hir_def/src/type_ref.rs | 162 ++++++++++++++ 9 files changed, 1392 insertions(+), 1 deletion(-) create mode 100644 crates/ra_hir_def/src/attr.rs create mode 100644 crates/ra_hir_def/src/either.rs create mode 100644 crates/ra_hir_def/src/name.rs create mode 100644 crates/ra_hir_def/src/nameres.rs create mode 100644 crates/ra_hir_def/src/nameres/raw.rs create mode 100644 crates/ra_hir_def/src/path.rs create mode 100644 crates/ra_hir_def/src/type_ref.rs (limited to 'crates/ra_hir_def/src') diff --git a/crates/ra_hir_def/src/attr.rs b/crates/ra_hir_def/src/attr.rs new file mode 100644 index 000000000..248f03cdf --- /dev/null +++ b/crates/ra_hir_def/src/attr.rs @@ -0,0 +1,91 @@ +//! A higher level attributes based on TokenTree, with also some shortcuts. + +use std::sync::Arc; + +use hir_expand::db::AstDatabase; +use mbe::ast_to_token_tree; +use ra_cfg::CfgOptions; +use ra_syntax::{ + ast::{self, AstNode, AttrsOwner}, + SmolStr, +}; +use tt::Subtree; + +use crate::{path::Path, HirFileId, Source}; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Attr { + pub(crate) path: Path, + pub(crate) input: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum AttrInput { + Literal(SmolStr), + TokenTree(Subtree), +} + +impl Attr { + pub(crate) fn from_src( + Source { file_id, ast }: Source, + db: &impl AstDatabase, + ) -> Option { + let path = Path::from_src(Source { file_id, ast: ast.path()? }, db)?; + let input = match ast.input() { + None => None, + Some(ast::AttrInput::Literal(lit)) => { + // FIXME: escape? raw string? + let value = lit.syntax().first_token()?.text().trim_matches('"').into(); + Some(AttrInput::Literal(value)) + } + Some(ast::AttrInput::TokenTree(tt)) => { + Some(AttrInput::TokenTree(ast_to_token_tree(&tt)?.0)) + } + }; + + Some(Attr { path, input }) + } + + pub fn from_attrs_owner( + file_id: HirFileId, + owner: &dyn AttrsOwner, + db: &impl AstDatabase, + ) -> Option> { + let mut attrs = owner.attrs().peekable(); + if attrs.peek().is_none() { + // Avoid heap allocation + return None; + } + Some(attrs.flat_map(|ast| Attr::from_src(Source { file_id, ast }, db)).collect()) + } + + pub fn is_simple_atom(&self, name: &str) -> bool { + // FIXME: Avoid cloning + self.path.as_ident().map_or(false, |s| s.to_string() == name) + } + + // FIXME: handle cfg_attr :-) + pub fn as_cfg(&self) -> Option<&Subtree> { + if !self.is_simple_atom("cfg") { + return None; + } + match &self.input { + Some(AttrInput::TokenTree(subtree)) => Some(subtree), + _ => None, + } + } + + pub fn as_path(&self) -> Option<&SmolStr> { + if !self.is_simple_atom("path") { + return None; + } + match &self.input { + Some(AttrInput::Literal(it)) => Some(it), + _ => None, + } + } + + pub fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> Option { + cfg_options.is_cfg_enabled(self.as_cfg()?) + } +} diff --git a/crates/ra_hir_def/src/db.rs b/crates/ra_hir_def/src/db.rs index f6f976c86..b271636b0 100644 --- a/crates/ra_hir_def/src/db.rs +++ b/crates/ra_hir_def/src/db.rs @@ -1,8 +1,12 @@ //! Defines database & queries for name resolution. +use std::sync::Arc; +use hir_expand::{db::AstDatabase, HirFileId}; use ra_db::{salsa, SourceDatabase}; use ra_syntax::ast; +use crate::nameres::raw::{ImportSourceMap, RawItems}; + #[salsa::query_group(InternDatabaseStorage)] pub trait InternDatabase: SourceDatabase { #[salsa::interned] @@ -10,6 +14,8 @@ pub trait InternDatabase: SourceDatabase { #[salsa::interned] fn intern_struct(&self, loc: crate::ItemLoc) -> crate::StructId; #[salsa::interned] + fn intern_union(&self, loc: crate::ItemLoc) -> crate::UnionId; + #[salsa::interned] fn intern_enum(&self, loc: crate::ItemLoc) -> crate::EnumId; #[salsa::interned] fn intern_const(&self, loc: crate::ItemLoc) -> crate::ConstId; @@ -20,3 +26,15 @@ pub trait InternDatabase: SourceDatabase { #[salsa::interned] fn intern_type_alias(&self, loc: crate::ItemLoc) -> crate::TypeAliasId; } + +#[salsa::query_group(DefDatabase2Storage)] +pub trait DefDatabase2: InternDatabase + AstDatabase { + #[salsa::invoke(RawItems::raw_items_with_source_map_query)] + fn raw_items_with_source_map( + &self, + file_id: HirFileId, + ) -> (Arc, Arc); + + #[salsa::invoke(RawItems::raw_items_query)] + fn raw_items(&self, file_id: HirFileId) -> Arc; +} diff --git a/crates/ra_hir_def/src/either.rs b/crates/ra_hir_def/src/either.rs new file mode 100644 index 000000000..83583ef8b --- /dev/null +++ b/crates/ra_hir_def/src/either.rs @@ -0,0 +1,54 @@ +//! FIXME: write short doc here + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum Either { + A(A), + B(B), +} + +impl Either { + pub fn either(self, f1: F1, f2: F2) -> R + where + F1: FnOnce(A) -> R, + F2: FnOnce(B) -> R, + { + match self { + Either::A(a) => f1(a), + Either::B(b) => f2(b), + } + } + pub fn map(self, f1: F1, f2: F2) -> Either + where + F1: FnOnce(A) -> U, + F2: FnOnce(B) -> V, + { + match self { + Either::A(a) => Either::A(f1(a)), + Either::B(b) => Either::B(f2(b)), + } + } + pub fn map_a(self, f: F) -> Either + where + F: FnOnce(A) -> U, + { + self.map(f, |it| it) + } + pub fn a(self) -> Option { + match self { + Either::A(it) => Some(it), + Either::B(_) => None, + } + } + pub fn b(self) -> Option { + match self { + Either::A(_) => None, + Either::B(it) => Some(it), + } + } + pub fn as_ref(&self) -> Either<&A, &B> { + match self { + Either::A(it) => Either::A(it), + Either::B(it) => Either::B(it), + } + } +} diff --git a/crates/ra_hir_def/src/lib.rs b/crates/ra_hir_def/src/lib.rs index 4d6b9db03..95d503325 100644 --- a/crates/ra_hir_def/src/lib.rs +++ b/crates/ra_hir_def/src/lib.rs @@ -8,12 +8,20 @@ //! actually true. pub mod db; +pub mod either; +pub mod attr; +pub mod name; +pub mod path; +pub mod type_ref; + +// FIXME: this should be private +pub mod nameres; use std::hash::{Hash, Hasher}; use hir_expand::{ast_id_map::FileAstId, db::AstDatabase, AstId, HirFileId}; use ra_arena::{impl_arena_id, RawId}; -use ra_db::{salsa, CrateId}; +use ra_db::{salsa, CrateId, FileId}; use ra_syntax::{ast, AstNode, SyntaxNode}; use crate::db::InternDatabase; @@ -24,6 +32,68 @@ pub struct Source { pub ast: T, } +pub enum ModuleSource { + SourceFile(ast::SourceFile), + Module(ast::Module), +} + +impl ModuleSource { + pub fn new( + db: &impl db::DefDatabase2, + file_id: Option, + decl_id: Option>, + ) -> ModuleSource { + match (file_id, decl_id) { + (Some(file_id), _) => { + let source_file = db.parse(file_id).tree(); + ModuleSource::SourceFile(source_file) + } + (None, Some(item_id)) => { + let module = item_id.to_node(db); + assert!(module.item_list().is_some(), "expected inline module"); + ModuleSource::Module(module) + } + (None, None) => panic!(), + } + } + + // FIXME: this methods do not belong here + pub fn from_position( + db: &impl db::DefDatabase2, + position: ra_db::FilePosition, + ) -> ModuleSource { + let parse = db.parse(position.file_id); + match &ra_syntax::algo::find_node_at_offset::( + parse.tree().syntax(), + position.offset, + ) { + Some(m) if !m.has_semi() => ModuleSource::Module(m.clone()), + _ => { + let source_file = parse.tree(); + ModuleSource::SourceFile(source_file) + } + } + } + + pub fn from_child_node( + db: &impl db::DefDatabase2, + file_id: FileId, + child: &SyntaxNode, + ) -> ModuleSource { + if let Some(m) = child.ancestors().filter_map(ast::Module::cast).find(|it| !it.has_semi()) { + ModuleSource::Module(m) + } else { + let source_file = db.parse(file_id).tree(); + ModuleSource::SourceFile(source_file) + } + } + + pub fn from_file_id(db: &impl db::DefDatabase2, file_id: FileId) -> ModuleSource { + let source_file = db.parse(file_id).tree(); + ModuleSource::SourceFile(source_file) + } +} + impl Source { pub fn map U, U>(self, f: F) -> Source { Source { file_id: self.file_id, ast: f(self.ast) } @@ -155,6 +225,18 @@ impl AstItemDef for StructId { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct UnionId(salsa::InternId); +impl_intern_key!(UnionId); +impl AstItemDef for UnionId { + fn intern(db: &impl InternDatabase, loc: ItemLoc) -> Self { + db.intern_union(loc) + } + fn lookup_intern(self, db: &impl InternDatabase) -> ItemLoc { + db.lookup_intern_union(self) + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct EnumId(salsa::InternId); impl_intern_key!(EnumId); @@ -167,6 +249,17 @@ impl AstItemDef for EnumId { } } +// FIXME: rename to `VariantId`, only enums can ave variants +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct EnumVariantId { + parent: EnumId, + local_id: LocalEnumVariantId, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub(crate) struct LocalEnumVariantId(RawId); +impl_arena_id!(LocalEnumVariantId); + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct ConstId(salsa::InternId); impl_intern_key!(ConstId); diff --git a/crates/ra_hir_def/src/name.rs b/crates/ra_hir_def/src/name.rs new file mode 100644 index 000000000..720896ee8 --- /dev/null +++ b/crates/ra_hir_def/src/name.rs @@ -0,0 +1,142 @@ +//! FIXME: write short doc here + +use std::fmt; + +use ra_syntax::{ast, SmolStr}; + +/// `Name` is a wrapper around string, which is used in hir for both references +/// and declarations. In theory, names should also carry hygiene info, but we are +/// not there yet! +#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct Name(Repr); + +#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +enum Repr { + Text(SmolStr), + 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 Name { + /// Note: this is private to make creating name from random string hard. + /// Hopefully, this should allow us to integrate hygiene cleaner in the + /// future, and to switch to interned representation of names. + const fn new_text(text: SmolStr) -> Name { + Name(Repr::Text(text)) + } + + pub fn new_tuple_field(idx: usize) -> Name { + Name(Repr::TupleField(idx)) + } + + /// Shortcut to create inline plain text name + const fn new_inline_ascii(len: usize, text: &[u8]) -> Name { + Name::new_text(SmolStr::new_inline_from_ascii(len, text)) + } + + /// Resolve a name from the text of token. + fn resolve(raw_text: &SmolStr) -> Name { + let raw_start = "r#"; + if raw_text.as_str().starts_with(raw_start) { + Name::new_text(SmolStr::new(&raw_text[raw_start.len()..])) + } else { + Name::new_text(raw_text.clone()) + } + } + + pub fn missing() -> Name { + Name::new_text("[missing name]".into()) + } + + pub fn as_tuple_index(&self) -> Option { + match self.0 { + Repr::TupleField(idx) => Some(idx), + _ => None, + } + } +} + +pub trait AsName { + fn as_name(&self) -> Name; +} + +impl AsName for ast::NameRef { + fn as_name(&self) -> Name { + match self.as_tuple_field() { + Some(idx) => Name::new_tuple_field(idx), + None => Name::resolve(self.text()), + } + } +} + +impl AsName for ast::Name { + fn as_name(&self) -> Name { + Name::resolve(self.text()) + } +} + +impl AsName for ast::FieldKind { + fn as_name(&self) -> Name { + match self { + ast::FieldKind::Name(nr) => nr.as_name(), + ast::FieldKind::Index(idx) => Name::new_tuple_field(idx.text().parse().unwrap()), + } + } +} + +impl AsName for ra_db::Dependency { + fn as_name(&self) -> Name { + Name::new_text(self.name.clone()) + } +} + +// Primitives +pub const ISIZE: Name = Name::new_inline_ascii(5, b"isize"); +pub const I8: Name = Name::new_inline_ascii(2, b"i8"); +pub const I16: Name = Name::new_inline_ascii(3, b"i16"); +pub const I32: Name = Name::new_inline_ascii(3, b"i32"); +pub const I64: Name = Name::new_inline_ascii(3, b"i64"); +pub const I128: Name = Name::new_inline_ascii(4, b"i128"); +pub const USIZE: Name = Name::new_inline_ascii(5, b"usize"); +pub const U8: Name = Name::new_inline_ascii(2, b"u8"); +pub const U16: Name = Name::new_inline_ascii(3, b"u16"); +pub const U32: Name = Name::new_inline_ascii(3, b"u32"); +pub const U64: Name = Name::new_inline_ascii(3, b"u64"); +pub const U128: Name = Name::new_inline_ascii(4, b"u128"); +pub const F32: Name = Name::new_inline_ascii(3, b"f32"); +pub const F64: Name = Name::new_inline_ascii(3, b"f64"); +pub const BOOL: Name = Name::new_inline_ascii(4, b"bool"); +pub const CHAR: Name = Name::new_inline_ascii(4, b"char"); +pub const STR: Name = Name::new_inline_ascii(3, b"str"); + +// Special names +pub const SELF_PARAM: Name = Name::new_inline_ascii(4, b"self"); +pub const SELF_TYPE: Name = Name::new_inline_ascii(4, b"Self"); +pub const MACRO_RULES: Name = Name::new_inline_ascii(11, b"macro_rules"); + +// Components of known path (value or mod name) +pub const STD: Name = Name::new_inline_ascii(3, b"std"); +pub const ITER: Name = Name::new_inline_ascii(4, b"iter"); +pub const OPS: Name = Name::new_inline_ascii(3, b"ops"); +pub const FUTURE: Name = Name::new_inline_ascii(6, b"future"); +pub const RESULT: Name = Name::new_inline_ascii(6, b"result"); +pub const BOXED: Name = Name::new_inline_ascii(5, b"boxed"); + +// Components of known path (type name) +pub const INTO_ITERATOR_TYPE: Name = Name::new_inline_ascii(12, b"IntoIterator"); +pub const ITEM_TYPE: Name = Name::new_inline_ascii(4, b"Item"); +pub const TRY_TYPE: Name = Name::new_inline_ascii(3, b"Try"); +pub const OK_TYPE: Name = Name::new_inline_ascii(2, b"Ok"); +pub const FUTURE_TYPE: Name = Name::new_inline_ascii(6, b"Future"); +pub const RESULT_TYPE: Name = Name::new_inline_ascii(6, b"Result"); +pub const OUTPUT_TYPE: Name = Name::new_inline_ascii(6, b"Output"); +pub const TARGET_TYPE: Name = Name::new_inline_ascii(6, b"Target"); +pub const BOX_TYPE: Name = Name::new_inline_ascii(3, b"Box"); diff --git a/crates/ra_hir_def/src/nameres.rs b/crates/ra_hir_def/src/nameres.rs new file mode 100644 index 000000000..1a3f7667d --- /dev/null +++ b/crates/ra_hir_def/src/nameres.rs @@ -0,0 +1 @@ +pub mod raw; diff --git a/crates/ra_hir_def/src/nameres/raw.rs b/crates/ra_hir_def/src/nameres/raw.rs new file mode 100644 index 000000000..13b9fbf48 --- /dev/null +++ b/crates/ra_hir_def/src/nameres/raw.rs @@ -0,0 +1,407 @@ +//! FIXME: write short doc here + +use std::{ops::Index, sync::Arc}; + +use hir_expand::{ast_id_map::AstIdMap, db::AstDatabase}; +use ra_arena::{impl_arena_id, map::ArenaMap, Arena, RawId}; +use ra_syntax::{ + ast::{self, AttrsOwner, NameOwner}, + AstNode, AstPtr, SourceFile, +}; +use test_utils::tested_by; + +use crate::{ + attr::Attr, + db::DefDatabase2, + either::Either, + name::{AsName, Name}, + path::Path, + FileAstId, HirFileId, ModuleSource, Source, +}; + +/// `RawItems` is a set of top-level items in a file (except for impls). +/// +/// It is the input to name resolution algorithm. `RawItems` are not invalidated +/// on most edits. +#[derive(Debug, Default, PartialEq, Eq)] +pub struct RawItems { + modules: Arena, + imports: Arena, + defs: Arena, + macros: Arena, + /// items for top-level module + items: Vec, +} + +#[derive(Debug, Default, PartialEq, Eq)] +pub struct ImportSourceMap { + map: ArenaMap, +} + +type ImportSourcePtr = Either, AstPtr>; +type ImportSource = Either; + +impl ImportSourcePtr { + fn to_node(self, file: &SourceFile) -> ImportSource { + self.map(|ptr| ptr.to_node(file.syntax()), |ptr| ptr.to_node(file.syntax())) + } +} + +impl ImportSourceMap { + fn insert(&mut self, import: ImportId, ptr: ImportSourcePtr) { + self.map.insert(import, ptr) + } + + pub fn get(&self, source: &ModuleSource, import: ImportId) -> ImportSource { + let file = match source { + ModuleSource::SourceFile(file) => file.clone(), + ModuleSource::Module(m) => m.syntax().ancestors().find_map(SourceFile::cast).unwrap(), + }; + + self.map[import].to_node(&file) + } +} + +impl RawItems { + pub(crate) fn raw_items_query( + db: &(impl DefDatabase2 + AstDatabase), + file_id: HirFileId, + ) -> Arc { + db.raw_items_with_source_map(file_id).0 + } + + pub(crate) fn raw_items_with_source_map_query( + db: &(impl DefDatabase2 + AstDatabase), + file_id: HirFileId, + ) -> (Arc, Arc) { + let mut collector = RawItemsCollector { + raw_items: RawItems::default(), + source_ast_id_map: db.ast_id_map(file_id), + source_map: ImportSourceMap::default(), + file_id, + db, + }; + if let Some(node) = db.parse_or_expand(file_id) { + if let Some(source_file) = ast::SourceFile::cast(node.clone()) { + collector.process_module(None, source_file); + } else if let Some(item_list) = ast::MacroItems::cast(node) { + collector.process_module(None, item_list); + } + } + (Arc::new(collector.raw_items), Arc::new(collector.source_map)) + } + + pub fn items(&self) -> &[RawItem] { + &self.items + } +} + +impl Index for RawItems { + type Output = ModuleData; + fn index(&self, idx: Module) -> &ModuleData { + &self.modules[idx] + } +} + +impl Index for RawItems { + type Output = ImportData; + fn index(&self, idx: ImportId) -> &ImportData { + &self.imports[idx] + } +} + +impl Index for RawItems { + type Output = DefData; + fn index(&self, idx: Def) -> &DefData { + &self.defs[idx] + } +} + +impl Index for RawItems { + type Output = MacroData; + fn index(&self, idx: Macro) -> &MacroData { + &self.macros[idx] + } +} + +// Avoid heap allocation on items without attributes. +type Attrs = Option>; + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct RawItem { + attrs: Attrs, + pub kind: RawItemKind, +} + +impl RawItem { + pub fn attrs(&self) -> &[Attr] { + self.attrs.as_ref().map_or(&[], |it| &*it) + } +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum RawItemKind { + Module(Module), + Import(ImportId), + Def(Def), + Macro(Macro), +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct Module(RawId); +impl_arena_id!(Module); + +#[derive(Debug, PartialEq, Eq)] +pub enum ModuleData { + Declaration { name: Name, ast_id: FileAstId }, + Definition { name: Name, ast_id: FileAstId, items: Vec }, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct ImportId(RawId); +impl_arena_id!(ImportId); + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ImportData { + pub path: Path, + pub alias: Option, + pub is_glob: bool, + pub is_prelude: bool, + pub is_extern_crate: bool, + pub is_macro_use: bool, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct Def(RawId); +impl_arena_id!(Def); + +#[derive(Debug, PartialEq, Eq)] +pub struct DefData { + pub name: Name, + pub kind: DefKind, +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum DefKind { + Function(FileAstId), + Struct(FileAstId), + Union(FileAstId), + Enum(FileAstId), + Const(FileAstId), + Static(FileAstId), + Trait(FileAstId), + TypeAlias(FileAstId), +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct Macro(RawId); +impl_arena_id!(Macro); + +#[derive(Debug, PartialEq, Eq)] +pub struct MacroData { + pub ast_id: FileAstId, + pub path: Path, + pub name: Option, + pub export: bool, +} + +struct RawItemsCollector { + raw_items: RawItems, + source_ast_id_map: Arc, + source_map: ImportSourceMap, + file_id: HirFileId, + db: DB, +} + +impl RawItemsCollector<&DB> { + fn process_module(&mut self, current_module: Option, body: impl ast::ModuleItemOwner) { + for item_or_macro in body.items_with_macros() { + match item_or_macro { + ast::ItemOrMacro::Macro(m) => self.add_macro(current_module, m), + ast::ItemOrMacro::Item(item) => self.add_item(current_module, item), + } + } + } + + fn add_item(&mut self, current_module: Option, item: ast::ModuleItem) { + let attrs = self.parse_attrs(&item); + let (kind, name) = match item { + ast::ModuleItem::Module(module) => { + self.add_module(current_module, module); + return; + } + ast::ModuleItem::UseItem(use_item) => { + self.add_use_item(current_module, use_item); + return; + } + ast::ModuleItem::ExternCrateItem(extern_crate) => { + self.add_extern_crate_item(current_module, extern_crate); + return; + } + ast::ModuleItem::ImplBlock(_) => { + // impls don't participate in name resolution + return; + } + ast::ModuleItem::StructDef(it) => { + let id = self.source_ast_id_map.ast_id(&it); + let name = it.name(); + if it.is_union() { + (DefKind::Union(id), name) + } else { + (DefKind::Struct(id), name) + } + } + ast::ModuleItem::EnumDef(it) => { + (DefKind::Enum(self.source_ast_id_map.ast_id(&it)), it.name()) + } + ast::ModuleItem::FnDef(it) => { + (DefKind::Function(self.source_ast_id_map.ast_id(&it)), it.name()) + } + ast::ModuleItem::TraitDef(it) => { + (DefKind::Trait(self.source_ast_id_map.ast_id(&it)), it.name()) + } + ast::ModuleItem::TypeAliasDef(it) => { + (DefKind::TypeAlias(self.source_ast_id_map.ast_id(&it)), it.name()) + } + ast::ModuleItem::ConstDef(it) => { + (DefKind::Const(self.source_ast_id_map.ast_id(&it)), it.name()) + } + ast::ModuleItem::StaticDef(it) => { + (DefKind::Static(self.source_ast_id_map.ast_id(&it)), it.name()) + } + }; + if let Some(name) = name { + let name = name.as_name(); + let def = self.raw_items.defs.alloc(DefData { name, kind }); + self.push_item(current_module, attrs, RawItemKind::Def(def)); + } + } + + fn add_module(&mut self, current_module: Option, module: ast::Module) { + let name = match module.name() { + Some(it) => it.as_name(), + None => return, + }; + let attrs = self.parse_attrs(&module); + + let ast_id = self.source_ast_id_map.ast_id(&module); + if module.has_semi() { + let item = self.raw_items.modules.alloc(ModuleData::Declaration { name, ast_id }); + self.push_item(current_module, attrs, RawItemKind::Module(item)); + return; + } + + if let Some(item_list) = module.item_list() { + let item = self.raw_items.modules.alloc(ModuleData::Definition { + name, + ast_id, + items: Vec::new(), + }); + self.process_module(Some(item), item_list); + self.push_item(current_module, attrs, RawItemKind::Module(item)); + return; + } + tested_by!(name_res_works_for_broken_modules); + } + + fn add_use_item(&mut self, current_module: Option, use_item: ast::UseItem) { + // FIXME: cfg_attr + let is_prelude = use_item.has_atom_attr("prelude_import"); + let attrs = self.parse_attrs(&use_item); + + Path::expand_use_item( + Source { ast: use_item, file_id: self.file_id }, + self.db, + |path, use_tree, is_glob, alias| { + let import_data = ImportData { + path, + alias, + is_glob, + is_prelude, + is_extern_crate: false, + is_macro_use: false, + }; + self.push_import( + current_module, + attrs.clone(), + import_data, + Either::A(AstPtr::new(use_tree)), + ); + }, + ) + } + + fn add_extern_crate_item( + &mut self, + current_module: Option, + extern_crate: ast::ExternCrateItem, + ) { + if let Some(name_ref) = extern_crate.name_ref() { + let path = Path::from_name_ref(&name_ref); + let alias = extern_crate.alias().and_then(|a| a.name()).map(|it| it.as_name()); + let attrs = self.parse_attrs(&extern_crate); + // FIXME: cfg_attr + let is_macro_use = extern_crate.has_atom_attr("macro_use"); + let import_data = ImportData { + path, + alias, + is_glob: false, + is_prelude: false, + is_extern_crate: true, + is_macro_use, + }; + self.push_import( + current_module, + attrs, + import_data, + Either::B(AstPtr::new(&extern_crate)), + ); + } + } + + fn add_macro(&mut self, current_module: Option, m: ast::MacroCall) { + let attrs = self.parse_attrs(&m); + let path = match m + .path() + .and_then(|path| Path::from_src(Source { ast: path, file_id: self.file_id }, self.db)) + { + Some(it) => it, + _ => return, + }; + + let name = m.name().map(|it| it.as_name()); + let ast_id = self.source_ast_id_map.ast_id(&m); + // FIXME: cfg_attr + let export = m.attrs().filter_map(|x| x.simple_name()).any(|name| name == "macro_export"); + + let m = self.raw_items.macros.alloc(MacroData { ast_id, path, name, export }); + self.push_item(current_module, attrs, RawItemKind::Macro(m)); + } + + fn push_import( + &mut self, + current_module: Option, + attrs: Attrs, + data: ImportData, + source: ImportSourcePtr, + ) { + let import = self.raw_items.imports.alloc(data); + self.source_map.insert(import, source); + self.push_item(current_module, attrs, RawItemKind::Import(import)) + } + + fn push_item(&mut self, current_module: Option, attrs: Attrs, kind: RawItemKind) { + match current_module { + Some(module) => match &mut self.raw_items.modules[module] { + ModuleData::Definition { items, .. } => items, + ModuleData::Declaration { .. } => unreachable!(), + }, + None => &mut self.raw_items.items, + } + .push(RawItem { attrs, kind }) + } + + fn parse_attrs(&self, item: &impl ast::AttrsOwner) -> Attrs { + Attr::from_attrs_owner(self.file_id, item, self.db) + } +} diff --git a/crates/ra_hir_def/src/path.rs b/crates/ra_hir_def/src/path.rs new file mode 100644 index 000000000..fe060437d --- /dev/null +++ b/crates/ra_hir_def/src/path.rs @@ -0,0 +1,423 @@ +//! FIXME: write short doc here + +use std::{iter, sync::Arc}; + +use hir_expand::db::AstDatabase; +use ra_db::CrateId; +use ra_syntax::{ + ast::{self, NameOwner, TypeAscriptionOwner}, + AstNode, +}; + +use crate::{ + name::{self, AsName, Name}, + type_ref::TypeRef, + Source, +}; + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Path { + pub kind: PathKind, + pub segments: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct PathSegment { + pub name: Name, + pub args_and_bindings: Option>, +} + +/// Generic arguments to a path segment (e.g. the `i32` in `Option`). This +/// can (in the future) also include bindings of associated types, like in +/// `Iterator`. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct GenericArgs { + pub args: Vec, + /// This specifies whether the args contain a Self type as the first + /// element. This is the case for path segments like ``, where + /// `T` is actually a type parameter for the path `Trait` specifying the + /// Self type. Otherwise, when we have a path `Trait`, the Self type + /// is left out. + pub has_self_type: bool, + /// Associated type bindings like in `Iterator`. + pub bindings: Vec<(Name, TypeRef)>, +} + +/// A single generic argument. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum GenericArg { + Type(TypeRef), + // or lifetime... +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum PathKind { + Plain, + Self_, + Super, + Crate, + // Absolute path + Abs, + // Type based path like `::foo` + Type(Box), + // `$crate` from macro expansion + DollarCrate(CrateId), +} + +impl Path { + /// Calls `cb` with all paths, represented by this use item. + pub fn expand_use_item( + item_src: Source, + db: &impl AstDatabase, + mut cb: impl FnMut(Path, &ast::UseTree, bool, Option), + ) { + if let Some(tree) = item_src.ast.use_tree() { + expand_use_tree(None, tree, &|| item_src.file_id.macro_crate(db), &mut cb); + } + } + + pub fn from_simple_segments(kind: PathKind, segments: impl IntoIterator) -> Path { + Path { + kind, + segments: segments + .into_iter() + .map(|name| PathSegment { name, args_and_bindings: None }) + .collect(), + } + } + + /// Converts an `ast::Path` to `Path`. Works with use trees. + /// DEPRECATED: It does not handle `$crate` from macro call. + pub fn from_ast(path: ast::Path) -> Option { + Path::parse(path, &|| None) + } + + /// Converts an `ast::Path` to `Path`. Works with use trees. + /// It correctly handles `$crate` based path from macro call. + pub fn from_src(source: Source, db: &impl AstDatabase) -> Option { + let file_id = source.file_id; + Path::parse(source.ast, &|| file_id.macro_crate(db)) + } + + fn parse(mut path: ast::Path, macro_crate: &impl Fn() -> Option) -> Option { + let mut kind = PathKind::Plain; + let mut segments = Vec::new(); + loop { + let segment = path.segment()?; + + if segment.has_colon_colon() { + kind = PathKind::Abs; + } + + match segment.kind()? { + ast::PathSegmentKind::Name(name) => { + if name.text() == "$crate" { + if let Some(macro_crate) = macro_crate() { + kind = PathKind::DollarCrate(macro_crate); + break; + } + } + + let args = segment + .type_arg_list() + .and_then(GenericArgs::from_ast) + .or_else(|| { + GenericArgs::from_fn_like_path_ast( + segment.param_list(), + segment.ret_type(), + ) + }) + .map(Arc::new); + let segment = PathSegment { name: name.as_name(), args_and_bindings: args }; + segments.push(segment); + } + ast::PathSegmentKind::Type { type_ref, trait_ref } => { + assert!(path.qualifier().is_none()); // this can only occur at the first segment + + let self_type = TypeRef::from_ast(type_ref?); + + match trait_ref { + // ::foo + None => { + kind = PathKind::Type(Box::new(self_type)); + } + // >::Foo desugars to Trait::Foo + Some(trait_ref) => { + let path = Path::parse(trait_ref.path()?, macro_crate)?; + kind = path.kind; + let mut prefix_segments = path.segments; + prefix_segments.reverse(); + segments.extend(prefix_segments); + // Insert the type reference (T in the above example) as Self parameter for the trait + let mut last_segment = segments.last_mut()?; + if last_segment.args_and_bindings.is_none() { + last_segment.args_and_bindings = + Some(Arc::new(GenericArgs::empty())); + }; + let args = last_segment.args_and_bindings.as_mut().unwrap(); + let mut args_inner = Arc::make_mut(args); + args_inner.has_self_type = true; + args_inner.args.insert(0, GenericArg::Type(self_type)); + } + } + } + ast::PathSegmentKind::CrateKw => { + kind = PathKind::Crate; + break; + } + ast::PathSegmentKind::SelfKw => { + kind = PathKind::Self_; + break; + } + ast::PathSegmentKind::SuperKw => { + kind = PathKind::Super; + break; + } + } + path = match qualifier(&path) { + Some(it) => it, + None => break, + }; + } + segments.reverse(); + return Some(Path { kind, segments }); + + fn qualifier(path: &ast::Path) -> Option { + if let Some(q) = path.qualifier() { + return Some(q); + } + // FIXME: this bottom up traversal is not too precise. + // Should we handle do a top-down analysis, recording results? + let use_tree_list = path.syntax().ancestors().find_map(ast::UseTreeList::cast)?; + let use_tree = use_tree_list.parent_use_tree(); + use_tree.path() + } + } + + /// Converts an `ast::NameRef` into a single-identifier `Path`. + pub fn from_name_ref(name_ref: &ast::NameRef) -> Path { + name_ref.as_name().into() + } + + /// `true` is this path is a single identifier, like `foo` + pub fn is_ident(&self) -> bool { + self.kind == PathKind::Plain && self.segments.len() == 1 + } + + /// `true` if this path is just a standalone `self` + pub fn is_self(&self) -> bool { + self.kind == PathKind::Self_ && self.segments.is_empty() + } + + /// If this path is a single identifier, like `foo`, return its name. + pub fn as_ident(&self) -> Option<&Name> { + if self.kind != PathKind::Plain || self.segments.len() > 1 { + return None; + } + self.segments.first().map(|s| &s.name) + } + + pub fn expand_macro_expr(&self) -> Option { + self.as_ident().and_then(|name| Some(name.clone())) + } + + pub fn is_type_relative(&self) -> bool { + match self.kind { + PathKind::Type(_) => true, + _ => false, + } + } +} + +impl GenericArgs { + pub fn from_ast(node: ast::TypeArgList) -> Option { + let mut args = Vec::new(); + for type_arg in node.type_args() { + let type_ref = TypeRef::from_ast_opt(type_arg.type_ref()); + args.push(GenericArg::Type(type_ref)); + } + // lifetimes ignored for now + let mut bindings = Vec::new(); + for assoc_type_arg in node.assoc_type_args() { + if let Some(name_ref) = assoc_type_arg.name_ref() { + let name = name_ref.as_name(); + let type_ref = TypeRef::from_ast_opt(assoc_type_arg.type_ref()); + bindings.push((name, type_ref)); + } + } + if args.is_empty() && bindings.is_empty() { + None + } else { + Some(GenericArgs { args, has_self_type: false, bindings }) + } + } + + /// Collect `GenericArgs` from the parts of a fn-like path, i.e. `Fn(X, Y) + /// -> Z` (which desugars to `Fn<(X, Y), Output=Z>`). + pub(crate) fn from_fn_like_path_ast( + params: Option, + ret_type: Option, + ) -> Option { + let mut args = Vec::new(); + let mut bindings = Vec::new(); + if let Some(params) = params { + let mut param_types = Vec::new(); + for param in params.params() { + let type_ref = TypeRef::from_ast_opt(param.ascribed_type()); + param_types.push(type_ref); + } + let arg = GenericArg::Type(TypeRef::Tuple(param_types)); + args.push(arg); + } + if let Some(ret_type) = ret_type { + let type_ref = TypeRef::from_ast_opt(ret_type.type_ref()); + bindings.push((name::OUTPUT_TYPE, type_ref)) + } + if args.is_empty() && bindings.is_empty() { + None + } else { + Some(GenericArgs { args, has_self_type: false, bindings }) + } + } + + pub(crate) fn empty() -> GenericArgs { + GenericArgs { args: Vec::new(), has_self_type: false, bindings: Vec::new() } + } +} + +impl From for Path { + fn from(name: Name) -> Path { + Path::from_simple_segments(PathKind::Plain, iter::once(name)) + } +} + +fn expand_use_tree( + prefix: Option, + tree: ast::UseTree, + macro_crate: &impl Fn() -> Option, + cb: &mut impl FnMut(Path, &ast::UseTree, bool, Option), +) { + if let Some(use_tree_list) = tree.use_tree_list() { + let prefix = match tree.path() { + // E.g. use something::{{{inner}}}; + None => prefix, + // 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 convert_path(prefix, path, macro_crate) { + Some(it) => Some(it), + None => return, // FIXME: report errors somewhere + }, + }; + for child_tree in use_tree_list.use_trees() { + expand_use_tree(prefix.clone(), child_tree, macro_crate, cb); + } + } else { + let alias = tree.alias().and_then(|a| a.name()).map(|a| a.as_name()); + if let Some(ast_path) = tree.path() { + // Handle self in a path. + // E.g. `use something::{self, <...>}` + if ast_path.qualifier().is_none() { + if let Some(segment) = ast_path.segment() { + if segment.kind() == Some(ast::PathSegmentKind::SelfKw) { + if let Some(prefix) = prefix { + cb(prefix, &tree, false, alias); + return; + } + } + } + } + if let Some(path) = convert_path(prefix, ast_path, macro_crate) { + let is_glob = tree.has_star(); + cb(path, &tree, is_glob, alias) + } + // FIXME: report errors somewhere + // We get here if we do + } + } +} + +fn convert_path( + prefix: Option, + path: ast::Path, + macro_crate: &impl Fn() -> Option, +) -> Option { + let prefix = if let Some(qual) = path.qualifier() { + Some(convert_path(prefix, qual, macro_crate)?) + } else { + prefix + }; + + let segment = path.segment()?; + let res = match segment.kind()? { + ast::PathSegmentKind::Name(name) => { + if name.text() == "$crate" { + if let Some(krate) = macro_crate() { + return Some(Path::from_simple_segments( + PathKind::DollarCrate(krate), + iter::empty(), + )); + } + } + + // no type args in use + let mut res = prefix + .unwrap_or_else(|| Path { kind: PathKind::Plain, segments: Vec::with_capacity(1) }); + res.segments.push(PathSegment { + name: name.as_name(), + args_and_bindings: None, // no type args in use + }); + res + } + ast::PathSegmentKind::CrateKw => { + if prefix.is_some() { + return None; + } + Path::from_simple_segments(PathKind::Crate, iter::empty()) + } + ast::PathSegmentKind::SelfKw => { + if prefix.is_some() { + return None; + } + Path::from_simple_segments(PathKind::Self_, iter::empty()) + } + ast::PathSegmentKind::SuperKw => { + if prefix.is_some() { + return None; + } + Path::from_simple_segments(PathKind::Super, iter::empty()) + } + ast::PathSegmentKind::Type { .. } => { + // not allowed in imports + return None; + } + }; + Some(res) +} + +pub mod known { + use super::{Path, PathKind}; + use crate::name; + + pub fn std_iter_into_iterator() -> Path { + Path::from_simple_segments( + PathKind::Abs, + vec![name::STD, name::ITER, name::INTO_ITERATOR_TYPE], + ) + } + + pub fn std_ops_try() -> Path { + Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::OPS, name::TRY_TYPE]) + } + + pub fn std_result_result() -> Path { + Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::RESULT, name::RESULT_TYPE]) + } + + pub fn std_future_future() -> Path { + Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::FUTURE, name::FUTURE_TYPE]) + } + + pub fn std_boxed_box() -> Path { + Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::BOXED, name::BOX_TYPE]) + } +} diff --git a/crates/ra_hir_def/src/type_ref.rs b/crates/ra_hir_def/src/type_ref.rs new file mode 100644 index 000000000..8af061116 --- /dev/null +++ b/crates/ra_hir_def/src/type_ref.rs @@ -0,0 +1,162 @@ +//! HIR for references to types. Paths in these are not yet resolved. They can +//! be directly created from an ast::TypeRef, without further queries. + +use ra_syntax::ast::{self, TypeAscriptionOwner, TypeBoundsOwner}; + +use crate::path::Path; + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub enum Mutability { + Shared, + Mut, +} + +impl Mutability { + pub fn from_mutable(mutable: bool) -> Mutability { + if mutable { + Mutability::Mut + } else { + Mutability::Shared + } + } + + pub fn as_keyword_for_ref(self) -> &'static str { + match self { + Mutability::Shared => "", + Mutability::Mut => "mut ", + } + } + + pub fn as_keyword_for_ptr(self) -> &'static str { + match self { + Mutability::Shared => "const ", + Mutability::Mut => "mut ", + } + } +} + +/// Compare ty::Ty +#[derive(Clone, PartialEq, Eq, Hash, Debug)] +pub enum TypeRef { + Never, + Placeholder, + Tuple(Vec), + Path(Path), + RawPtr(Box, Mutability), + Reference(Box, Mutability), + Array(Box /*, Expr*/), + Slice(Box), + /// A fn pointer. Last element of the vector is the return type. + Fn(Vec), + // For + ImplTrait(Vec), + DynTrait(Vec), + Error, +} + +#[derive(Clone, PartialEq, Eq, Hash, Debug)] +pub enum TypeBound { + Path(Path), + // also for<> bounds + // also Lifetimes + Error, +} + +impl TypeRef { + /// Converts an `ast::TypeRef` to a `hir::TypeRef`. + pub fn from_ast(node: ast::TypeRef) -> Self { + match node { + ast::TypeRef::ParenType(inner) => TypeRef::from_ast_opt(inner.type_ref()), + ast::TypeRef::TupleType(inner) => { + TypeRef::Tuple(inner.fields().map(TypeRef::from_ast).collect()) + } + ast::TypeRef::NeverType(..) => TypeRef::Never, + ast::TypeRef::PathType(inner) => { + // FIXME: Use `Path::from_src` + inner.path().and_then(Path::from_ast).map(TypeRef::Path).unwrap_or(TypeRef::Error) + } + ast::TypeRef::PointerType(inner) => { + let inner_ty = TypeRef::from_ast_opt(inner.type_ref()); + let mutability = Mutability::from_mutable(inner.is_mut()); + TypeRef::RawPtr(Box::new(inner_ty), mutability) + } + ast::TypeRef::ArrayType(inner) => { + TypeRef::Array(Box::new(TypeRef::from_ast_opt(inner.type_ref()))) + } + ast::TypeRef::SliceType(inner) => { + TypeRef::Slice(Box::new(TypeRef::from_ast_opt(inner.type_ref()))) + } + ast::TypeRef::ReferenceType(inner) => { + let inner_ty = TypeRef::from_ast_opt(inner.type_ref()); + let mutability = Mutability::from_mutable(inner.is_mut()); + TypeRef::Reference(Box::new(inner_ty), mutability) + } + ast::TypeRef::PlaceholderType(_inner) => TypeRef::Placeholder, + ast::TypeRef::FnPointerType(inner) => { + let ret_ty = TypeRef::from_ast_opt(inner.ret_type().and_then(|rt| rt.type_ref())); + let mut params = if let Some(pl) = inner.param_list() { + pl.params().map(|p| p.ascribed_type()).map(TypeRef::from_ast_opt).collect() + } else { + Vec::new() + }; + params.push(ret_ty); + TypeRef::Fn(params) + } + // for types are close enough for our purposes to the inner type for now... + ast::TypeRef::ForType(inner) => TypeRef::from_ast_opt(inner.type_ref()), + ast::TypeRef::ImplTraitType(inner) => { + TypeRef::ImplTrait(type_bounds_from_ast(inner.type_bound_list())) + } + ast::TypeRef::DynTraitType(inner) => { + TypeRef::DynTrait(type_bounds_from_ast(inner.type_bound_list())) + } + } + } + + pub fn from_ast_opt(node: Option) -> Self { + if let Some(node) = node { + TypeRef::from_ast(node) + } else { + TypeRef::Error + } + } + + pub fn unit() -> TypeRef { + TypeRef::Tuple(Vec::new()) + } +} + +pub(crate) fn type_bounds_from_ast(type_bounds_opt: Option) -> Vec { + if let Some(type_bounds) = type_bounds_opt { + type_bounds.bounds().map(TypeBound::from_ast).collect() + } else { + vec![] + } +} + +impl TypeBound { + pub fn from_ast(node: ast::TypeBound) -> Self { + match node.kind() { + ast::TypeBoundKind::PathType(path_type) => { + let path = match path_type.path() { + Some(p) => p, + None => return TypeBound::Error, + }; + // FIXME: Use `Path::from_src` + let path = match Path::from_ast(path) { + Some(p) => p, + None => return TypeBound::Error, + }; + TypeBound::Path(path) + } + ast::TypeBoundKind::ForType(_) | ast::TypeBoundKind::Lifetime(_) => TypeBound::Error, + } + } + + pub fn as_path(&self) -> Option<&Path> { + match self { + TypeBound::Path(p) => Some(p), + _ => None, + } + } +} -- cgit v1.2.3 From c1ed9ccc4ed7dfff3abb6eb01d7c311c8e31108c Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 30 Oct 2019 17:40:13 +0300 Subject: fix compilation --- crates/ra_hir_def/src/nameres.rs | 2 ++ crates/ra_hir_def/src/nameres/raw.rs | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'crates/ra_hir_def/src') diff --git a/crates/ra_hir_def/src/nameres.rs b/crates/ra_hir_def/src/nameres.rs index 1a3f7667d..5893708e8 100644 --- a/crates/ra_hir_def/src/nameres.rs +++ b/crates/ra_hir_def/src/nameres.rs @@ -1 +1,3 @@ +//! FIXME: write short doc here + pub mod raw; diff --git a/crates/ra_hir_def/src/nameres/raw.rs b/crates/ra_hir_def/src/nameres/raw.rs index 13b9fbf48..86b4fef96 100644 --- a/crates/ra_hir_def/src/nameres/raw.rs +++ b/crates/ra_hir_def/src/nameres/raw.rs @@ -8,7 +8,6 @@ use ra_syntax::{ ast::{self, AttrsOwner, NameOwner}, AstNode, AstPtr, SourceFile, }; -use test_utils::tested_by; use crate::{ attr::Attr, @@ -301,7 +300,8 @@ impl RawItemsCollector<&DB> { self.push_item(current_module, attrs, RawItemKind::Module(item)); return; } - tested_by!(name_res_works_for_broken_modules); + // FIXME: restore this mark once we complete hir splitting + // tested_by!(name_res_works_for_broken_modules); } fn add_use_item(&mut self, current_module: Option, use_item: ast::UseItem) { -- cgit v1.2.3