Repository: rust-analyzer/rust-analyzer
Pull request #4990 (2020-06-22 17:29:10)

Introduce an ItemTree layer to avoid reparsing files
This reduces the latency of "go to definition" in a simple benchmark on rust-analyzer by around 30%.

cc https://github.com/rust-analyzer/rust-analyzer/issues/1650
Closes https://github.com/rust-analyzer/rust-analyzer/issues/3485


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 7bd2144a74..c2d00adebc 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1018,6 +1018,7 @@ dependencies = [
  "ra_syntax",
  "ra_tt",
  "rustc-hash",
+ "smallvec",
  "stdx",
  "test_utils",
 ]
diff --git a/crates/ra_arena/src/lib.rs b/crates/ra_arena/src/lib.rs
index 441fbb3cbe..3169aa5b8c 100644
--- a/crates/ra_arena/src/lib.rs
+++ b/crates/ra_arena/src/lib.rs
@@ -116,6 +116,9 @@ impl<T> Arena<T> {
     ) -> impl Iterator<Item = (Idx<T>, &T)> + ExactSizeIterator + DoubleEndedIterator {
         self.data.iter().enumerate().map(|(idx, value)| (Idx::from_raw(RawId(idx as u32)), value))
     }
+    pub fn shrink_to_fit(&mut self) {
+        self.data.shrink_to_fit();
+    }
 }
 
 impl<T> Default for Arena<T> {
diff --git a/crates/ra_db/src/lib.rs b/crates/ra_db/src/lib.rs
index 875290259b..4a3ba57dab 100644
--- a/crates/ra_db/src/lib.rs
+++ b/crates/ra_db/src/lib.rs
@@ -114,7 +114,7 @@ pub trait SourceDatabase: CheckCanceled + FileLoader + std::fmt::Debug {
 }
 
 fn parse_query(db: &impl SourceDatabase, file_id: FileId) -> Parse<ast::SourceFile> {
-    let _p = profile("parse_query");
+    let _p = profile("parse_query").detail(|| format!("{:?}", file_id));
     let text = db.file_text(file_id);
     SourceFile::parse(&*text)
 }
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index ffd5278ecb..a379b9f49b 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -31,10 +31,7 @@ use hir_ty::{
 };
 use ra_db::{CrateId, CrateName, Edition, FileId};
 use ra_prof::profile;
-use ra_syntax::{
-    ast::{self, AttrsOwner, NameOwner},
-    AstNode,
-};
+use ra_syntax::ast::{self, AttrsOwner, NameOwner};
 use rustc_hash::FxHashSet;
 
 use crate::{
@@ -205,7 +202,8 @@ impl ModuleDef {
 }
 
 pub use hir_def::{
-    attr::Attrs, item_scope::ItemInNs, visibility::Visibility, AssocItemId, AssocItemLoc,
+    attr::Attrs, item_scope::ItemInNs, item_tree::ItemTreeNode, visibility::Visibility,
+    AssocItemId, AssocItemLoc,
 };
 
 impl Module {
@@ -872,7 +870,7 @@ where
     ID: Lookup<Data = AssocItemLoc<AST>>,
     DEF: From<ID>,
     CTOR: FnOnce(DEF) -> AssocItem,
-    AST: AstNode,
+    AST: ItemTreeNode,
 {
     match id.lookup(db.upcast()).container {
         AssocContainerId::TraitId(_) | AssocContainerId::ImplId(_) => Some(ctor(DEF::from(id))),
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs
index b25dac28ec..bb67952de2 100644
--- a/crates/ra_hir/src/db.rs
+++ b/crates/ra_hir/src/db.rs
@@ -6,7 +6,7 @@ pub use hir_def::db::{
     ExprScopesQuery, FunctionDataQuery, GenericParamsQuery, ImplDataQuery, ImportMapQuery,
     InternConstQuery, InternDatabase, InternDatabaseStorage, InternEnumQuery, InternFunctionQuery,
     InternImplQuery, InternStaticQuery, InternStructQuery, InternTraitQuery, InternTypeAliasQuery,
-    InternUnionQuery, LangItemQuery, ModuleLangItemsQuery, RawItemsQuery, StaticDataQuery,
+    InternUnionQuery, ItemTreeQuery, LangItemQuery, ModuleLangItemsQuery, StaticDataQuery,
     StructDataQuery, TraitDataQuery, TypeAliasDataQuery, UnionDataQuery,
 };
 pub use hir_expand::db::{
diff --git a/crates/ra_hir_def/Cargo.toml b/crates/ra_hir_def/Cargo.toml
index ef1f65ee06..6d43924e30 100644
--- a/crates/ra_hir_def/Cargo.toml
+++ b/crates/ra_hir_def/Cargo.toml
@@ -17,6 +17,7 @@ drop_bomb = "0.1.4"
 fst = { version = "0.4", default-features = false }
 itertools = "0.9.0"
 indexmap = "1.4.0"
+smallvec = "1.4.0"
 
 stdx = { path = "../stdx" }
 
diff --git a/crates/ra_hir_def/src/attr.rs b/crates/ra_hir_def/src/attr.rs
index 2eeba05729..197737ffce 100644
--- a/crates/ra_hir_def/src/attr.rs
+++ b/crates/ra_hir_def/src/attr.rs
@@ -13,7 +13,11 @@ use ra_syntax::{
 use tt::Subtree;
 
 use crate::{
-    db::DefDatabase, nameres::ModuleSource, path::ModPath, src::HasChildSource, src::HasSource,
+    db::DefDatabase,
+    item_tree::{ItemTreeId, ItemTreeNode},
+    nameres::ModuleSource,
+    path::ModPath,
+    src::HasChildSource,
     AdtId, AttrDefId, Lookup,
 };
 
@@ -34,6 +38,8 @@ impl ops::Deref for Attrs {
 }
 
 impl Attrs {
+    pub const EMPTY: Attrs = Attrs { entries: None };
+
     pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Attrs {
         match def {
             AttrDefId::ModuleId(module) => {
@@ -65,19 +71,19 @@ impl Attrs {
                 Attrs::from_attrs_owner(db, src.map(|it| it as &dyn AttrsOwner))
             }
             AttrDefId::AdtId(it) => match it {
-                AdtId::StructId(it) => attrs_from_loc(it.lookup(db), db),
-                AdtId::EnumId(it) => attrs_from_loc(it.lookup(db), db),
-                AdtId::UnionId(it) => attrs_from_loc(it.lookup(db), db),
+                AdtId::StructId(it) => attrs_from_item_tree(it.lookup(db).id, db),
+                AdtId::EnumId(it) => attrs_from_item_tree(it.lookup(db).id, db),
+                AdtId::UnionId(it) => attrs_from_item_tree(it.lookup(db).id, db),
             },
-            AttrDefId::TraitId(it) => attrs_from_loc(it.lookup(db), db),
+            AttrDefId::TraitId(it) => attrs_from_item_tree(it.lookup(db).id, db),
             AttrDefId::MacroDefId(it) => {
                 it.ast_id.map_or_else(Default::default, |ast_id| attrs_from_ast(ast_id, db))
             }
-            AttrDefId::ImplId(it) => attrs_from_loc(it.lookup(db), db),
-            AttrDefId::ConstId(it) => attrs_from_loc(it.lookup(db), db),
-            AttrDefId::StaticId(it) => attrs_from_loc(it.lookup(db), db),
-            AttrDefId::FunctionId(it) => attrs_from_loc(it.lookup(db), db),
-            AttrDefId::TypeAliasId(it) => attrs_from_loc(it.lookup(db), db),
+            AttrDefId::ImplId(it) => attrs_from_item_tree(it.lookup(db).id, db),
+            AttrDefId::ConstId(it) => attrs_from_item_tree(it.lookup(db).id, db),
+            AttrDefId::StaticId(it) => attrs_from_item_tree(it.lookup(db).id, db),
+            AttrDefId::FunctionId(it) => attrs_from_item_tree(it.lookup(db).id, db),
+            AttrDefId::TypeAliasId(it) => attrs_from_item_tree(it.lookup(db).id, db),
         }
     }
 
@@ -103,6 +109,18 @@ impl Attrs {
         Attrs { entries }
     }
 
+    pub fn merge(&self, other: Attrs) -> Attrs {
+        match (&self.entries, &other.entries) {
+            (None, None) => Attrs { entries: None },
+            (Some(entries), None) | (None, Some(entries)) => {
+                Attrs { entries: Some(entries.clone()) }
+            }
+            (Some(a), Some(b)) => {
+                Attrs { entries: Some(a.iter().chain(b.iter()).cloned().collect()) }
+            }
+        }
+    }
+
     pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> {
         AttrQuery { attrs: self, key }
     }
@@ -187,11 +205,8 @@ where
     Attrs::from_attrs_owner(db, src.as_ref().map(|it| it as &dyn AttrsOwner))
 }
 
-fn attrs_from_loc<T>(node: T, db: &dyn DefDatabase) -> Attrs
-where
-    T: HasSource,
-    T::Value: ast::AttrsOwner,
-{
-    let src = node.source(db);
-    Attrs::from_attrs_owner(db, src.as_ref().map(|it| it as &dyn AttrsOwner))
+fn attrs_from_item_tree<N: ItemTreeNode>(id: ItemTreeId<N>, db: &dyn DefDatabase) -> Attrs {
+    let tree = db.item_tree(id.file_id);
+    let mod_item = N::id_to_mod_item(id.value);
+    tree.attrs(mod_item).clone()
 }
diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs
index f159f80af0..3ced648e56 100644
--- a/crates/ra_hir_def/src/body/lower.rs
+++ b/crates/ra_hir_def/src/body/lower.rs
@@ -5,7 +5,7 @@ use either::Either;
 use hir_expand::{
     hygiene::Hygiene,
     name::{name, AsName, Name},
-    HirFileId, MacroDefId, MacroDefKind,
+    AstId, HirFileId, MacroDefId, MacroDefKind,
 };
 use ra_arena::Arena;
 use ra_syntax::{
@@ -27,6 +27,7 @@ use crate::{
         LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement,
     },
     item_scope::BuiltinShadowMode,
+    item_tree::{FileItemTreeId, ItemTree, ItemTreeNode},
     path::{GenericArgs, Path},
     type_ref::{Mutability, Rawness, TypeRef},
     AdtId, ConstLoc, ContainerId, DefWithBodyId, EnumLoc, FunctionLoc, Intern, ModuleDefId,
@@ -35,6 +36,8 @@ use crate::{
 
 use super::{ExprSource, PatSource};
 use ast::AstChildren;
+use rustc_hash::FxHashMap;
+use std::sync::Arc;
 
 pub(crate) struct LowerCtx {
     hygiene: Hygiene,
@@ -60,10 +63,10 @@ pub(super) fn lower(
     params: Option<ast::ParamList>,
     body: Option<ast::Expr>,
 ) -> (Body, BodySourceMap) {
+    let item_tree = db.item_tree(expander.current_file_id);
     ExprCollector {
         db,
         def,
-        expander,
         source_map: BodySourceMap::default(),
         body: Body {
             exprs: Arena::default(),
@@ -72,6 +75,12 @@ pub(super) fn lower(
             body_expr: dummy_expr_id(),
             item_scope: Default::default(),
         },
+        item_trees: {
+            let mut map = FxHashMap::default();
+            map.insert(expander.current_file_id, item_tree);
+            map
+        },
+        expander,
     }
     .collect(params, body)
 }
@@ -82,6 +91,8 @@ struct ExprCollector<'a> {
     expander: Expander,
     body: Body,
     source_map: BodySourceMap,
+
+    item_trees: FxHashMap<HirFileId, Arc<ItemTree>>,
 }
 
 impl ExprCollector<'_> {
@@ -533,6 +544,9 @@ impl ExprCollector<'_> {
                             self.source_map
                                 .expansions
                                 .insert(macro_call, self.expander.current_file_id);
+
+                            let item_tree = self.db.item_tree(self.expander.current_file_id);
+                            self.item_trees.insert(self.expander.current_file_id, item_tree);
                             let id = self.collect_expr(expansion);
                             self.expander.exit(self.db, mark);
                             id
@@ -547,6 +561,19 @@ impl ExprCollector<'_> {
         }
     }
 
+    fn find_inner_item<S: ItemTreeNode>(&self, id: AstId<ast::ModuleItem>) -> FileItemTreeId<S> {
+        let tree = &self.item_trees[&id.file_id];
+
+        // FIXME: This probably breaks with `use` items, since they produce multiple item tree nodes
+
+        // Root file (non-macro).
+        tree.all_inner_items()
+            .chain(tree.top_level_items().iter().copied())
+            .filter_map(|mod_item| mod_item.downcast::<S>())
+            .find(|tree_id| tree[*tree_id].ast_id().upcast() == id.value)
+            .unwrap_or_else(|| panic!("couldn't find inner item for {:?}", id))
+    }
+
     fn collect_expr_opt(&mut self, expr: Option<ast::Expr>) -> ExprId {
         if let Some(expr) = expr {
             self.collect_expr(expr)
@@ -578,56 +605,102 @@ impl ExprCollector<'_> {
 
     fn collect_block_items(&mut self, block: &ast::BlockExpr) {
         let container = ContainerId::DefWithBodyId(self.def);
-        for item in block.items() {
-            let (def, name): (ModuleDefId, Option<ast::Name>) = match item {
-                ast::ModuleItem::FnDef(def) => {
-                    let ast_id = self.expander.ast_id(&def);
-                    (
-                        FunctionLoc { container: container.into(), ast_id }.intern(self.db).into(),
-                        def.name(),
-                    )
-                }
-                ast::ModuleItem::TypeAliasDef(def) => {
-                    let ast_id = self.expander.ast_id(&def);
-                    (
-                        TypeAliasLoc { container: container.into(), ast_id }.intern(self.db).into(),
-                        def.name(),
-                    )
-                }
-                ast::ModuleItem::ConstDef(def) => {
-                    let ast_id = self.expander.ast_id(&def);
-                    (
-                        ConstLoc { container: container.into(), ast_id }.intern(self.db).into(),
-                        def.name(),
-                    )
-                }
-                ast::ModuleItem::StaticDef(def) => {
-                    let ast_id = self.expander.ast_id(&def);
-                    (StaticLoc { container, ast_id }.intern(self.db).into(), def.name())
-                }
-                ast::ModuleItem::StructDef(def) => {
-                    let ast_id = self.expander.ast_id(&def);
-                    (StructLoc { container, ast_id }.intern(self.db).into(), def.name())
-                }
-                ast::ModuleItem::EnumDef(def) => {
-                    let ast_id = self.expander.ast_id(&def);
-                    (EnumLoc { container, ast_id }.intern(self.db).into(), def.name())
-                }
-                ast::ModuleItem::UnionDef(def) => {
-                    let ast_id = self.expander.ast_id(&def);
-                    (UnionLoc { container, ast_id }.intern(self.db).into(), def.name())
-                }
-                ast::ModuleItem::TraitDef(def) => {
-                    let ast_id = self.expander.ast_id(&def);
-                    (TraitLoc { container, ast_id }.intern(self.db).into(), def.name())
-                }
-                ast::ModuleItem::ExternBlock(_) => continue, // FIXME: collect from extern blocks
-                ast::ModuleItem::ImplDef(_)
-                | ast::ModuleItem::UseItem(_)
-                | ast::ModuleItem::ExternCrateItem(_)
-                | ast::ModuleItem::Module(_)
-                | ast::ModuleItem::MacroCall(_) => continue,
-            };
+
+        let items = block
+            .items()
+            .filter_map(|item| {
+                let (def, name): (ModuleDefId, Option<ast::Name>) = match item {
+                    ast::ModuleItem::FnDef(def) => {
+                        let ast_id = self.expander.ast_id(&def);
+                        let id = self.find_inner_item(ast_id.map(|id| id.upcast()));
+                        (
+                            FunctionLoc { container: container.into(), id: ast_id.with_value(id) }
+                                .intern(self.db)
+                                .into(),
+                            def.name(),
+                        )
+                    }
+                    ast::ModuleItem::TypeAliasDef(def) => {
+                        let ast_id = self.expander.ast_id(&def);
+                        let id = self.find_inner_item(ast_id.map(|id| id.upcast()));
+                        (
+                            TypeAliasLoc { container: container.into(), id: ast_id.with_value(id) }
+                                .intern(self.db)
+                                .into(),
+                            def.name(),
+                        )
+                    }
+                    ast::ModuleItem::ConstDef(def) => {
+                        let ast_id = self.expander.ast_id(&def);
+                        let id = self.find_inner_item(ast_id.map(|id| id.upcast()));
+                        (
+                            ConstLoc { container: container.into(), id: ast_id.with_value(id) }
+                                .intern(self.db)
+                                .into(),
+                            def.name(),
+                        )
+                    }
+                    ast::ModuleItem::StaticDef(def) => {
+                        let ast_id = self.expander.ast_id(&def);
+                        let id = self.find_inner_item(ast_id.map(|id| id.upcast()));
+                        (
+                            StaticLoc { container, id: ast_id.with_value(id) }
+                                .intern(self.db)
+                                .into(),
+                            def.name(),
+                        )
+                    }
+                    ast::ModuleItem::StructDef(def) => {
+                        let ast_id = self.expander.ast_id(&def);
+                        let id = self.find_inner_item(ast_id.map(|id| id.upcast()));
+                        (
+                            StructLoc { container, id: ast_id.with_value(id) }
+                                .intern(self.db)
+                                .into(),
+                            def.name(),
+                        )
+                    }
+                    ast::ModuleItem::EnumDef(def) => {
+                        let ast_id = self.expander.ast_id(&def);
+                        let id = self.find_inner_item(ast_id.map(|id| id.upcast()));
+                        (
+                            EnumLoc { container, id: ast_id.with_value(id) }.intern(self.db).into(),
+                            def.name(),
+                        )
+                    }
+                    ast::ModuleItem::UnionDef(def) => {
+                        let ast_id = self.expander.ast_id(&def);
+                        let id = self.find_inner_item(ast_id.map(|id| id.upcast()));
+                        (
+                            UnionLoc { container, id: ast_id.with_value(id) }
+                                .intern(self.db)
+                                .into(),
+                            def.name(),
+                        )
+                    }
+                    ast::ModuleItem::TraitDef(def) => {
+                        let ast_id = self.expander.ast_id(&def);
+                        let id = self.find_inner_item(ast_id.map(|id| id.upcast()));
+                        (
+                            TraitLoc { container, id: ast_id.with_value(id) }
+                                .intern(self.db)
+                                .into(),
+                            def.name(),
+                        )
+                    }
+                    ast::ModuleItem::ExternBlock(_) => return None, // FIXME: collect from extern blocks
+                    ast::ModuleItem::ImplDef(_)
+                    | ast::ModuleItem::UseItem(_)
+                    | ast::ModuleItem::ExternCrateItem(_)
+                    | ast::ModuleItem::Module(_)
+                    | ast::ModuleItem::MacroCall(_) => return None,
+                };
+
+                Some((def, name))
+            })
+            .collect::<Vec<_>>();
+
+        for (def, name) in items {
             self.body.item_scope.define_def(def);
             if let Some(name) = name {
                 let vis = crate::visibility::Visibility::Public; // FIXME determine correctly
diff --git a/crates/ra_hir_def/src/body/scope.rs b/crates/ra_hir_def/src/body/scope.rs
index 0b74199d9f..81397b0631 100644
--- a/crates/ra_hir_def/src/body/scope.rs
+++ b/crates/ra_hir_def/src/body/scope.rs
@@ -317,6 +317,26 @@ fn foo() {
         );
     }
 
+    #[test]
+    fn macro_inner_item() {
+        do_check(
+            r"
+            macro_rules! mac {
+                () => {{
+                    fn inner() {}
+                    inner();
+                }};
+            }
+
+            fn foo() {
+                mac!();
+                <|>
+            }
+        ",
+            &[],
+        );
+    }
+
     fn do_check_local_name(ra_fixture: &str, expected_offset: u32) {
         let (db, position) = TestDB::with_position(ra_fixture);
         let file_id = position.file_id;
diff --git a/crates/ra_hir_def/src/data.rs b/crates/ra_hir_def/src/data.rs
index 53599e74a2..f9e5701db2 100644
--- a/crates/ra_hir_def/src/data.rs
+++ b/crates/ra_hir_def/src/data.rs
@@ -2,27 +2,19 @@
 
 use std::sync::Arc;
 
-use hir_expand::{
-    hygiene::Hygiene,
-    name::{name, AsName, Name},
-    AstId, InFile,
-};
+use hir_expand::{name::Name, InFile};
 use ra_prof::profile;
-use ra_syntax::ast::{
-    self, AssocItem, AstNode, ModuleItemOwner, NameOwner, TypeAscriptionOwner, TypeBoundsOwner,
-    VisibilityOwner,
-};
+use ra_syntax::ast;
 
 use crate::{
     attr::Attrs,
-    body::LowerCtx,
+    body::Expander,
     db::DefDatabase,
-    path::{path, AssociatedTypeBinding, GenericArgs, Path},
-    src::HasSource,
-    type_ref::{Mutability, TypeBound, TypeRef},
+    item_tree::{AssocItem, ItemTreeId, ModItem},
+    type_ref::{TypeBound, TypeRef},
     visibility::RawVisibility,
-    AssocContainerId, AssocItemId, ConstId, ConstLoc, Expander, FunctionId, FunctionLoc, HasModule,
-    ImplId, Intern, Lookup, StaticId, TraitId, TypeAliasId, TypeAliasLoc,
+    AssocContainerId, AssocItemId, ConstId, ConstLoc, FunctionId, FunctionLoc, HasModule, ImplId,
+    Intern, Lookup, ModuleId, StaticId, TraitId, TypeAliasId, TypeAliasLoc,
 };
 
 #[derive(Debug, Clone, PartialEq, Eq)]
@@ -41,82 +33,27 @@ pub struct FunctionData {
 impl FunctionData {
     pub(crate) fn fn_data_query(db: &impl DefDatabase, func: FunctionId) -> Arc<FunctionData> {
         let loc = func.lookup(db);
-        let src = loc.source(db);
-        let ctx = LowerCtx::new(db, src.file_id);
-        let name = src.value.name().map(|n| n.as_name()).unwrap_or_else(Name::missing);
-        let mut params = Vec::new();
-        let mut has_self_param = false;
-        if let Some(param_list) = src.value.param_list() {
-            if let Some(self_param) = param_list.self_param() {
-                let self_type = if let Some(type_ref) = self_param.ascribed_type() {
-                    TypeRef::from_ast(&ctx, type_ref)
-                } else {
-                    let self_type = TypeRef::Path(name![Self].into());
-                    match self_param.kind() {
-                        ast::SelfParamKind::Owned => self_type,
-                        ast::SelfParamKind::Ref => {
-                            TypeRef::Reference(Box::new(self_type), Mutability::Shared)
-                        }
-                        ast::SelfParamKind::MutRef => {
-                            TypeRef::Reference(Box::new(self_type), Mutability::Mut)
-                        }
-                    }
-                };
-                params.push(self_type);
-                has_self_param = true;
-            }
-            for param in param_list.params() {
-                let type_ref = TypeRef::from_ast_opt(&ctx, param.ascribed_type());
-                params.push(type_ref);
-            }
-        }
-        let attrs = Attrs::new(&src.value, &Hygiene::new(db.upcast(), src.file_id));
-
-        let ret_type = if let Some(type_ref) = src.value.ret_type().and_then(|rt| rt.type_ref()) {
-            TypeRef::from_ast(&ctx, type_ref)
-        } else {
-            TypeRef::unit()
-        };
-
-        let ret_type = if src.value.async_token().is_some() {
-            let future_impl = desugar_future_path(ret_type);
-            let ty_bound = TypeBound::Path(future_impl);
-            TypeRef::ImplTrait(vec![ty_bound])
-        } else {
-            ret_type
-        };
-
-        let is_unsafe = src.value.unsafe_token().is_some();
-
-        let vis_default = RawVisibility::default_for_container(loc.container);
-        let visibility =
-            RawVisibility::from_ast_with_default(db, vis_default, src.map(|s| s.visibility()));
-
-        let sig =
-            FunctionData { name, params, ret_type, has_self_param, is_unsafe, visibility, attrs };
-        Arc::new(sig)
+        let item_tree = db.item_tree(loc.id.file_id);
+        let func = &item_tree[loc.id.value];
+
+        Arc::new(FunctionData {
+            name: func.name.clone(),
+            params: func.params.to_vec(),
+            ret_type: func.ret_type.clone(),
+            attrs: item_tree.attrs(loc.id.value.into()).clone(),
+            has_self_param: func.has_self_param,
+            is_unsafe: func.is_unsafe,
+            visibility: item_tree[func.visibility].clone(),
+        })
     }
 }
 
-fn desugar_future_path(orig: TypeRef) -> Path {
-    let path = path![core::future::Future];
-    let mut generic_args: Vec<_> = std::iter::repeat(None).take(path.segments.len() - 1).collect();
-    let mut last = GenericArgs::empty();
-    last.bindings.push(AssociatedTypeBinding {
-        name: name![Output],
-        type_ref: Some(orig),
-        bounds: Vec::new(),
-    });
-    generic_args.push(Some(Arc::new(last)));
-
-    Path::from_known_path(path, generic_args)
-}
-
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub struct TypeAliasData {
     pub name: Name,
     pub type_ref: Option<TypeRef>,
     pub visibility: RawVisibility,
+    /// Bounds restricting the type alias itself (eg. `type Ty: Bound;` in a trait or impl).
     pub bounds: Vec<TypeBound>,
 }
 
@@ -126,22 +63,15 @@ impl TypeAliasData {
         typ: TypeAliasId,
     ) -> Arc<TypeAliasData> {
         let loc = typ.lookup(db);
-        let node = loc.source(db);
-        let name = node.value.name().map_or_else(Name::missing, |n| n.as_name());
-        let lower_ctx = LowerCtx::new(db, node.file_id);
-        let type_ref = node.value.type_ref().map(|it| TypeRef::from_ast(&lower_ctx, it));
-        let vis_default = RawVisibility::default_for_container(loc.container);
-        let visibility = RawVisibility::from_ast_with_default(
-            db,
-            vis_default,
-            node.as_ref().map(|n| n.visibility()),
-        );
-        let bounds = if let Some(bound_list) = node.value.type_bound_list() {
-            bound_list.bounds().map(|it| TypeBound::from_ast(&lower_ctx, it)).collect()
-        } else {
-            Vec::new()
-        };
-        Arc::new(TypeAliasData { name, type_ref, visibility, bounds })
+        let item_tree = db.item_tree(loc.id.file_id);
+        let typ = &item_tree[loc.id.value];
+
+        Arc::new(TypeAliasData {
+            name: typ.name.clone(),
+            type_ref: typ.type_ref.clone(),
+            visibility: item_tree[typ.visibility].clone(),
+            bounds: typ.bounds.to_vec(),
+        })
     }
 }
 
@@ -155,30 +85,24 @@ pub struct TraitData {
 impl TraitData {
     pub(crate) fn trait_data_query(db: &dyn DefDatabase, tr: TraitId) -> Arc<TraitData> {
         let tr_loc = tr.lookup(db);
-        let src = tr_loc.source(db);
-        let name = src.value.name().map_or_else(Name::missing, |n| n.as_name());
-        let auto = src.value.auto_token().is_some();
+        let item_tree = db.item_tree(tr_loc.id.file_id);
+        let tr_def = &item_tree[tr_loc.id.value];
+        let name = tr_def.name.clone();
+        let auto = tr_def.auto;
         let module_id = tr_loc.container.module(db);
-
         let container = AssocContainerId::TraitId(tr);
-        let mut items = Vec::new();
-
-        if let Some(item_list) = src.value.item_list() {
-            let mut expander = Expander::new(db, tr_loc.ast_id.file_id, module_id);
-            items.extend(collect_items(
-                db,
-                &mut expander,
-                item_list.assoc_items(),
-                src.file_id,
-                container,
-            ));
-            items.extend(collect_items_in_macros(
-                db,
-                &mut expander,
-                &src.with_value(item_list),
-                container,
-            ));
-        }
+        let mut expander = Expander::new(db, tr_loc.id.file_id, module_id);
+
+        let items = collect_items(
+            db,
+            module_id,
+            &mut expander,
+            tr_def.items.iter().copied(),
+            tr_loc.id.file_id,
+            container,
+            100,
+        );
+
         Arc::new(TraitData { name, items, auto })
     }
 
@@ -209,33 +133,28 @@ impl ImplData {
     pub(crate) fn impl_data_query(db: &dyn DefDatabase, id: ImplId) -> Arc<ImplData> {
         let _p = profile("impl_data_query");
         let impl_loc = id.lookup(db);
-        let src = impl_loc.source(db);
-        let lower_ctx = LowerCtx::new(db, src.file_id);
 
-        let target_trait = src.value.target_trait().map(|it| TypeRef::from_ast(&lower_ctx, it));
-        let target_type = TypeRef::from_ast_opt(&lower_ctx, src.value.target_type());
-        let is_negative = src.value.excl_token().is_some();
+        let item_tree = db.item_tree(impl_loc.id.file_id);
+        let impl_def = &item_tree[impl_loc.id.value];
+        let target_trait = impl_def.target_trait.clone();
+        let target_type = impl_def.target_type.clone();
+        let is_negative = impl_def.is_negative;
         let module_id = impl_loc.container.module(db);
         let container = AssocContainerId::ImplId(id);
+        let mut expander = Expander::new(db, impl_loc.id.file_id, module_id);
 
-        let mut items: Vec<AssocItemId> = Vec::new();
-
-        if let Some(item_list) = src.value.item_list() {
-            let mut expander = Expander::new(db, impl_loc.ast_id.file_id, module_id);
-            items.extend(
-                collect_items(db, &mut expander, item_list.assoc_items(), src.file_id, container)
-                    .into_iter()
-                    .map(|(_, item)| item),
-            );
-            items.extend(
-                collect_items_in_macros(db, &mut expander, &src.with_value(item_list), container)
-                    .into_iter()
-                    .map(|(_, item)| item),
-            );
-        }
+        let items = collect_items(
+            db,
+            module_id,
+            &mut expander,
+            impl_def.items.iter().copied(),
+            impl_loc.id.file_id,
+            container,
+            100,
+        );
+        let items = items.into_iter().map(|(_, item)| item).collect();
 
-        let res = ImplData { target_trait, target_type, items, is_negative };
-        Arc::new(res)
+        Arc::new(ImplData { target_trait, target_type, items, is_negative })
     }
 }
 
@@ -250,22 +169,14 @@ pub struct ConstData {
 impl ConstData {
     pub(crate) fn const_data_query(db: &dyn DefDatabase, konst: ConstId) -> Arc<ConstData> {
         let loc = konst.lookup(db);
-        let node = loc.source(db);
-        let vis_default = RawVisibility::default_for_container(loc.container);
-        Arc::new(ConstData::new(db, vis_default, node))
-    }
+        let item_tree = db.item_tree(loc.id.file_id);
+        let konst = &item_tree[loc.id.value];
 
-    fn new<N: NameOwner + TypeAscriptionOwner + VisibilityOwner>(
-        db: &dyn DefDatabase,
-        vis_default: RawVisibility,
-        node: InFile<N>,
-    ) -> ConstData {
-        let ctx = LowerCtx::new(db, node.file_id);
-        let name = node.value.name().map(|n| n.as_name());
-        let type_ref = TypeRef::from_ast_opt(&ctx, node.value.ascribed_type());
-        let visibility =
-            RawVisibility::from_ast_with_default(db, vis_default, node.map(|n| n.visibility()));
-        ConstData { name, type_ref, visibility }
+        Arc::new(ConstData {
+            name: konst.name.clone(),
+            type_ref: konst.type_ref.clone(),
+            visibility: item_tree[konst.visibility].clone(),
+        })
     }
 }
 
@@ -279,44 +190,25 @@ pub struct StaticData {
 
 impl StaticData {
     pub(crate) fn static_data_query(db: &dyn DefDatabase, konst: StaticId) -> Arc<StaticData> {
-        let node = konst.lookup(db).source(db);
-        let ctx = LowerCtx::new(db, node.file_id);
-
-        let name = node.value.name().map(|n| n.as_name());
-        let type_ref = TypeRef::from_ast_opt(&ctx, node.value.ascribed_type());
-        let mutable = node.value.mut_token().is_some();
-        let visibility = RawVisibility::from_ast_with_default(
-            db,
-            RawVisibility::private(),
-            node.map(|n| n.visibility()),
-        );
-
-        Arc::new(StaticData { name, type_ref, visibility, mutable })
-    }
-}
-
-fn collect_items_in_macros(
-    db: &dyn DefDatabase,
-    expander: &mut Expander,
-    impl_def: &InFile<ast::ItemList>,
-    container: AssocContainerId,
-) -> Vec<(Name, AssocItemId)> {
-    let mut res = Vec::new();
-
-    // We set a limit to protect against infinite recursion
-    let limit = 100;
-
-    for m in impl_def.value.syntax().children().filter_map(ast::MacroCall::cast) {
-        res.extend(collect_items_in_macro(db, expander, m, container, limit))
+        let node = konst.lookup(db);
+        let item_tree = db.item_tree(node.id.file_id);
+        let statik = &item_tree[node.id.value];
+
+        Arc::new(StaticData {
+            name: Some(statik.name.clone()),
+            type_ref: statik.type_ref.clone(),
+            visibility: item_tree[statik.visibility].clone(),
+            mutable: statik.mutable,
+        })
     }
-
-    res
 }
 
-fn collect_items_in_macro(
+fn collect_items(
     db: &dyn DefDatabase,
+    module: ModuleId,
     expander: &mut Expander,
-    m: ast::MacroCall,
+    assoc_items: impl Iterator<Item = AssocItem>,
+    file_id: crate::HirFileId,
     container: AssocContainerId,
     limit: usize,
 ) -> Vec<(Name, AssocItemId)> {
@@ -324,62 +216,62 @@ fn collect_items_in_macro(
         return Vec::new();
     }
 
-    if let Some((mark, items)) = expander.enter_expand(db, None, m) {
-        let items: InFile<ast::MacroItems> = expander.to_source(items);
-        let mut res = collect_items(
-            db,
-            expander,
-            items.value.items().filter_map(|it| AssocItem::cast(it.syntax().clone())),
-            items.file_id,
-            container,
-        );
-
-        // Recursive collect macros
-        // Note that ast::ModuleItem do not include ast::MacroCall
-        // We cannot use ModuleItemOwner::items here
-        for it in items.value.syntax().children().filter_map(ast::MacroCall::cast) {
-            res.extend(collect_items_in_macro(db, expander, it, container, limit - 1))
-        }
-        expander.exit(db, mark);
-        res
-    } else {
-        Vec::new()
-    }
-}
-
-fn collect_items(
-    db: &dyn DefDatabase,
-    expander: &mut Expander,
-    assoc_items: impl Iterator<Item = AssocItem>,
-    file_id: crate::HirFileId,
-    container: AssocContainerId,
-) -> Vec<(Name, AssocItemId)> {
-    let items = db.ast_id_map(file_id);
-
-    assoc_items
-        .filter_map(|item_node| match item_node {
-            ast::AssocItem::FnDef(it) => {
-                let name = it.name().map_or_else(Name::missing, |it| it.as_name());
-                if !expander.is_cfg_enabled(&it) {
-                    return None;
+    let item_tree = db.item_tree(file_id);
+    let cfg_options = db.crate_graph()[module.krate].cfg_options.clone();
+
+    let mut items = Vec::new();
+    for item in assoc_items {
+        match item {
+            AssocItem::Function(id) => {
+                let item = &item_tree[id];
+                let attrs = item_tree.attrs(id.into());
+                if !attrs.is_cfg_enabled(&cfg_options) {
+                    continue;
                 }
-                let def = FunctionLoc { container, ast_id: AstId::new(file_id, items.ast_id(&it)) }
-                    .intern(db);
-                Some((name, def.into()))
+                let def = FunctionLoc { container, id: ItemTreeId::new(file_id, id) }.intern(db);
+                items.push((item.name.clone(), def.into()));
             }
-            ast::AssocItem::ConstDef(it) => {
-                let name = it.name().map_or_else(Name::missing, |it| it.as_name());
-                let def = ConstLoc { container, ast_id: AstId::new(file_id, items.ast_id(&it)) }
-                    .intern(db);
-                Some((name, def.into()))
+            // FIXME: cfg?
+            AssocItem::Const(id) => {
+                let item = &item_tree[id];
+                let name = match item.name.clone() {
+                    Some(name) => name,
+                    None => continue,
+                };
+                let def = ConstLoc { container, id: ItemTreeId::new(file_id, id) }.intern(db);
+                items.push((name, def.into()));
             }
-            ast::AssocItem::TypeAliasDef(it) => {
-                let name = it.name().map_or_else(Name::missing, |it| it.as_name());
-                let def =
-                    TypeAliasLoc { container, ast_id: AstId::new(file_id, items.ast_id(&it)) }
-                        .intern(db);
-                Some((name, def.into()))
+            AssocItem::TypeAlias(id) => {
+                let item = &item_tree[id];
+                let def = TypeAliasLoc { container, id: ItemTreeId::new(file_id, id) }.intern(db);
+                items.push((item.name.clone(), def.into()));
             }
-        })
-        .collect()
+            AssocItem::MacroCall(call) => {
+                let call = &item_tree[call];
+                let ast_id_map = db.ast_id_map(file_id);
+                let root = db.parse_or_expand(file_id).unwrap();
+                let call = ast_id_map.get(call.ast_id).to_node(&root);
+
+                if let Some((mark, mac)) = expander.enter_expand(db, None, call) {
+                    let src: InFile<ast::MacroItems> = expander.to_source(mac);
+                    let item_tree = db.item_tree(src.file_id);
+                    let iter =
+                        item_tree.top_level_items().iter().filter_map(ModItem::as_assoc_item);
+                    items.extend(collect_items(
+                        db,
+                        module,
+                        expander,
+                        iter,
+                        src.file_id,
+                        container,
+                        limit - 1,
+                    ));
+
+                    expander.exit(db, mark);
+                }
+            }
+        }
+    }
+
+    items
 }
diff --git a/crates/ra_hir_def/src/db.rs b/crates/ra_hir_def/src/db.rs
index 10cc26480f..9c3ede2d79 100644
--- a/crates/ra_hir_def/src/db.rs
+++ b/crates/ra_hir_def/src/db.rs
@@ -14,8 +14,9 @@ use crate::{
     docs::Documentation,
     generics::GenericParams,
     import_map::ImportMap,
+    item_tree::ItemTree,
     lang_item::{LangItemTarget, LangItems},
-    nameres::{raw::RawItems, CrateDefMap},
+    nameres::CrateDefMap,
     AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc,
     GenericDefId, ImplId, ImplLoc, ModuleId, StaticId, StaticLoc, StructId, StructLoc, TraitId,
     TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc,
@@ -45,8 +46,8 @@ pub trait InternDatabase: SourceDatabase {
 
 #[salsa::query_group(DefDatabaseStorage)]
 pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
-    #[salsa::invoke(RawItems::raw_items_query)]
-    fn raw_items(&self, file_id: HirFileId) -> Arc<RawItems>;
+    #[salsa::invoke(ItemTree::item_tree_query)]
+    fn item_tree(&self, file_id: HirFileId) -> Arc<ItemTree>;
 
     #[salsa::invoke(crate_def_map_wait)]
     #[salsa::transparent]
diff --git a/crates/ra_hir_def/src/generics.rs b/crates/ra_hir_def/src/generics.rs
index 09a5241f78..6a0f493a79 100644
--- a/crates/ra_hir_def/src/generics.rs
+++ b/crates/ra_hir_def/src/generics.rs
@@ -42,7 +42,7 @@ pub enum TypeParamProvenance {
 }
 
 /// Data about the generic parameters of a function, struct, impl, etc.
-#[derive(Clone, PartialEq, Eq, Debug)]
+#[derive(Clone, PartialEq, Eq, Debug, Default)]
 pub struct GenericParams {
     pub types: Arena<TypeParamData>,
     // lifetimes: Arena<LocalLifetimeParamId, LifetimeParamData>,
@@ -74,8 +74,53 @@ impl GenericParams {
         def: GenericDefId,
     ) -> Arc<GenericParams> {
         let _p = profile("generic_params_query");
-        let (params, _source_map) = GenericParams::new(db, def);
-        Arc::new(params)
+
+        let generics = match def {
+            GenericDefId::FunctionId(id) => {
+                let id = id.lookup(db).id;
+                let tree = db.item_tree(id.file_id);
+                let item = &tree[id.value];
+                tree[item.generic_params].clone()
+            }
+            GenericDefId::AdtId(AdtId::StructId(id)) => {
+                let id = id.lookup(db).id;
+                let tree = db.item_tree(id.file_id);
+                let item = &tree[id.value];
+                tree[item.generic_params].clone()
+            }
+            GenericDefId::AdtId(AdtId::EnumId(id)) => {
+                let id = id.lookup(db).id;
+                let tree = db.item_tree(id.file_id);
+                let item = &tree[id.value];
+                tree[item.generic_params].clone()
+            }
+            GenericDefId::AdtId(AdtId::UnionId(id)) => {
+                let id = id.lookup(db).id;
+                let tree = db.item_tree(id.file_id);
+                let item = &tree[id.value];
+                tree[item.generic_params].clone()
+            }
+            GenericDefId::TraitId(id) => {
+                let id = id.lookup(db).id;
+                let tree = db.item_tree(id.file_id);
+                let item = &tree[id.value];
+                tree[item.generic_params].clone()
+            }
+            GenericDefId::TypeAliasId(id) => {
+                let id = id.lookup(db).id;
+                let tree = db.item_tree(id.file_id);
+                let item = &tree[id.value];
+                tree[item.generic_params].clone()
+            }
+            GenericDefId::ImplId(id) => {
+                let id = id.lookup(db).id;
+                let tree = db.item_tree(id.file_id);
+                let item = &tree[id.value];
+                tree[item.generic_params].clone()
+            }
+            GenericDefId::EnumVariantId(_) | GenericDefId::ConstId(_) => GenericParams::default(),
+        };
+        Arc::new(generics)
     }
 
     fn new(db: &dyn DefDatabase, def: GenericDefId) -> (GenericParams, InFile<SourceMap>) {
@@ -156,7 +201,12 @@ impl GenericParams {
         (generics, InFile::new(file_id, sm))
     }
 
-    fn fill(&mut self, lower_ctx: &LowerCtx, sm: &mut SourceMap, node: &dyn TypeParamsOwner) {
+    pub(crate) fn fill(
+        &mut self,
+        lower_ctx: &LowerCtx,
+        sm: &mut SourceMap,
+        node: &dyn TypeParamsOwner,
+    ) {
         if let Some(params) = node.type_param_list() {
             self.fill_params(lower_ctx, sm, params)
         }
@@ -165,7 +215,7 @@ impl GenericParams {
         }
     }
 
-    fn fill_bounds(
+    pub(crate) fn fill_bounds(
         &mut self,
         lower_ctx: &LowerCtx,
         node: &dyn ast::TypeBoundsOwner,
@@ -229,7 +279,7 @@ impl GenericParams {
             .push(WherePredicate { target: WherePredicateTarget::TypeRef(type_ref), bound });
     }
 
-    fn fill_implicit_impl_trait_args(&mut self, type_ref: &TypeRef) {
+    pub(crate) fn fill_implicit_impl_trait_args(&mut self, type_ref: &TypeRef) {
         type_ref.walk(&mut |type_ref| {
             if let TypeRef::ImplTrait(bounds) = type_ref {
                 let param = TypeParamData {
diff --git a/crates/ra_hir_def/src/item_tree.rs b/crates/ra_hir_def/src/item_tree.rs
new file mode 100644
index 0000000000..d7bc64e6ca
--- /dev/null
+++ b/crates/ra_hir_def/src/item_tree.rs
@@ -0,0 +1,697 @@
+//! A simplified AST that only contains items.
+
+mod lower;
+#[cfg(test)]
+mod tests;
+
+use std::{
+    fmt::{self, Debug},
+    hash::{Hash, Hasher},
+    marker::PhantomData,
+    ops::{Index, Range},
+    sync::Arc,
+};
+
+use ast::{AstNode, AttrsOwner, NameOwner, StructKind, TypeAscriptionOwner};
+use either::Either;
+use hir_expand::{
+    ast_id_map::FileAstId,
+    hygiene::Hygiene,
+    name::{name, AsName, Name},
+    HirFileId, InFile,
+};
+use ra_arena::{Arena, Idx, RawId};
+use ra_syntax::{ast, match_ast};
+use rustc_hash::FxHashMap;
+use smallvec::SmallVec;
+use test_utils::mark;
+
+use crate::{
+    attr::Attrs,
+    db::DefDatabase,
+    generics::GenericParams,
+    path::{path, AssociatedTypeBinding, GenericArgs, ImportAlias, ModPath, Path, PathKind},
+    type_ref::{Mutability, TypeBound, TypeRef},
+    visibility::RawVisibility,
+};
+
+#[derive(Copy, Clone, Eq, PartialEq)]
+pub struct RawVisibilityId(u32);
+
+impl RawVisibilityId {
+    pub const PUB: Self = RawVisibilityId(u32::max_value());
+    pub const PRIV: Self = RawVisibilityId(u32::max_value() - 1);
+    pub const PUB_CRATE: Self = RawVisibilityId(u32::max_value() - 2);
+}
+
+impl fmt::Debug for RawVisibilityId {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let mut f = f.debug_tuple("RawVisibilityId");
+        match *self {
+            Self::PUB => f.field(&"pub"),
+            Self::PRIV => f.field(&"pub(self)"),
+            Self::PUB_CRATE => f.field(&"pub(crate)"),
+            _ => f.field(&self.0),
+        };
+        f.finish()
+    }
+}
+
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub struct GenericParamsId(u32);
+
+impl GenericParamsId {
+    pub const EMPTY: Self = GenericParamsId(u32::max_value());
+}
+
+/// The item tree of a source file.
+#[derive(Debug, Eq, PartialEq)]
+pub struct ItemTree {
+    top_level: SmallVec<[ModItem; 1]>,
+    attrs: FxHashMap<AttrOwner, Attrs>,
+    inner_items: FxHashMap<FileAstId<ast::ModuleItem>, SmallVec<[ModItem; 1]>>,
+
+    data: Option<Box<ItemTreeData>>,
+}
+
+impl ItemTree {
+    pub fn item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<ItemTree> {
+        let _p = ra_prof::profile("item_tree_query").detail(|| format!("{:?}", file_id));
+        let syntax = if let Some(node) = db.parse_or_expand(file_id) {
+            node
+        } else {
+            return Arc::new(Self::empty());
+        };
+
+        let hygiene = Hygiene::new(db.upcast(), file_id);
+        let ctx = lower::Ctx::new(db, hygiene.clone(), file_id);
+        let mut top_attrs = None;
+        let mut item_tree = match_ast! {
+            match syntax {
+                ast::SourceFile(file) => {
+                    top_attrs = Some(Attrs::new(&file, &hygiene));
+                    ctx.lower_module_items(&file)
+                },
+                ast::MacroItems(items) => {
+                    ctx.lower_module_items(&items)
+                },
+                // Macros can expand to expressions. We return an empty item tree in this case, but
+                // still need to collect inner items.
+                ast::Expr(e) => {
+                    ctx.lower_inner_items(e.syntax())
+                },
+                _ => {
+                    panic!("cannot create item tree from {:?}", syntax);
+                },
+            }
+        };
+
+        if let Some(attrs) = top_attrs {
+            item_tree.attrs.insert(AttrOwner::TopLevel, attrs);
+        }
+        item_tree.shrink_to_fit();
+        Arc::new(item_tree)
+    }
+
+    fn empty() -> Self {
+        Self {
+            top_level: Default::default(),
+            attrs: Default::default(),
+            inner_items: Default::default(),
+            data: Default::default(),
+        }
+    }
+
+    fn shrink_to_fit(&mut self) {
+        if let Some(data) = &mut self.data {
+            let ItemTreeData {
+                imports,
+                extern_crates,
+                functions,
+                structs,
+                fields,
+                unions,
+                enums,
+                variants,
+                consts,
+                statics,
+                traits,
+                impls,
+                type_aliases,
+                mods,
+                macro_calls,
+                exprs,
+                vis,
+                generics,
+            } = &mut **data;
+
+            imports.shrink_to_fit();
+            extern_crates.shrink_to_fit();
+            functions.shrink_to_fit();
+            structs.shrink_to_fit();
+            fields.shrink_to_fit();
+            unions.shrink_to_fit();
+            enums.shrink_to_fit();
+            variants.shrink_to_fit();
+            consts.shrink_to_fit();
+            statics.shrink_to_fit();
+            traits.shrink_to_fit();
+            impls.shrink_to_fit();
+            type_aliases.shrink_to_fit();
+            mods.shrink_to_fit();
+            macro_calls.shrink_to_fit();
+            exprs.shrink_to_fit();
+
+            vis.arena.shrink_to_fit();
+            generics.arena.shrink_to_fit();
+        }
+    }
+
+    /// Returns an iterator over all items located at the top level of the `HirFileId` this
+    /// `ItemTree` was created from.
+    pub fn top_level_items(&self) -> &[ModItem] {
+        &self.top_level
+    }
+
+    /// Returns the inner attributes of the source file.
+    pub fn top_level_attrs(&self) -> &Attrs {
+        self.attrs.get(&AttrOwner::TopLevel).unwrap_or(&Attrs::EMPTY)
+    }
+
+    pub fn attrs(&self, of: ModItem) -> &Attrs {
+        self.attrs.get(&AttrOwner::ModItem(of)).unwrap_or(&Attrs::EMPTY)
+    }
+
+    /// Returns the lowered inner items that `ast` corresponds to.
+    ///
+    /// Most AST items are lowered to a single `ModItem`, but some (eg. `use` items) may be lowered
+    /// to multiple items in the `ItemTree`.
+    pub fn inner_items(&self, ast: FileAstId<ast::ModuleItem>) -> &[ModItem] {
+        &self.inner_items[&ast]
+    }
+
+    pub fn all_inner_items(&self) -> impl Iterator<Item = ModItem> + '_ {
+        self.inner_items.values().flatten().copied()
+    }
+
+    pub fn source<S: ItemTreeNode>(&self, db: &dyn DefDatabase, of: ItemTreeId<S>) -> S::Source {
+        // This unwrap cannot fail, since it has either succeeded above, or resulted in an empty
+        // ItemTree (in which case there is no valid `FileItemTreeId` to call this method with).
+        let root =
+            db.parse_or_expand(of.file_id).expect("parse_or_expand failed on constructed ItemTree");
+
+        let id = self[of.value].ast_id();
+        let map = db.ast_id_map(of.file_id);
+        let ptr = map.get(id);
+        ptr.to_node(&root)
+    }
+
+    fn data(&self) -> &ItemTreeData {
+        self.data.as_ref().expect("attempted to access data of empty ItemTree")
+    }
+
+    fn data_mut(&mut self) -> &mut ItemTreeData {
+        self.data.get_or_insert_with(Box::default)
+    }
+}
+
+#[derive(Default, Debug, Eq, PartialEq)]
+struct ItemVisibilities {
+    arena: Arena<RawVisibility>,
+}
+
+impl ItemVisibilities {
+    fn alloc(&mut self, vis: RawVisibility) -> RawVisibilityId {
+        match &vis {
+            RawVisibility::Public => RawVisibilityId::PUB,
+            RawVisibility::Module(path) if path.segments.is_empty() => match &path.kind {
+                PathKind::Super(0) => RawVisibilityId::PRIV,
+                PathKind::Crate => RawVisibilityId::PUB_CRATE,
+                _ => RawVisibilityId(self.arena.alloc(vis).into_raw().into()),
+            },
+            _ => RawVisibilityId(self.arena.alloc(vis).into_raw().into()),
+        }
+    }
+}
+
+static VIS_PUB: RawVisibility = RawVisibility::Public;
+static VIS_PRIV: RawVisibility =
+    RawVisibility::Module(ModPath { kind: PathKind::Super(0), segments: Vec::new() });
+static VIS_PUB_CRATE: RawVisibility =
+    RawVisibility::Module(ModPath { kind: PathKind::Crate, segments: Vec::new() });
+
+#[derive(Default, Debug, Eq, PartialEq)]
+struct GenericParamsStorage {
+    arena: Arena<GenericParams>,
+}
+
+impl GenericParamsStorage {
+    fn alloc(&mut self, params: GenericParams) -> GenericParamsId {
+        if params.types.is_empty() && params.where_predicates.is_empty() {
+            return GenericParamsId::EMPTY;
+        }
+
+        GenericParamsId(self.arena.alloc(params).into_raw().into())
+    }
+}
+
+static EMPTY_GENERICS: GenericParams =
+    GenericParams { types: Arena::new(), where_predicates: Vec::new() };
+
+#[derive(Default, Debug, Eq, PartialEq)]
+struct ItemTreeData {
+    imports: Arena<Import>,
+    extern_crates: Arena<ExternCrate>,
+    functions: Arena<Function>,
+    structs: Arena<Struct>,
+    fields: Arena<Field>,
+    unions: Arena<Union>,
+    enums: Arena<Enum>,
+    variants: Arena<Variant>,
+    consts: Arena<Const>,
+    statics: Arena<Static>,
+    traits: Arena<Trait>,
+    impls: Arena<Impl>,
+    type_aliases: Arena<TypeAlias>,
+    mods: Arena<Mod>,
+    macro_calls: Arena<MacroCall>,
+    exprs: Arena<Expr>,
+
+    vis: ItemVisibilities,
+    generics: GenericParamsStorage,
+}
+
+#[derive(Debug, Eq, PartialEq, Hash)]
+enum AttrOwner {
+    /// Attributes on an item.
+    ModItem(ModItem),
+    /// Inner attributes of the source file.
+    TopLevel,
+    // FIXME: Store variant and field attrs, and stop reparsing them in `attrs_query`.
+}
+
+/// Trait implemented by all nodes in the item tree.
+pub trait ItemTreeNode: Clone {
+    type Source: AstNode + Into<ast::ModuleItem>;
+
+    fn ast_id(&self) -> FileAstId<Self::Source>;
+
+    /// Looks up an instance of `Self` in an item tree.
+    fn lookup(tree: &ItemTree, index: Idx<Self>) -> &Self;
+
+    /// Downcasts a `ModItem` to a `FileItemTreeId` specific to this type.
+    fn id_from_mod_item(mod_item: ModItem) -> Option<FileItemTreeId<Self>>;
+
+    /// Upcasts a `FileItemTreeId` to a generic `ModItem`.
+    fn id_to_mod_item(id: FileItemTreeId<Self>) -> ModItem;
+}
+
+pub struct FileItemTreeId<N: ItemTreeNode> {
+    index: Idx<N>,
+    _p: PhantomData<N>,
+}
+
+impl<N: ItemTreeNode> Clone for FileItemTreeId<N> {
+    fn clone(&self) -> Self {
+        Self { index: self.index, _p: PhantomData }
+    }
+}
+impl<N: ItemTreeNode> Copy for FileItemTreeId<N> {}
+
+impl<N: ItemTreeNode> PartialEq for FileItemTreeId<N> {
+    fn eq(&self, other: &FileItemTreeId<N>) -> bool {
+        self.index == other.index
+    }
+}
+impl<N: ItemTreeNode> Eq for FileItemTreeId<N> {}
+
+impl<N: ItemTreeNode> Hash for FileItemTreeId<N> {
+    fn hash<H: Hasher>(&self, state: &mut H) {
+        self.index.hash(state)
+    }
+}
+
+impl<N: ItemTreeNode> fmt::Debug for FileItemTreeId<N> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        self.index.fmt(f)
+    }
+}
+
+pub type ItemTreeId<N> = InFile<FileItemTreeId<N>>;
+
+macro_rules! mod_items {
+    ( $( $typ:ident in $fld:ident -> $ast:ty ),+ $(,)? ) => {
+        #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
+        pub enum ModItem {
+            $(
+                $typ(FileItemTreeId<$typ>),
+            )+
+        }
+
+        $(
+            impl From<FileItemTreeId<$typ>> for ModItem {
+                fn from(id: FileItemTreeId<$typ>) -> ModItem {
+                    ModItem::$typ(id)
+                }
+            }
+        )+
+
+        $(
+            impl ItemTreeNode for $typ {
+                type Source = $ast;
+
+                fn ast_id(&self) -> FileAstId<Self::Source> {
+                    self.ast_id
+                }
+
+                fn lookup(tree: &ItemTree, index: Idx<Self>) -> &Self {
+                    &tree.data().$fld[index]
+                }
+
+                fn id_from_mod_item(mod_item: ModItem) -> Option<FileItemTreeId<Self>> {
+                    if let ModItem::$typ(id) = mod_item {
+                        Some(id)
+                    } else {
+                        None
+                    }
+                }
+
+                fn id_to_mod_item(id: FileItemTreeId<Self>) -> ModItem {
+                    ModItem::$typ(id)
+                }
+            }
+
+            impl Index<Idx<$typ>> for ItemTree {
+                type Output = $typ;
+
+                fn index(&self, index: Idx<$typ>) -> &Self::Output {
+                    &self.data().$fld[index]
+                }
+            }
+        )+
+    };
+}
+
+mod_items! {
+    Import in imports -> ast::UseItem,
+    ExternCrate in extern_crates -> ast::ExternCrateItem,
+    Function in functions -> ast::FnDef,
+    Struct in structs -> ast::StructDef,
+    Union in unions -> ast::UnionDef,
+    Enum in enums -> ast::EnumDef,
+    Const in consts -> ast::ConstDef,
+    Static in statics -> ast::StaticDef,
+    Trait in traits -> ast::TraitDef,
+    Impl in impls -> ast::ImplDef,
+    TypeAlias in type_aliases -> ast::TypeAliasDef,
+    Mod in mods -> ast::Module,
+    MacroCall in macro_calls -> ast::MacroCall,
+}
+
+macro_rules! impl_index {
+    ( $($fld:ident: $t:ty),+ $(,)? ) => {
+        $(
+            impl Index<Idx<$t>> for ItemTree {
+                type Output = $t;
+
+                fn index(&self, index: Idx<$t>) -> &Self::Output {
+                    &self.data().$fld[index]
+                }
+            }
+        )+
+    };
+}
+
+impl_index!(fields: Field, variants: Variant, exprs: Expr);
+
+impl Index<RawVisibilityId> for ItemTree {
+    type Output = RawVisibility;
+    fn index(&self, index: RawVisibilityId) -> &Self::Output {
+        match index {
+            RawVisibilityId::PRIV => &VIS_PRIV,
+            RawVisibilityId::PUB => &VIS_PUB,
+            RawVisibilityId::PUB_CRATE => &VIS_PUB_CRATE,
+            _ => &self.data().vis.arena[Idx::from_raw(index.0.into())],
+        }
+    }
+}
+
+impl Index<GenericParamsId> for ItemTree {
+    type Output = GenericParams;
+
+    fn index(&self, index: GenericParamsId) -> &Self::Output {
+        match index {
+            GenericParamsId::EMPTY => &EMPTY_GENERICS,
+            _ => &self.data().generics.arena[Idx::from_raw(index.0.into())],
+        }
+    }
+}
+
+impl<N: ItemTreeNode> Index<FileItemTreeId<N>> for ItemTree {
+    type Output = N;
+    fn index(&self, id: FileItemTreeId<N>) -> &N {
+        N::lookup(self, id.index)
+    }
+}
+
+/// A desugared `use` import.
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct Import {
+    pub path: ModPath,
+    pub alias: Option<ImportAlias>,
+    pub visibility: RawVisibilityId,
+    pub is_glob: bool,
+    pub is_prelude: bool,
+    /// AST ID of the `use` or `extern crate` item this import was derived from. Note that many
+    /// `Import`s can map to the same `use` item.
+    pub ast_id: FileAstId<ast::UseItem>,
+}
+
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct ExternCrate {
+    pub path: ModPath,
+    pub alias: Option<ImportAlias>,
+    pub visibility: RawVisibilityId,
+    /// Whether this is a `#[macro_use] extern crate ...`.
+    pub is_macro_use: bool,
+    pub ast_id: FileAstId<ast::ExternCrateItem>,
+}
+
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct Function {
+    pub name: Name,
+    pub visibility: RawVisibilityId,
+    pub generic_params: GenericParamsId,
+    pub has_self_param: bool,
+    pub is_unsafe: bool,
+    pub params: Box<[TypeRef]>,
+    pub ret_type: TypeRef,
+    pub ast_id: FileAstId<ast::FnDef>,
+}
+
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct Struct {
+    pub name: Name,
+    pub visibility: RawVisibilityId,
+    pub generic_params: GenericParamsId,
+    pub fields: Fields,
+    pub ast_id: FileAstId<ast::StructDef>,
+    pub kind: StructDefKind,
+}
+
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub enum StructDefKind {
+    /// `struct S { ... }` - type namespace only.
+    Record,
+    /// `struct S(...);`
+    Tuple,
+    /// `struct S;`
+    Unit,
+}
+
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct Union {
+    pub name: Name,
+    pub visibility: RawVisibilityId,
+    pub generic_params: GenericParamsId,
+    pub fields: Fields,
+    pub ast_id: FileAstId<ast::UnionDef>,
+}
+
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct Enum {
+    pub name: Name,
+    pub visibility: RawVisibilityId,
+    pub generic_params: GenericParamsId,
+    pub variants: Range<Idx<Variant>>,
+    pub ast_id: FileAstId<ast::EnumDef>,
+}
+
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct Const {
+    /// const _: () = ();
+    pub name: Option<Name>,
+    pub visibility: RawVisibilityId,
+    pub type_ref: TypeRef,
+    pub ast_id: FileAstId<ast::ConstDef>,
+}
+
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct Static {
+    pub name: Name,
+    pub visibility: RawVisibilityId,
+    pub mutable: bool,
+    pub type_ref: TypeRef,
+    pub ast_id: FileAstId<ast::StaticDef>,
+}
+
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct Trait {
+    pub name: Name,
+    pub visibility: RawVisibilityId,
+    pub generic_params: GenericParamsId,
+    pub auto: bool,
+    pub items: Box<[AssocItem]>,
+    pub ast_id: FileAstId<ast::TraitDef>,
+}
+
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct Impl {
+    pub generic_params: GenericParamsId,
+    pub target_trait: Option<TypeRef>,
+    pub target_type: TypeRef,
+    pub is_negative: bool,
+    pub items: Box<[AssocItem]>,
+    pub ast_id: FileAstId<ast::ImplDef>,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct TypeAlias {
+    pub name: Name,
+    pub visibility: RawVisibilityId,
+    /// Bounds on the type alias itself. Only valid in trait declarations, eg. `type Assoc: Copy;`.
+    pub bounds: Box<[TypeBound]>,
+    pub generic_params: GenericParamsId,
+    pub type_ref: Option<TypeRef>,
+    pub ast_id: FileAstId<ast::TypeAliasDef>,
+}
+
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct Mod {
+    pub name: Name,
+    pub visibility: RawVisibilityId,
+    pub kind: ModKind,
+    pub ast_id: FileAstId<ast::Module>,
+}
+
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub enum ModKind {
+    /// `mod m { ... }`
+    Inline { items: Box<[ModItem]> },
+
+    /// `mod m;`
+    Outline {},
+}
+
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct MacroCall {
+    /// For `macro_rules!` declarations, this is the name of the declared macro.
+    pub name: Option<Name>,
+    /// Path to the called macro.
+    pub path: ModPath,
+    /// Has `#[macro_export]`.
+    pub is_export: bool,
+    /// Has `#[macro_export(local_inner_macros)]`.
+    pub is_local_inner: bool,
+    /// Has `#[rustc_builtin_macro]`.
+    pub is_builtin: bool,
+    pub ast_id: FileAstId<ast::MacroCall>,
+}
+
+// NB: There's no `FileAstId` for `Expr`. The only case where this would be useful is for array
+// lengths, but we don't do much with them yet.
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct Expr;
+
+macro_rules! impl_froms {
+    ($e:ident { $($v:ident ($t:ty)),* $(,)? }) => {
+        $(
+            impl From<$t> for $e {
+                fn from(it: $t) -> $e {
+                    $e::$v(it)
+                }
+            }
+        )*
+    }
+}
+
+impl ModItem {
+    pub fn as_assoc_item(&self) -> Option<AssocItem> {
+        match self {
+            ModItem::Import(_)
+            | ModItem::ExternCrate(_)
+            | ModItem::Struct(_)
+            | ModItem::Union(_)
+            | ModItem::Enum(_)
+            | ModItem::Static(_)
+            | ModItem::Trait(_)
+            | ModItem::Impl(_)
+            | ModItem::Mod(_) => None,
+            ModItem::MacroCall(call) => Some(AssocItem::MacroCall(*call)),
+            ModItem::Const(konst) => Some(AssocItem::Const(*konst)),
+            ModItem::TypeAlias(alias) => Some(AssocItem::TypeAlias(*alias)),
+            ModItem::Function(func) => Some(AssocItem::Function(*func)),
+        }
+    }
+
+    pub fn downcast<N: ItemTreeNode>(self) -> Option<FileItemTreeId<N>> {
+        N::id_from_mod_item(self)
+    }
+}
+
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub enum AssocItem {
+    Function(FileItemTreeId<Function>),
+    TypeAlias(FileItemTreeId<TypeAlias>),
+    Const(FileItemTreeId<Const>),
+    MacroCall(FileItemTreeId<MacroCall>),
+}
+
+impl_froms!(AssocItem {
+    Function(FileItemTreeId<Function>),
+    TypeAlias(FileItemTreeId<TypeAlias>),
+    Const(FileItemTreeId<Const>),
+    MacroCall(FileItemTreeId<MacroCall>),
+});
+
+impl From<AssocItem> for ModItem {
+    fn from(item: AssocItem) -> Self {
+        match item {
+            AssocItem::Function(it) => it.into(),
+            AssocItem::TypeAlias(it) => it.into(),
+            AssocItem::Const(it) => it.into(),
+            AssocItem::MacroCall(it) => it.into(),
+        }
+    }
+}
+
+#[derive(Debug, Eq, PartialEq)]
+pub struct Variant {
+    pub name: Name,
+    pub fields: Fields,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum Fields {
+    Record(Range<Idx<Field>>),
+    Tuple(Range<Idx<Field>>),
+    Unit,
+}
+
+/// A single field of an enum variant or struct
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct Field {
+    pub name: Name,
+    pub type_ref: TypeRef,
+    pub visibility: RawVisibilityId,
+}
diff --git a/crates/ra_hir_def/src/item_tree/lower.rs b/crates/ra_hir_def/src/item_tree/lower.rs
new file mode 100644
index 0000000000..f10ad25f70
--- /dev/null
+++ b/crates/ra_hir_def/src/item_tree/lower.rs
@@ -0,0 +1,695 @@
+//! AST -> `ItemTree` lowering code.
+
+use super::*;
+use crate::{
+    attr::Attrs,
+    generics::{GenericParams, TypeParamData, TypeParamProvenance},
+};
+use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, HirFileId};
+use ra_arena::map::ArenaMap;
+use ra_syntax::{
+    ast::{self, ModuleItemOwner},
+    SyntaxNode,
+};
+use smallvec::SmallVec;
+use std::{collections::hash_map::Entry, mem, sync::Arc};
+
+fn id<N: ItemTreeNode>(index: Idx<N>) -> FileItemTreeId<N> {
+    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 {
+    tree: ItemTree,
+    hygiene: Hygiene,
+    file: HirFileId,
+    source_ast_id_map: Arc<AstIdMap>,
+    body_ctx: crate::body::LowerCtx,
+    inner_items: Vec<ModItem>,
+    forced_visibility: Option<RawVisibilityId>,
+}
+
+impl Ctx {
+    pub(super) fn new(db: &dyn DefDatabase, hygiene: Hygiene, file: HirFileId) -> Self {
+        Self {
+            tree: ItemTree::empty(),
+            hygiene,
+            file,
+            source_ast_id_map: db.ast_id_map(file),
+            body_ctx: crate::body::LowerCtx::new(db, file),
+            inner_items: Vec::new(),
+            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();
+        self.tree
+    }
+
+    pub(super) fn lower_inner_items(mut self, within: &SyntaxNode) -> ItemTree {
+        self.collect_inner_items(within);
+        self.tree
+    }
+
+    fn data(&mut self) -> &mut ItemTreeData {
+        self.tree.data_mut()
+    }
+
+    fn lower_mod_item(&mut self, item: &ast::ModuleItem, inner: bool) -> Option<ModItems> {
+        assert!(inner || self.inner_items.is_empty());
+
+        // Collect inner items for 1-to-1-lowered items.
+        match item {
+            ast::ModuleItem::StructDef(_)
+            | ast::ModuleItem::UnionDef(_)
+            | ast::ModuleItem::EnumDef(_)
+            | ast::ModuleItem::FnDef(_)
+            | ast::ModuleItem::TypeAliasDef(_)
+            | ast::ModuleItem::ConstDef(_)
+            | ast::ModuleItem::StaticDef(_)
+            | ast::ModuleItem::MacroCall(_) => {
+                // Skip this if we're already collecting inner items. We'll descend into all nodes
+                // already.
+                if !inner {
+                    self.collect_inner_items(item.syntax());
+                }
+            }
+
+            // These are handled in their respective `lower_X` method (since we can't just blindly
+            // walk them).
+            ast::ModuleItem::TraitDef(_)
+            | ast::ModuleItem::ImplDef(_)
+            | ast::ModuleItem::ExternBlock(_) => {}
+
+            // These don't have inner items.
+            ast::ModuleItem::Module(_)
+            | ast::ModuleItem::ExternCrateItem(_)
+            | ast::ModuleItem::UseItem(_) => {}
+        };
+
+        let attrs = Attrs::new(item, &self.hygiene);
+        let items = match item {
+            ast::ModuleItem::StructDef(ast) => self.lower_struct(ast).map(Into::into),
+            ast::ModuleItem::UnionDef(ast) => self.lower_union(ast).map(Into::into),
+            ast::ModuleItem::EnumDef(ast) => self.lower_enum(ast).map(Into::into),
+            ast::ModuleItem::FnDef(ast) => self.lower_function(ast).map(Into::into),
+            ast::ModuleItem::TypeAliasDef(ast) => self.lower_type_alias(ast).map(Into::into),
+            ast::ModuleItem::StaticDef(ast) => self.lower_static(ast).map(Into::into),
+            ast::ModuleItem::ConstDef(ast) => Some(self.lower_const(ast).into()),
+            ast::ModuleItem::Module(ast) => self.lower_module(ast).map(Into::into),
+            ast::ModuleItem::TraitDef(ast) => self.lower_trait(ast).map(Into::into),
+            ast::ModuleItem::ImplDef(ast) => self.lower_impl(ast).map(Into::into),
+            ast::ModuleItem::UseItem(ast) => Some(ModItems(
+                self.lower_use(ast).into_iter().map(Into::into).collect::<SmallVec<_>>(),
+            )),
+            ast::ModuleItem::ExternCrateItem(ast) => self.lower_extern_crate(ast).map(Into::into),
+            ast::ModuleItem::MacroCall(ast) => self.lower_macro_call(ast).map(Into::into),
+            ast::ModuleItem::ExternBlock(ast) => {
+                Some(ModItems(self.lower_extern_block(ast).into_iter().collect::<SmallVec<_>>()))
+            }
+        };
+
+        if !attrs.is_empty() {
+            for item in items.iter().flat_map(|items| &items.0) {
+                self.add_attrs(*item, attrs.clone());
+            }
+        }
+
+        items
+    }
+
+    fn add_attrs(&mut self, item: ModItem, attrs: Attrs) {
+        match self.tree.attrs.entry(AttrOwner::ModItem(item)) {
+            Entry::Occupied(mut entry) => {
+                *entry.get_mut() = entry.get().merge(attrs);
+            }
+            Entry::Vacant(entry) => {
+                entry.insert(attrs);
+            }
+        }
+    }
+
+    fn collect_inner_items(&mut self, container: &SyntaxNode) {
+        let forced_vis = self.forced_visibility.take();
+        let mut inner_items = mem::take(&mut self.tree.inner_items);
+        inner_items.extend(
+            container.descendants().skip(1).filter_map(ast::ModuleItem::cast).filter_map(|item| {
+                let ast_id = self.source_ast_id_map.ast_id(&item);
+                Some((ast_id, self.lower_mod_item(&item, true)?.0))
+            }),
+        );
+        self.tree.inner_items = inner_items;
+        self.forced_visibility = forced_vis;
+    }
+
+    fn lower_assoc_item(&mut self, item: &ast::ModuleItem) -> Option<AssocItem> {
+        match item {
+            ast::ModuleItem::FnDef(ast) => self.lower_function(ast).map(Into::into),
+            ast::ModuleItem::TypeAliasDef(ast) => self.lower_type_alias(ast).map(Into::into),
+            ast::ModuleItem::ConstDef(ast) => Some(self.lower_const(ast).into()),
+            ast::ModuleItem::MacroCall(ast) => self.lower_macro_call(ast).map(Into::into),
+            _ => None,
+        }
+    }
+
+    fn lower_struct(&mut self, strukt: &ast::StructDef) -> Option<FileItemTreeId<Struct>> {
+        let visibility = self.lower_visibility(strukt);
+        let name = strukt.name()?.as_name();
+        let generic_params = self.lower_generic_params(GenericsOwner::Struct, strukt);
+        let fields = self.lower_fields(&strukt.kind());
+        let ast_id = self.source_ast_id_map.ast_id(strukt);
+        let kind = match strukt.kind() {
+            ast::StructKind::Record(_) => StructDefKind::Record,
+            ast::StructKind::Tuple(_) => StructDefKind::Tuple,
+            ast::StructKind::Unit => StructDefKind::Unit,
+        };
+        let res = Struct { name, visibility, generic_params, fields, ast_id, kind };
+        Some(id(self.data().structs.alloc(res)))
+    }
+
+    fn lower_fields(&mut self, strukt_kind: &ast::StructKind) -> Fields {
+        match strukt_kind {
+            ast::StructKind::Record(it) => {
+                let range = self.lower_record_fields(it);
+                Fields::Record(range)
+            }
+            ast::StructKind::Tuple(it) => {
+                let range = self.lower_tuple_fields(it);
+                Fields::Tuple(range)
+            }
+            ast::StructKind::Unit => Fields::Unit,
+        }
+    }
+
+    fn lower_record_fields(&mut self, fields: &ast::RecordFieldDefList) -> Range<Idx<Field>> {
+        let start = self.next_field_idx();
+        for field in fields.fields() {
+            if let Some(data) = self.lower_record_field(&field) {
+                self.data().fields.alloc(data);
+            }
+        }
+        let end = self.next_field_idx();
+        start..end
+    }
+
+    fn lower_record_field(&mut self, field: &ast::RecordFieldDef) -> Option<Field> {
+        let name = field.name()?.as_name();
+        let visibility = self.lower_visibility(field);
+        let type_ref = self.lower_type_ref(&field.ascribed_type()?);
+        let res = Field { name, type_ref, visibility };
+        Some(res)
+    }
+
+    fn lower_tuple_fields(&mut self, fields: &ast::TupleFieldDefList) -> Range<Idx<Field>> {
+        let start = self.next_field_idx();
+        for (i, field) in fields.fields().enumerate() {
+            if let Some(data) = self.lower_tuple_field(i, &field) {
+                self.data().fields.alloc(data);
+            }
+        }
+        let end = self.next_field_idx();
+        start..end
+    }
+
+    fn lower_tuple_field(&mut self, idx: usize, field: &ast::TupleFieldDef) -> Option<Field> {
+        let name = Name::new_tuple_field(idx);
+        let visibility = self.lower_visibility(field);
+        let type_ref = self.lower_type_ref(&field.type_ref()?);
+        let res = Field { name, type_ref, visibility };
+        Some(res)
+    }
+
+    fn lower_union(&mut self, union: &ast::UnionDef) -> Option<FileItemTreeId<Union>> {
+        let visibility = self.lower_visibility(union);
+        let name = union.name()?.as_name();
+        let generic_params = self.lower_generic_params(GenericsOwner::Union, union);
+        let fields = match union.record_field_def_list() {
+            Some(record_field_def_list) => {
+                self.lower_fields(&StructKind::Record(record_field_def_list))
+            }
+            None => Fields::Record(self.next_field_idx()..self.next_field_idx()),
+        };
+        let ast_id = self.source_ast_id_map.ast_id(union);
+        let res = Union { name, visibility, generic_params, fields, ast_id };
+        Some(id(self.data().unions.alloc(res)))
+    }
+
+    fn lower_enum(&mut self, enum_: &ast::EnumDef) -> Option<FileItemTreeId<Enum>> {
+        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 => self.next_variant_idx()..self.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)))
+    }
+
+    fn lower_variants(&mut self, variants: &ast::EnumVariantList) -> Range<Idx<Variant>> {
+        let start = self.next_variant_idx();
+        for variant in variants.variants() {
+            if let Some(data) = self.lower_variant(&variant) {
+                self.data().variants.alloc(data);
+            }
+        }
+        let end = self.next_variant_idx();
+        start..end
+    }
+
+    fn lower_variant(&mut self, variant: &ast::EnumVariant) -> Option<Variant> {
+        let name = variant.name()?.as_name();
+        let fields = self.lower_fields(&variant.kind());
+        let res = Variant { name, fields };
+        Some(res)
+    }
+
+    fn lower_function(&mut self, func: &ast::FnDef) -> Option<FileItemTreeId<Function>> {
+        let visibility = self.lower_visibility(func);
+        let name = func.name()?.as_name();
+
+        let mut params = Vec::new();
+        let mut has_self_param = false;
+        if let Some(param_list) = func.param_list() {
+            if let Some(self_param) = param_list.self_param() {
+                let self_type = match self_param.ascribed_type() {
+                    Some(type_ref) => TypeRef::from_ast(&self.body_ctx, type_ref),
+                    None => {
+                        let self_type = TypeRef::Path(name![Self].into());
+                        match self_param.kind() {
+                            ast::SelfParamKind::Owned => self_type,
+                            ast::SelfParamKind::Ref => {
+                                TypeRef::Reference(Box::new(self_type), Mutability::Shared)
+                            }
+                            ast::SelfParamKind::MutRef => {
+                                TypeRef::Reference(Box::new(self_type), Mutability::Mut)
+                            }
+                        }
+                    }
+                };
+                params.push(self_type);
+                has_self_param = true;
+            }
+            for param in param_list.params() {
+                let type_ref = TypeRef::from_ast_opt(&self.body_ctx, param.ascribed_type());
+                params.push(type_ref);
+            }
+        }
+        let ret_type = match func.ret_type().and_then(|rt| rt.type_ref()) {
+            Some(type_ref) => TypeRef::from_ast(&self.body_ctx, type_ref),
+            _ => TypeRef::unit(),
+        };
+
+        let ret_type = if func.async_token().is_some() {
+            let future_impl = desugar_future_path(ret_type);
+            let ty_bound = TypeBound::Path(future_impl);
+            TypeRef::ImplTrait(vec![ty_bound])
+        } else {
+            ret_type
+        };
+
+        let ast_id = self.source_ast_id_map.ast_id(func);
+        let mut res = Function {
+            name,
+            visibility,
+            generic_params: GenericParamsId::EMPTY,
+            has_self_param,
+            is_unsafe: func.unsafe_token().is_some(),
+            params: params.into_boxed_slice(),
+            ret_type,
+            ast_id,
+        };
+        res.generic_params = self.lower_generic_params(GenericsOwner::Function(&res), func);
+
+        Some(id(self.data().functions.alloc(res)))
+    }
+
+    fn lower_type_alias(
+        &mut self,
+        type_alias: &ast::TypeAliasDef,
+    ) -> Option<FileItemTreeId<TypeAlias>> {
+        let name = type_alias.name()?.as_name();
+        let type_ref = type_alias.type_ref().map(|it| self.lower_type_ref(&it));
+        let visibility = self.lower_visibility(type_alias);
+        let bounds = self.lower_type_bounds(type_alias);
+        let generic_params = self.lower_generic_params(GenericsOwner::TypeAlias, type_alias);
+        let ast_id = self.source_ast_id_map.ast_id(type_alias);
+        let res = TypeAlias {
+            name,
+            visibility,
+            bounds: bounds.into_boxed_slice(),
+            generic_params,
+            type_ref,
+            ast_id,
+        };
+        Some(id(self.data().type_aliases.alloc(res)))
+    }
+
+    fn lower_static(&mut self, static_: &ast::StaticDef) -> Option<FileItemTreeId<Static>> {
+        let name = static_.name()?.as_name();
+        let type_ref = self.lower_type_ref_opt(static_.ascribed_type());
+        let visibility = self.lower_visibility(static_);
+        let mutable = static_.mut_token().is_some();
+        let ast_id = self.source_ast_id_map.ast_id(static_);
+        let res = Static { name, visibility, mutable, type_ref, ast_id };
+        Some(id(self.data().statics.alloc(res)))
+    }
+
+    fn lower_const(&mut self, konst: &ast::ConstDef) -> FileItemTreeId<Const> {
+        let name = konst.name().map(|it| it.as_name());
+        let type_ref = self.lower_type_ref_opt(konst.ascribed_type());
+        let visibility = self.lower_visibility(konst);
+        let ast_id = self.source_ast_id_map.ast_id(konst);
+        let res = Const { name, visibility, type_ref, ast_id };
+        id(self.data().consts.alloc(res))
+    }
+
+    fn lower_module(&mut self, module: &ast::Module) -> Option<FileItemTreeId<Mod>> {
+        let name = module.name()?.as_name();
+        let visibility = self.lower_visibility(module);
+        let kind = if module.semicolon_token().is_some() {
+            ModKind::Outline {}
+        } else {
+            ModKind::Inline {
+                items: module
+                    .item_list()
+                    .map(|list| {
+                        list.items()
+                            .flat_map(|item| self.lower_mod_item(&item, false))
+                            .flat_map(|items| items.0)
+                            .collect()
+                    })
+                    .unwrap_or_else(|| {
+                        mark::hit!(name_res_works_for_broken_modules);
+                        Box::new([]) as Box<[_]>
+                    }),
+            }
+        };
+        let ast_id = self.source_ast_id_map.ast_id(module);
+        let res = Mod { name, visibility, kind, ast_id };
+        Some(id(self.data().mods.alloc(res)))
+    }
+
+    fn lower_trait(&mut self, trait_def: &ast::TraitDef) -> Option<FileItemTreeId<Trait>> {
+        let name = trait_def.name()?.as_name();
+        let visibility = self.lower_visibility(trait_def);
+        let generic_params =
+            self.lower_generic_params_and_inner_items(GenericsOwner::Trait(trait_def), trait_def);
+        let auto = trait_def.auto_token().is_some();
+        let items = trait_def.item_list().map(|list| {
+            self.with_inherited_visibility(visibility, |this| {
+                list.items()
+                    .filter_map(|item| {
+                        let attrs = Attrs::new(&item, &this.hygiene);
+                        this.collect_inner_items(item.syntax());
+                        this.lower_assoc_item(&item).map(|item| {
+                            this.add_attrs(item.into(), attrs);
+                            item
+                        })
+                    })
+                    .collect()
+            })
+        });
+        let ast_id = self.source_ast_id_map.ast_id(trait_def);
+        let res = Trait {
+            name,
+            visibility,
+            generic_params,
+            auto,
+            items: items.unwrap_or_default(),
+            ast_id,
+        };
+        Some(id(self.data().traits.alloc(res)))
+    }
+
+    fn lower_impl(&mut self, impl_def: &ast::ImplDef) -> Option<FileItemTreeId<Impl>> {
+        let generic_params =
+            self.lower_generic_params_and_inner_items(GenericsOwner::Impl, impl_def);
+        let target_trait = impl_def.target_trait().map(|tr| self.lower_type_ref(&tr));
+        let target_type = self.lower_type_ref(&impl_def.target_type()?);
+        let is_negative = impl_def.excl_token().is_some();
+
+        // We cannot use `assoc_items()` here as that does not include macro calls.
+        let items = impl_def
+            .item_list()?
+            .items()
+            .filter_map(|item| {
+                self.collect_inner_items(item.syntax());
+                let assoc = self.lower_assoc_item(&item)?;
+                let attrs = Attrs::new(&item, &self.hygiene);
+                self.add_attrs(assoc.into(), attrs);
+                Some(assoc)
+            })
+            .collect();
+        let ast_id = self.source_ast_id_map.ast_id(impl_def);
+        let res = Impl { generic_params, target_trait, target_type, is_negative, items, ast_id };
+        Some(id(self.data().impls.alloc(res)))
+    }
+
+    fn lower_use(&mut self, use_item: &ast::UseItem) -> Vec<FileItemTreeId<Import>> {
+        // FIXME: cfg_attr
+        let is_prelude = use_item.has_atom_attr("prelude_import");
+        let visibility = self.lower_visibility(use_item);
+        let ast_id = self.source_ast_id_map.ast_id(use_item);
+
+        // Every use item can expand to many `Import`s.
+        let mut imports = Vec::new();
+        let tree = self.tree.data_mut();
+        ModPath::expand_use_item(
+            InFile::new(self.file, use_item.clone()),
+            &self.hygiene,
+            |path, _tree, is_glob, alias| {
+                imports.push(id(tree.imports.alloc(Import {
+                    path,
+                    alias,
+                    visibility,
+                    is_glob,
+                    is_prelude,
+                    ast_id,
+                })));
+            },
+        );
+
+        imports
+    }
+
+    fn lower_extern_crate(
+        &mut self,
+        extern_crate: &ast::ExternCrateItem,
+    ) -> Option<FileItemTreeId<ExternCrate>> {
+        let path = ModPath::from_name_ref(&extern_crate.name_ref()?);
+        let alias = extern_crate.alias().map(|a| {
+            a.name().map(|it| it.as_name()).map_or(ImportAlias::Underscore, ImportAlias::Alias)
+        });
+        let visibility = self.lower_visibility(extern_crate);
+        let ast_id = self.source_ast_id_map.ast_id(extern_crate);
+        // FIXME: cfg_attr
+        let is_macro_use = extern_crate.has_atom_attr("macro_use");
+
+        let res = ExternCrate { path, alias, visibility, is_macro_use, ast_id };
+        Some(id(self.data().extern_crates.alloc(res)))
+    }
+
+    fn lower_macro_call(&mut self, m: &ast::MacroCall) -> Option<FileItemTreeId<MacroCall>> {
+        let name = m.name().map(|it| it.as_name());
+        let attrs = Attrs::new(m, &self.hygiene);
+        let path = ModPath::from_src(m.path()?, &self.hygiene)?;
+
+        let ast_id = self.source_ast_id_map.ast_id(m);
+
+        // FIXME: cfg_attr
+        let export_attr = attrs.by_key("macro_export");
+
+        let is_export = export_attr.exists();
+        let is_local_inner = if is_export {
+            export_attr.tt_values().map(|it| &it.token_trees).flatten().any(|it| match it {
+                tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => {
+                    ident.text.contains("local_inner_macros")
+                }
+                _ => false,
+            })
+        } else {
+            false
+        };
+
+        let is_builtin = attrs.by_key("rustc_builtin_macro").exists();
+        let res = MacroCall { name, path, is_export, is_builtin, is_local_inner, ast_id };
+        Some(id(self.data().macro_calls.alloc(res)))
+    }
+
+    fn lower_extern_block(&mut self, block: &ast::ExternBlock) -> Vec<ModItem> {
+        block.extern_item_list().map_or(Vec::new(), |list| {
+            list.extern_items()
+                .filter_map(|item| {
+                    self.collect_inner_items(item.syntax());
+                    let attrs = Attrs::new(&item, &self.hygiene);
+                    let id = match item {
+                        ast::ExternItem::FnDef(ast) => {
+                            let func = self.lower_function(&ast)?;
+                            func.into()
+                        }
+                        ast::ExternItem::StaticDef(ast) => {
+                            let statik = self.lower_static(&ast)?;
+                            statik.into()
+                        }
+                    };
+                    self.add_attrs(id, attrs);
+                    Some(id)
+                })
+                .collect()
+        })
+    }
+
+    /// Lowers generics defined on `node` and collects inner items defined within.
+    fn lower_generic_params_and_inner_items(
+        &mut self,
+        owner: GenericsOwner<'_>,
+        node: &impl ast::TypeParamsOwner,
+    ) -> GenericParamsId {
+        // Generics are part of item headers and may contain inner items we need to collect.
+        if let Some(params) = node.type_param_list() {
+            self.collect_inner_items(params.syntax());
+        }
+        if let Some(clause) = node.where_clause() {
+            self.collect_inner_items(clause.syntax());
+        }
+
+        self.lower_generic_params(owner, node)
+    }
+
+    fn lower_generic_params(
+        &mut self,
+        owner: GenericsOwner<'_>,
+        node: &impl ast::TypeParamsOwner,
+    ) -> GenericParamsId {
+        let mut sm = &mut ArenaMap::default();
+        let mut generics = GenericParams::default();
+        match owner {
+            GenericsOwner::Function(func) => {
+                generics.fill(&self.body_ctx, sm, node);
+                // lower `impl Trait` in arguments
+                for param in &*func.params {
+                    generics.fill_implicit_impl_trait_args(param);
+                }
+            }
+            GenericsOwner::Struct
+            | GenericsOwner::Enum
+            | GenericsOwner::Union
+            | GenericsOwner::TypeAlias => {
+                generics.fill(&self.body_ctx, sm, node);
+            }
+            GenericsOwner::Trait(trait_def) => {
+                // traits get the Self type as an implicit first type parameter
+                let self_param_id = generics.types.alloc(TypeParamData {
+                    name: Some(name![Self]),
+                    default: None,
+                    provenance: TypeParamProvenance::TraitSelf,
+                });
+                sm.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, self_param);
+
+                generics.fill(&self.body_ctx, &mut sm, 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);
+            }
+        }
+
+        self.data().generics.alloc(generics)
+    }
+
+    fn lower_type_bounds(&mut self, node: &impl ast::TypeBoundsOwner) -> Vec<TypeBound> {
+        match node.type_bound_list() {
+            Some(bound_list) => {
+                bound_list.bounds().map(|it| TypeBound::from_ast(&self.body_ctx, it)).collect()
+            }
+            None => Vec::new(),
+        }
+    }
+
+    fn lower_visibility(&mut self, item: &impl ast::VisibilityOwner) -> RawVisibilityId {
+        let vis = match self.forced_visibility {
+            Some(vis) => return vis,
+            None => RawVisibility::from_ast_with_hygiene(item.visibility(), &self.hygiene),
+        };
+
+        self.data().vis.alloc(vis)
+    }
+
+    fn lower_type_ref(&self, type_ref: &ast::TypeRef) -> TypeRef {
+        TypeRef::from_ast(&self.body_ctx, type_ref.clone())
+    }
+    fn lower_type_ref_opt(&self, type_ref: Option<ast::TypeRef>) -> TypeRef {
+        type_ref.map(|ty| self.lower_type_ref(&ty)).unwrap_or(TypeRef::Error)
+    }
+
+    /// Forces the visibility `vis` to be used for all items lowered during execution of `f`.
+    fn with_inherited_visibility<R>(
+        &mut self,
+        vis: RawVisibilityId,
+        f: impl FnOnce(&mut Self) -> R,
+    ) -> R {
+        let old = mem::replace(&mut self.forced_visibility, Some(vis));
+        let res = f(self);
+        self.forced_visibility = old;
+        res
+    }
+
+    fn next_field_idx(&self) -> Idx<Field> {
+        Idx::from_raw(RawId::from(
+            self.tree.data.as_ref().map_or(0, |data| data.fields.len() as u32),
+        ))
+    }
+    fn next_variant_idx(&self) -> Idx<Variant> {
+        Idx::from_raw(RawId::from(
+            self.tree.data.as_ref().map_or(0, |data| data.variants.len() as u32),
+        ))
+    }
+}
+
+fn desugar_future_path(orig: TypeRef) -> Path {
+    let path = path![core::future::Future];
+    let mut generic_args: Vec<_> = std::iter::repeat(None).take(path.segments.len() - 1).collect();
+    let mut last = GenericArgs::empty();
+    let binding =
+        AssociatedTypeBinding { name: name![Output], type_ref: Some(orig), bounds: Vec::new() };
+    last.bindings.push(binding);
+    generic_args.push(Some(Arc::new(last)));
+
+    Path::from_known_path(path, generic_args)
+}
+
+enum GenericsOwner<'a> {
+    /// We need access to the partially-lowered `Function` for lowering `impl Trait` in argument
+    /// position.
+    Function(&'a Function),
+    Struct,
+    Enum,
+    Union,
+    /// The `TraitDef` is needed to fill the source map for the implicit `Self` parameter.
+    Trait(&'a ast::TraitDef),
+    TypeAlias,
+    Impl,
+}
diff --git a/crates/ra_hir_def/src/lib.rs b/crates/ra_hir_def/src/lib.rs
index af2a717c9d..564434cccf 100644
--- a/crates/ra_hir_def/src/lib.rs
+++ b/crates/ra_hir_def/src/lib.rs
@@ -25,6 +25,8 @@ pub mod item_scope;
 pub mod dyn_map;
 pub mod keys;
 
+pub mod item_tree;
+
 pub mod adt;
 pub mod data;
 pub mod generics;
@@ -48,7 +50,7 @@ pub mod import_map;
 #[cfg(test)]
 mod test_db;
 
-use std::hash::Hash;
+use std::hash::{Hash, Hasher};
 
 use hir_expand::{
     ast_id_map::FileAstId, eager::expand_eager_macro, hygiene::Hygiene, AstId, HirFileId, InFile,
@@ -56,10 +58,13 @@ use hir_expand::{
 };
 use ra_arena::Idx;
 use ra_db::{impl_intern_key, salsa, CrateId};
-use ra_syntax::{ast, AstNode};
+use ra_syntax::ast;
 
-use crate::body::Expander;
 use crate::builtin_type::BuiltinType;
+use item_tree::{
+    Const, Enum, Function, Impl, ItemTreeId, ItemTreeNode, ModItem, Static, Struct, Trait,
+    TypeAlias, Union,
+};
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct ModuleId {
@@ -70,16 +75,62 @@ pub struct ModuleId {
 /// An ID of a module, **local** to a specific crate
 pub type LocalModuleId = Idx<nameres::ModuleData>;
 
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub struct ItemLoc<N: AstNode> {
+#[derive(Debug)]
+pub struct ItemLoc<N: ItemTreeNode> {
     pub container: ContainerId,
-    pub ast_id: AstId<N>,
+    pub id: ItemTreeId<N>,
+}
+
+impl<N: ItemTreeNode> Clone for ItemLoc<N> {
+    fn clone(&self) -> Self {
+        Self { container: self.container, id: self.id }
+    }
 }
 
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub struct AssocItemLoc<N: AstNode> {
+impl<N: ItemTreeNode> Copy for ItemLoc<N> {}
+
+impl<N: ItemTreeNode> PartialEq for ItemLoc<N> {
+    fn eq(&self, other: &Self) -> bool {
+        self.container == other.container && self.id == other.id
+    }
+}
+
+impl<N: ItemTreeNode> Eq for ItemLoc<N> {}
+
+impl<N: ItemTreeNode> Hash for ItemLoc<N> {
+    fn hash<H: Hasher>(&self, state: &mut H) {
+        self.container.hash(state);
+        self.id.hash(state);
+    }
+}
+
+#[derive(Debug)]
+pub struct AssocItemLoc<N: ItemTreeNode> {
     pub container: AssocContainerId,
-    pub ast_id: AstId<N>,
+    pub id: ItemTreeId<N>,
+}
+
+impl<N: ItemTreeNode> Clone for AssocItemLoc<N> {
+    fn clone(&self) -> Self {
+        Self { container: self.container, id: self.id }
+    }
+}
+
+impl<N: ItemTreeNode> Copy for AssocItemLoc<N> {}
+
+impl<N: ItemTreeNode> PartialEq for AssocItemLoc<N> {
+    fn eq(&self, other: &Self) -> bool {
+        self.container == other.container && self.id == other.id
+    }
+}
+
+impl<N: ItemTreeNode> Eq for AssocItemLoc<N> {}
+
+impl<N: ItemTreeNode> Hash for AssocItemLoc<N> {
+    fn hash<H: Hasher>(&self, state: &mut H) {
+        self.container.hash(state);
+        self.id.hash(state);
+    }
 }
 
 macro_rules! impl_intern {
@@ -104,22 +155,22 @@ macro_rules! impl_intern {
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct FunctionId(salsa::InternId);
-type FunctionLoc = AssocItemLoc<ast::FnDef>;
+type FunctionLoc = AssocItemLoc<Function>;
 impl_intern!(FunctionId, FunctionLoc, intern_function, lookup_intern_function);
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct StructId(salsa::InternId);
-type StructLoc = ItemLoc<ast::StructDef>;
+type StructLoc = ItemLoc<Struct>;
 impl_intern!(StructId, StructLoc, intern_struct, lookup_intern_struct);
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct UnionId(salsa::InternId);
-pub type UnionLoc = ItemLoc<ast::UnionDef>;
+pub type UnionLoc = ItemLoc<Union>;
 impl_intern!(UnionId, UnionLoc, intern_union, lookup_intern_union);
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct EnumId(salsa::InternId);
-pub type EnumLoc = ItemLoc<ast::EnumDef>;
+pub type EnumLoc = ItemLoc<Enum>;
 impl_intern!(EnumId, EnumLoc, intern_enum, lookup_intern_enum);
 
 // FIXME: rename to `VariantId`, only enums can ave variants
@@ -141,27 +192,27 @@ pub type LocalFieldId = Idx<adt::FieldData>;
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct ConstId(salsa::InternId);
-type ConstLoc = AssocItemLoc<ast::ConstDef>;
+type ConstLoc = AssocItemLoc<Const>;
 impl_intern!(ConstId, ConstLoc, intern_const, lookup_intern_const);
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct StaticId(salsa::InternId);
-pub type StaticLoc = ItemLoc<ast::StaticDef>;
+pub type StaticLoc = ItemLoc<Static>;
 impl_intern!(StaticId, StaticLoc, intern_static, lookup_intern_static);
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct TraitId(salsa::InternId);
-pub type TraitLoc = ItemLoc<ast::TraitDef>;
+pub type TraitLoc = ItemLoc<Trait>;
 impl_intern!(TraitId, TraitLoc, intern_trait, lookup_intern_trait);
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct TypeAliasId(salsa::InternId);
-type TypeAliasLoc = AssocItemLoc<ast::TypeAliasDef>;
+type TypeAliasLoc = AssocItemLoc<TypeAlias>;
 impl_intern!(TypeAliasId, TypeAliasLoc, intern_type_alias, lookup_intern_type_alias);
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
 pub struct ImplId(salsa::InternId);
-type ImplLoc = ItemLoc<ast::ImplDef>;
+type ImplLoc = ItemLoc<Impl>;
 impl_intern!(ImplId, ImplLoc, intern_impl, lookup_intern_impl);
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -365,7 +416,7 @@ impl HasModule for AssocContainerId {
     }
 }
 
-impl<N: AstNode> HasModule for AssocItemLoc<N> {
+impl<N: ItemTreeNode> HasModule for AssocItemLoc<N> {
     fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
         self.container.module(db)
     }
@@ -392,6 +443,16 @@ impl HasModule for DefWithBodyId {
     }
 }
 
+impl DefWithBodyId {
+    pub fn as_mod_item(self, db: &dyn db::DefDatabase) -> ModItem {
+        match self {
+            DefWithBodyId::FunctionId(it) => it.lookup(db).id.value.into(),
+            DefWithBodyId::StaticId(it) => it.lookup(db).id.value.into(),
+            DefWithBodyId::ConstId(it) => it.lookup(db).id.value.into(),
+        }
+    }
+}
+
 impl HasModule for GenericDefId {
     fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
         match self {
diff --git a/crates/ra_hir_def/src/nameres.rs b/crates/ra_hir_def/src/nameres.rs
index 060273db40..b279bdeef6 100644
--- a/crates/ra_hir_def/src/nameres.rs
+++ b/crates/ra_hir_def/src/nameres.rs
@@ -47,7 +47,6 @@
 //! path and, upon success, we run macro expansion and "collect module" phase on
 //! the result
 
-pub(crate) mod raw;
 mod collector;
 mod mod_resolution;
 mod path_resolution;
diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs
index cbce043159..94da700ad3 100644
--- a/crates/ra_hir_def/src/nameres/collector.rs
+++ b/crates/ra_hir_def/src/nameres/collector.rs
@@ -4,6 +4,7 @@
 //! resolves imports and expands macros.
 
 use hir_expand::{
+    ast_id_map::FileAstId,
     builtin_derive::find_builtin_derive,
     builtin_macro::find_builtin_macro,
     name::{name, AsName, Name},
@@ -19,13 +20,16 @@ use test_utils::mark;
 use crate::{
     attr::Attrs,
     db::DefDatabase,
+    item_tree::{
+        self, FileItemTreeId, ItemTree, ItemTreeId, MacroCall, Mod, ModItem, ModKind, StructDefKind,
+    },
     nameres::{
         diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint,
-        raw, BuiltinShadowMode, CrateDefMap, ModuleData, ModuleOrigin, ResolveMode,
+        BuiltinShadowMode, CrateDefMap, ModuleData, ModuleOrigin, ResolveMode,
     },
     path::{ImportAlias, ModPath, PathKind},
     per_ns::PerNs,
-    visibility::Visibility,
+    visibility::{RawVisibility, Visibility},
     AdtId, AsMacroCall, AstId, AstIdWithPath, ConstLoc, ContainerId, EnumLoc, EnumVariantId,
     FunctionLoc, ImplLoc, Intern, LocalModuleId, ModuleDefId, ModuleId, StaticLoc, StructLoc,
     TraitLoc, TypeAliasLoc, UnionLoc,
@@ -101,11 +105,51 @@ impl PartialResolvedImport {
     }
 }
 
+#[derive(Clone, Debug, Eq, PartialEq)]
+struct Import {
+    pub path: ModPath,
+    pub alias: Option<ImportAlias>,
+    pub visibility: RawVisibility,
+    pub is_glob: bool,
+    pub is_prelude: bool,
+    pub is_extern_crate: bool,
+    pub is_macro_use: bool,
+}
+
+impl Import {
+    fn from_use(tree: &ItemTree, id: FileItemTreeId<item_tree::Import>) -> Self {
+        let it = &tree[id];
+        let visibility = &tree[it.visibility];
+        Self {
+            path: it.path.clone(),
+            alias: it.alias.clone(),
+            visibility: visibility.clone(),
+            is_glob: it.is_glob,
+            is_prelude: it.is_prelude,
+            is_extern_crate: false,
+            is_macro_use: false,
+        }
+    }
+
+    fn from_extern_crate(tree: &ItemTree, id: FileItemTreeId<item_tree::ExternCrate>) -> Self {
+        let it = &tree[id];
+        let visibility = &tree[it.visibility];
+        Self {
+            path: it.path.clone(),
+            alias: it.alias.clone(),
+            visibility: visibility.clone(),
+            is_glob: false,
+            is_prelude: false,
+            is_extern_crate: true,
+            is_macro_use: it.is_macro_use,
+        }
+    }
+}
+
 #[derive(Clone, Debug, Eq, PartialEq)]
 struct ImportDirective {
     module_id: LocalModuleId,
-    import_id: raw::Import,
-    import: raw::ImportData,
+    import: Import,
     status: PartialResolvedImport,
 }
 
@@ -123,6 +167,13 @@ struct DeriveDirective {
     ast_id: AstIdWithPath<ast::ModuleItem>,
 }
 
+struct DefData<'a> {
+    id: ModuleDefId,
+    name: &'a Name,
+    visibility: &'a RawVisibility,
+    has_constructor: bool,
+}
+
 /// Walks the tree of module recursively
 struct DefCollector<'a> {
     db: &'a dyn DefDatabase,
@@ -140,7 +191,7 @@ struct DefCollector<'a> {
 impl DefCollector<'_> {
     fn collect(&mut self) {
         let file_id = self.db.crate_graph()[self.def_map.krate].root_file_id;
-        let raw_items = self.db.raw_items(file_id.into());
+        let item_tree = self.db.item_tree(file_id.into());
         let module_id = self.def_map.root;
         self.def_map.modules[module_id].origin = ModuleOrigin::CrateRoot { definition: file_id };
         ModCollector {
@@ -148,10 +199,10 @@ impl DefCollector<'_> {
             macro_depth: 0,
             module_id,
             file_id: file_id.into(),
-            raw_items: &raw_items,
+            item_tree: &item_tree,
             mod_dir: ModDir::root(),
         }
-        .collect(raw_items.items());
+        .collect(item_tree.top_level_items());
 
         // main name resolution fixed-point loop.
         let mut i = 0;
@@ -286,7 +337,7 @@ impl DefCollector<'_> {
     fn import_macros_from_extern_crate(
         &mut self,
         current_module_id: LocalModuleId,
-        import: &raw::ImportData,
+        import: &item_tree::ExternCrate,
     ) {
         log::debug!(
             "importing macros from extern crate: {:?} ({:?})",
@@ -352,11 +403,7 @@ impl DefCollector<'_> {
         }
     }
 
-    fn resolve_import(
-        &self,
-        module_id: LocalModuleId,
-        import: &raw::ImportData,
-    ) -> PartialResolvedImport {
+    fn resolve_import(&self, module_id: LocalModuleId, import: &Import) -> PartialResolvedImport {
         log::debug!("resolving import: {:?} ({:?})", import, self.def_map.edition);
         if import.is_extern_crate {
             let res = self.def_map.resolve_name_in_extern_prelude(
@@ -649,17 +696,17 @@ impl DefCollector<'_> {
         depth: usize,
     ) {
         let file_id: HirFileId = macro_call_id.as_file();
-        let raw_items = self.db.raw_items(file_id);
+        let item_tree = self.db.item_tree(file_id);
         let mod_dir = self.mod_dirs[&module_id].clone();
         ModCollector {
             def_collector: &mut *self,
             macro_depth: depth,
             file_id,
             module_id,
-            raw_items: &raw_items,
+            item_tree: &item_tree,
             mod_dir,
         }
-        .collect(raw_items.items());
+        .collect(item_tree.top_level_items());
     }
 
     fn finish(self) -> CrateDefMap {
@@ -673,12 +720,12 @@ struct ModCollector<'a, 'b> {
     macro_depth: usize,
     module_id: LocalModuleId,
     file_id: HirFileId,
-    raw_items: &'a raw::RawItems,
+    item_tree: &'a ItemTree,
     mod_dir: ModDir,
 }
 
 impl ModCollector<'_, '_> {
-    fn collect(&mut self, items: &[raw::RawItem]) {
+    fn collect(&mut self, items: &[ModItem]) {
         // Note: don't assert that inserted value is fresh: it's simply not true
         // for macros.
         self.def_collector.mod_dirs.insert(self.module_id, self.mod_dir.clone());
@@ -695,64 +742,204 @@ impl ModCollector<'_, '_> {
         // `#[macro_use] extern crate` is hoisted to imports macros before collecting
         // any other items.
         for item in items {
-            if self.is_cfg_enabled(&item.attrs) {
-                if let raw::RawItemKind::Import(import_id) = item.kind {
-                    let import = self.raw_items[import_id].clone();
-                    if import.is_extern_crate && import.is_macro_use {
+            if self.is_cfg_enabled(self.item_tree.attrs(*item)) {
+                if let ModItem::ExternCrate(id) = item {
+                    let import = self.item_tree[*id].clone();
+                    if import.is_macro_use {
                         self.def_collector.import_macros_from_extern_crate(self.module_id, &import);
                     }
                 }
             }
         }
 
-        for item in items {
-            if self.is_cfg_enabled(&item.attrs) {
-                match item.kind {
-                    raw::RawItemKind::Module(m) => {
-                        self.collect_module(&self.raw_items[m], &item.attrs)
-                    }
-                    raw::RawItemKind::Import(import_id) => {
+        for &item in items {
+            let attrs = self.item_tree.attrs(item);
+            if self.is_cfg_enabled(attrs) {
+                let module =
+                    ModuleId { krate: self.def_collector.def_map.krate, local_id: self.module_id };
+                let container = ContainerId::ModuleId(module);
+
+                let mut def = None;
+                match item {
+                    ModItem::Mod(m) => self.collect_module(&self.item_tree[m], attrs),
+                    ModItem::Import(import_id) => {
                         self.def_collector.unresolved_imports.push(ImportDirective {
                             module_id: self.module_id,
-                            import_id,
-                            import: self.raw_items[import_id].clone(),
+                            import: Import::from_use(&self.item_tree, import_id),
                             status: PartialResolvedImport::Unresolved,
                         })
                     }
-                    raw::RawItemKind::Def(def) => {
-                        self.define_def(&self.raw_items[def], &item.attrs)
+                    ModItem::ExternCrate(import_id) => {
+                        self.def_collector.unresolved_imports.push(ImportDirective {
+                            module_id: self.module_id,
+                            import: Import::from_extern_crate(&self.item_tree, import_id),
+                            status: PartialResolvedImport::Unresolved,
+                        })
                     }
-                    raw::RawItemKind::Macro(mac) => self.collect_macro(&self.raw_items[mac]),
-                    raw::RawItemKind::Impl(imp) => {
+                    ModItem::MacroCall(mac) => self.collect_macro(&self.item_tree[mac]),
+                    ModItem::Impl(imp) => {
                         let module = ModuleId {
                             krate: self.def_collector.def_map.krate,
                             local_id: self.module_id,
                         };
                         let container = ContainerId::ModuleId(module);
-                        let ast_id = self.raw_items[imp].ast_id;
-                        let impl_id =
-                            ImplLoc { container, ast_id: AstId::new(self.file_id, ast_id) }
-                                .intern(self.def_collector.db);
+                        let impl_id = ImplLoc { container, id: ItemTreeId::new(self.file_id, imp) }
+                            .intern(self.def_collector.db);
                         self.def_collector.def_map.modules[self.module_id]
                             .scope
                             .define_impl(impl_id)
                     }
+                    ModItem::Function(id) => {
+                        let func = &self.item_tree[id];
+                        def = Some(DefData {
+                            id: FunctionLoc {
+                                container: container.into(),
+                                id: ItemTreeId::new(self.file_id, id),
+                            }
+                            .intern(self.def_collector.db)
+                            .into(),
+                            name: &func.name,
+                            visibility: &self.item_tree[func.visibility],
+                            has_constructor: false,
+                        });
+                    }
+                    ModItem::Struct(id) => {
+                        let it = &self.item_tree[id];
+
+                        // FIXME: check attrs to see if this is an attribute macro invocation;
+                        // in which case we don't add the invocation, just a single attribute
+                        // macro invocation
+                        self.collect_derives(attrs, it.ast_id.upcast());
+
+                        def = Some(DefData {
+                            id: StructLoc { container, id: ItemTreeId::new(self.file_id, id) }
+                                .intern(self.def_collector.db)
+                                .into(),
+                            name: &it.name,
+                            visibility: &self.item_tree[it.visibility],
+                            has_constructor: it.kind != StructDefKind::Record,
+                        });
+                    }
+                    ModItem::Union(id) => {
+                        let it = &self.item_tree[id];
+
+                        // FIXME: check attrs to see if this is an attribute macro invocation;
+                        // in which case we don't add the invocation, just a single attribute
+                        // macro invocation
+                        self.collect_derives(attrs, it.ast_id.upcast());
+
+                        def = Some(DefData {
+                            id: UnionLoc { container, id: ItemTreeId::new(self.file_id, id) }
+                                .intern(self.def_collector.db)
+                                .into(),
+                            name: &it.name,
+                            visibility: &self.item_tree[it.visibility],
+                            has_constructor: false,
+                        });
+                    }
+                    ModItem::Enum(id) => {
+                        let it = &self.item_tree[id];
+
+                        // FIXME: check attrs to see if this is an attribute macro invocation;
+                        // in which case we don't add the invocation, just a single attribute
+                        // macro invocation
+                        self.collect_derives(attrs, it.ast_id.upcast());
+
+                        def = Some(DefData {
+                            id: EnumLoc { container, id: ItemTreeId::new(self.file_id, id) }
+                                .intern(self.def_collector.db)
+                                .into(),
+                            name: &it.name,
+                            visibility: &self.item_tree[it.visibility],
+                            has_constructor: false,
+                        });
+                    }
+                    ModItem::Const(id) => {
+                        let it = &self.item_tree[id];
+
+                        if let Some(name) = &it.name {
+                            def = Some(DefData {
+                                id: ConstLoc {
+                                    container: container.into(),
+                                    id: ItemTreeId::new(self.file_id, id),
+                                }
+                                .intern(self.def_collector.db)
+                                .into(),
+                                name,
+                                visibility: &self.item_tree[it.visibility],
+                                has_constructor: false,
+                            });
+                        }
+                    }
+                    ModItem::Static(id) => {
+                        let it = &self.item_tree[id];
+
+                        def = Some(DefData {
+                            id: StaticLoc { container, id: ItemTreeId::new(self.file_id, id) }
+                                .intern(self.def_collector.db)
+                                .into(),
+                            name: &it.name,
+                            visibility: &self.item_tree[it.visibility],
+                            has_constructor: false,
+                        });
+                    }
+                    ModItem::Trait(id) => {
+                        let it = &self.item_tree[id];
+
+                        def = Some(DefData {
+                            id: TraitLoc { container, id: ItemTreeId::new(self.file_id, id) }
+                                .intern(self.def_collector.db)
+                                .into(),
+                            name: &it.name,
+                            visibility: &self.item_tree[it.visibility],
+                            has_constructor: false,
+                        });
+                    }
+                    ModItem::TypeAlias(id) => {
+                        let it = &self.item_tree[id];
+
+                        def = Some(DefData {
+                            id: TypeAliasLoc {
+                                container: container.into(),
+                                id: ItemTreeId::new(self.file_id, id),
+                            }
+                            .intern(self.def_collector.db)
+                            .into(),
+                            name: &it.name,
+                            visibility: &self.item_tree[it.visibility],
+                            has_constructor: false,
+                        });
+                    }
+                }
+
+                if let Some(DefData { id, name, visibility, has_constructor }) = def {
+                    self.def_collector.def_map.modules[self.module_id].scope.define_def(id);
+                    let vis = self
+                        .def_collector
+                        .def_map
+                        .resolve_visibility(self.def_collector.db, self.module_id, visibility)
+                        .unwrap_or(Visibility::Public);
+                    self.def_collector.update(
+                        self.module_id,
+                        &[(name.clone(), PerNs::from_def(id, vis, has_constructor))],
+                        vis,
+                    )
                 }
             }
         }
     }
 
-    fn collect_module(&mut self, module: &raw::ModuleData, attrs: &Attrs) {
+    fn collect_module(&mut self, module: &Mod, attrs: &Attrs) {
         let path_attr = attrs.by_key("path").string_value();
         let is_macro_use = attrs.by_key("macro_use").exists();
-        match module {
+        match &module.kind {
             // inline module, just recurse
-            raw::ModuleData::Definition { name, visibility, items, ast_id } => {
+            ModKind::Inline { items } => {
                 let module_id = self.push_child_module(
-                    name.clone(),
-                    AstId::new(self.file_id, *ast_id),
+                    module.name.clone(),
+                    AstId::new(self.file_id, module.ast_id),
                     None,
-                    &visibility,
+                    &self.item_tree[module.visibility],
                 );
 
                 ModCollector {
@@ -760,8 +947,8 @@ impl ModCollector<'_, '_> {
                     macro_depth: self.macro_depth,
                     module_id,
                     file_id: self.file_id,
-                    raw_items: self.raw_items,
-                    mod_dir: self.mod_dir.descend_into_definition(name, path_attr),
+                    item_tree: self.item_tree,
+                    mod_dir: self.mod_dir.descend_into_definition(&module.name, path_attr),
                 }
                 .collect(&*items);
                 if is_macro_use {
@@ -769,31 +956,31 @@ impl ModCollector<'_, '_> {
                 }
             }
             // out of line module, resolve, parse and recurse
-            raw::ModuleData::Declaration { name, visibility, ast_id } => {
-                let ast_id = AstId::new(self.file_id, *ast_id);
+            ModKind::Outline {} => {
+                let ast_id = AstId::new(self.file_id, module.ast_id);
                 match self.mod_dir.resolve_declaration(
                     self.def_collector.db,
                     self.file_id,
-                    name,
+                    &module.name,
                     path_attr,
                 ) {
                     Ok((file_id, is_mod_rs, mod_dir)) => {
                         let module_id = self.push_child_module(
-                            name.clone(),
+                            module.name.clone(),
                             ast_id,
                             Some((file_id, is_mod_rs)),
-                            &visibility,
+                            &self.item_tree[module.visibility],
                         );
-                        let raw_items = self.def_collector.db.raw_items(file_id.into());
+                        let item_tree = self.def_collector.db.item_tree(file_id.into());
                         ModCollector {
                             def_collector: &mut *self.def_collector,
                             macro_depth: self.macro_depth,
                             module_id,
                             file_id: file_id.into(),
-                            raw_items: &raw_items,
+                            item_tree: &item_tree,
                             mod_dir,
                         }
-                        .collect(raw_items.items());
+                        .collect(item_tree.top_level_items());
                         if is_macro_use {
                             self.import_all_legacy_macros(module_id);
                         }
@@ -842,77 +1029,7 @@ impl ModCollector<'_, '_> {
         res
     }
 
-    fn define_def(&mut self, def: &raw::DefData, attrs: &Attrs) {
-        let module = ModuleId { krate: self.def_collector.def_map.krate, local_id: self.module_id };
-        // FIXME: check attrs to see if this is an attribute macro invocation;
-        // in which case we don't add the invocation, just a single attribute
-        // macro invocation
-        self.collect_derives(attrs, def);
-
-        let name = def.name.clone();
-        let container = ContainerId::ModuleId(module);
-        let vis = &def.visibility;
-        let mut has_constructor = false;
-
-        let def: ModuleDefId = match def.kind {
-            raw::DefKind::Function(ast_id) => FunctionLoc {
-                container: container.into(),
-                ast_id: AstId::new(self.file_id, ast_id),
-            }
-            .intern(self.def_collector.db)
-            .into(),
-            raw::DefKind::Struct(ast_id, mode) => {
-                has_constructor = mode != raw::StructDefKind::Record;
-                StructLoc { container, ast_id: AstId::new(self.file_id, ast_id) }
-                    .intern(self.def_collector.db)
-                    .into()
-            }
-            raw::DefKind::Union(ast_id) => {
-                UnionLoc { container, ast_id: AstId::new(self.file_id, ast_id) }
-                    .intern(self.def_collector.db)
-                    .into()
-            }
-            raw::DefKind::Enum(ast_id) => {
-                EnumLoc { container, ast_id: AstId::new(self.file_id, ast_id) }
-                    .intern(self.def_collector.db)
-                    .into()
-            }
-            raw::DefKind::Const(ast_id) => {
-                ConstLoc { container: container.into(), ast_id: AstId::new(self.file_id, ast_id) }
-                    .intern(self.def_collector.db)
-                    .into()
-            }
-            raw::DefKind::Static(ast_id) => {
-                StaticLoc { container, ast_id: AstId::new(self.file_id, ast_id) }
-                    .intern(self.def_collector.db)
-                    .into()
-            }
-            raw::DefKind::Trait(ast_id) => {
-                TraitLoc { container, ast_id: AstId::new(self.file_id, ast_id) }
-                    .intern(self.def_collector.db)
-                    .into()
-            }
-            raw::DefKind::TypeAlias(ast_id) => TypeAliasLoc {
-                container: container.into(),
-                ast_id: AstId::new(self.file_id, ast_id),
-            }
-            .intern(self.def_collector.db)
-            .into(),
-        };
-        self.def_collector.def_map.modules[self.module_id].scope.define_def(def);
-        let vis = self
-            .def_collector
-            .def_map
-            .resolve_visibility(self.def_collector.db, self.module_id, vis)
-            .unwrap_or(Visibility::Public);
-        self.def_collector.update(
-            self.module_id,
-            &[(name, PerNs::from_def(def, vis, has_constructor))],
-            vis,
-        )
-    }
-
-    fn collect_derives(&mut self, attrs: &Attrs, def: &raw::DefData) {
+    fn collect_derives(&mut self, attrs: &Attrs, ast_id: FileAstId<ast::ModuleItem>) {
         for derive_subtree in attrs.by_key("derive").tt_values() {
             // for #[derive(Copy, Clone)], `derive_subtree` is the `(Copy, Clone)` subtree
             for tt in &derive_subtree.token_trees {
@@ -923,7 +1040,7 @@ impl ModCollector<'_, '_> {
                 };
                 let path = ModPath::from_tt_ident(ident);
 
-                let ast_id = AstIdWithPath::new(self.file_id, def.kind.ast_id(), path);
+                let ast_id = AstIdWithPath::new(self.file_id, ast_id, path);
                 self.def_collector
                     .unexpanded_attribute_macros
                     .push(DeriveDirective { module_id: self.module_id, ast_id });
@@ -931,11 +1048,11 @@ impl ModCollector<'_, '_> {
         }
     }
 
-    fn collect_macro(&mut self, mac: &raw::MacroData) {
+    fn collect_macro(&mut self, mac: &MacroCall) {
         let mut ast_id = AstIdWithPath::new(self.file_id, mac.ast_id, mac.path.clone());
 
         // Case 0: builtin macros
-        if mac.builtin {
+        if mac.is_builtin {
             if let Some(name) = &mac.name {
                 let krate = self.def_collector.def_map.krate;
                 if let Some(macro_id) = find_builtin_macro(name, krate, ast_id.ast_id) {
@@ -943,7 +1060,7 @@ impl ModCollector<'_, '_> {
                         self.module_id,
                         name.clone(),
                         macro_id,
-                        mac.export,
+                        mac.is_export,
                     );
                     return;
                 }
@@ -957,9 +1074,14 @@ impl ModCollector<'_, '_> {
                     ast_id: Some(ast_id.ast_id),
                     krate: Some(self.def_collector.def_map.krate),
                     kind: MacroDefKind::Declarative,
-                    local_inner: mac.local_inner,
+                    local_inner: mac.is_local_inner,
                 };
-                self.def_collector.define_macro(self.module_id, name.clone(), macro_id, mac.export);
+                self.def_collector.define_macro(
+                    self.module_id,
+                    name.clone(),
+                    macro_id,
+                    mac.is_export,
+                );
             }
             return;
         }
diff --git a/crates/ra_hir_def/src/nameres/raw.rs b/crates/ra_hir_def/src/nameres/raw.rs
deleted file mode 100644
index f44baa5794..0000000000
--- a/crates/ra_hir_def/src/nameres/raw.rs
+++ /dev/null
@@ -1,482 +0,0 @@
-//! Lowers syntax tree of a rust file into a raw representation of containing
-//! items, *without* attaching them to a module structure.
-//!
-//! That is, raw items don't have semantics, just as syntax, but, unlike syntax,
-//! they don't change with trivial source code edits, making them a great tool
-//! for building salsa recomputation firewalls.
-
-use std::{ops::Index, sync::Arc};
-
-use hir_expand::{
-    ast_id_map::AstIdMap,
-    hygiene::Hygiene,
-    name::{AsName, Name},
-};
-use ra_arena::{Arena, Idx};
-use ra_prof::profile;
-use ra_syntax::{
-    ast::{self, AttrsOwner, NameOwner, VisibilityOwner},
-    AstNode,
-};
-use test_utils::mark;
-
-use crate::{
-    attr::Attrs,
-    db::DefDatabase,
-    path::{ImportAlias, ModPath},
-    visibility::RawVisibility,
-    FileAstId, HirFileId, InFile,
-};
-
-/// `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<ModuleData>,
-    imports: Arena<ImportData>,
-    defs: Arena<DefData>,
-    macros: Arena<MacroData>,
-    impls: Arena<ImplData>,
-    /// items for top-level module
-    items: Vec<RawItem>,
-}
-
-impl RawItems {
-    pub(crate) fn raw_items_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<RawItems> {
-        let _p = profile("raw_items_query");
-        let mut collector = RawItemsCollector {
-            raw_items: RawItems::default(),
-            source_ast_id_map: db.ast_id_map(file_id),
-            file_id,
-            hygiene: Hygiene::new(db.upcast(), file_id),
-        };
-        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);
-            }
-        }
-        let raw_items = collector.raw_items;
-        Arc::new(raw_items)
-    }
-
-    pub(super) fn items(&self) -> &[RawItem] {
-        &self.items
-    }
-}
-
-impl Index<Idx<ModuleData>> for RawItems {
-    type Output = ModuleData;
-    fn index(&self, idx: Idx<ModuleData>) -> &ModuleData {
-        &self.modules[idx]
-    }
-}
-
-impl Index<Import> for RawItems {
-    type Output = ImportData;
-    fn index(&self, idx: Import) -> &ImportData {
-        &self.imports[idx]
-    }
-}
-
-impl Index<Idx<DefData>> for RawItems {
-    type Output = DefData;
-    fn index(&self, idx: Idx<DefData>) -> &DefData {
-        &self.defs[idx]
-    }
-}
-
-impl Index<Idx<MacroData>> for RawItems {
-    type Output = MacroData;
-    fn index(&self, idx: Idx<MacroData>) -> &MacroData {
-        &self.macros[idx]
-    }
-}
-
-impl Index<Idx<ImplData>> for RawItems {
-    type Output = ImplData;
-    fn index(&self, idx: Idx<ImplData>) -> &ImplData {
-        &self.impls[idx]
-    }
-}
-
-#[derive(Debug, PartialEq, Eq, Clone)]
-pub(super) struct RawItem {
-    pub(super) attrs: Attrs,
-    pub(super) kind: RawItemKind,
-}
-
-#[derive(Debug, PartialEq, Eq, Clone, Copy)]
-pub(super) enum RawItemKind {
-    Module(Idx<ModuleData>),
-    Import(Import),
-    Def(Idx<DefData>),
-    Macro(Idx<MacroData>),
-    Impl(Idx<ImplData>),
-}
-
-#[derive(Debug, PartialEq, Eq)]
-pub(super) enum ModuleData {
-    Declaration {
-        name: Name,
-        visibility: RawVisibility,
-        ast_id: FileAstId<ast::Module>,
-    },
-    Definition {
-        name: Name,
-        visibility: RawVisibility,
-        ast_id: FileAstId<ast::Module>,
-        items: Vec<RawItem>,
-    },
-}
-
-pub(crate) type Import = Idx<ImportData>;
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct ImportData {
-    pub(super) path: ModPath,
-    pub(super) alias: Option<ImportAlias>,
-    pub(super) is_glob: bool,
-    pub(super) is_prelude: bool,
-    pub(super) is_extern_crate: bool,
-    pub(super) is_macro_use: bool,
-    pub(super) visibility: RawVisibility,
-}
-
-// type Def = Idx<DefData>;
-
-#[derive(Debug, PartialEq, Eq)]
-pub(super) struct DefData {
-    pub(super) name: Name,
-    pub(super) kind: DefKind,
-    pub(super) visibility: RawVisibility,
-}
-
-#[derive(Debug, PartialEq, Eq, Clone, Copy)]
-pub(super) enum StructDefKind {
-    Record,
-    Tuple,
-    Unit,
-}
-
-#[derive(Debug, PartialEq, Eq, Clone, Copy)]
-pub(super) enum DefKind {
-    Function(FileAstId<ast::FnDef>),
-    Struct(FileAstId<ast::StructDef>, StructDefKind),
-    Union(FileAstId<ast::UnionDef>),
-    Enum(FileAstId<ast::EnumDef>),
-    Const(FileAstId<ast::ConstDef>),
-    Static(FileAstId<ast::StaticDef>),
-    Trait(FileAstId<ast::TraitDef>),
-    TypeAlias(FileAstId<ast::TypeAliasDef>),
-}
-
-impl DefKind {
-    pub fn ast_id(self) -> FileAstId<ast::ModuleItem> {
-        match self {
-            DefKind::Function(it) => it.upcast(),
-            DefKind::Struct(it, _) => it.upcast(),
-            DefKind::Union(it) => it.upcast(),
-            DefKind::Enum(it) => it.upcast(),
-            DefKind::Const(it) => it.upcast(),
-            DefKind::Static(it) => it.upcast(),
-            DefKind::Trait(it) => it.upcast(),
-            DefKind::TypeAlias(it) => it.upcast(),
-        }
-    }
-}
-
-#[derive(Debug, PartialEq, Eq)]
-pub(super) struct MacroData {
-    pub(super) ast_id: FileAstId<ast::MacroCall>,
-    pub(super) path: ModPath,
-    pub(super) name: Option<Name>,
-    pub(super) export: bool,
-    pub(super) local_inner: bool,
-    pub(super) builtin: bool,
-}
-
-#[derive(Debug, PartialEq, Eq)]
-pub(super) struct ImplData {
-    pub(super) ast_id: FileAstId<ast::ImplDef>,
-}
-
-struct RawItemsCollector {
-    raw_items: RawItems,
-    source_ast_id_map: Arc<AstIdMap>,
-    file_id: HirFileId,
-    hygiene: Hygiene,
-}
-
-impl RawItemsCollector {
-    fn process_module(
-        &mut self,
-        current_module: Option<Idx<ModuleData>>,
-        body: impl ast::ModuleItemOwner,
-    ) {
-        for item in body.items() {
-            self.add_item(current_module, item)
-        }
-    }
-
-    fn add_item(&mut self, current_module: Option<Idx<ModuleData>>, item: ast::ModuleItem) {
-        let attrs = self.parse_attrs(&item);
-        let visibility = RawVisibility::from_ast_with_hygiene(item.visibility(), &self.hygiene);
-        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::ImplDef(it) => {
-                self.add_impl(current_module, it);
-                return;
-            }
-            ast::ModuleItem::StructDef(it) => {
-                let kind = match it.kind() {
-                    ast::StructKind::Record(_) => StructDefKind::Record,
-                    ast::StructKind::Tuple(_) => StructDefKind::Tuple,
-                    ast::StructKind::Unit => StructDefKind::Unit,
-                };
-                let id = self.source_ast_id_map.ast_id(&it);
-                let name = it.name();
-                (DefKind::Struct(id, kind), name)
-            }
-            ast::ModuleItem::UnionDef(it) => {
-                let id = self.source_ast_id_map.ast_id(&it);
-                let name = it.name();
-                (DefKind::Union(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())
-            }
-            ast::ModuleItem::MacroCall(it) => {
-                self.add_macro(current_module, it);
-                return;
-            }
-            ast::ModuleItem::ExternBlock(it) => {
-                self.add_extern_block(current_module, it);
-                return;
-            }
-        };
-        if let Some(name) = name {
-            let name = name.as_name();
-            let def = self.raw_items.defs.alloc(DefData { name, kind, visibility });
-            self.push_item(current_module, attrs, RawItemKind::Def(def));
-        }
-    }
-
-    fn add_extern_block(
-        &mut self,
-        current_module: Option<Idx<ModuleData>>,
-        block: ast::ExternBlock,
-    ) {
-        if let Some(items) = block.extern_item_list() {
-            for item in items.extern_items() {
-                let attrs = self.parse_attrs(&item);
-                let visibility =
-                    RawVisibility::from_ast_with_hygiene(item.visibility(), &self.hygiene);
-                let (kind, name) = match item {
-                    ast::ExternItem::FnDef(it) => {
-                        (DefKind::Function(self.source_ast_id_map.ast_id(&it)), it.name())
-                    }
-                    ast::ExternItem::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, visibility });
-                    self.push_item(current_module, attrs, RawItemKind::Def(def));
-                }
-            }
-        }
-    }
-
-    fn add_module(&mut self, current_module: Option<Idx<ModuleData>>, module: ast::Module) {
-        let name = match module.name() {
-            Some(it) => it.as_name(),
-            None => return,
-        };
-        let attrs = self.parse_attrs(&module);
-        let visibility = RawVisibility::from_ast_with_hygiene(module.visibility(), &self.hygiene);
-
-        let ast_id = self.source_ast_id_map.ast_id(&module);
-        if module.semicolon_token().is_some() {
-            let item =
-                self.raw_items.modules.alloc(ModuleData::Declaration { name, visibility, 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,
-                visibility,
-                ast_id,
-                items: Vec::new(),
-            });
-            self.process_module(Some(item), item_list);
-            self.push_item(current_module, attrs, RawItemKind::Module(item));
-            return;
-        }
-        mark::hit!(name_res_works_for_broken_modules);
-    }
-
-    fn add_use_item(&mut self, current_module: Option<Idx<ModuleData>>, use_item: ast::UseItem) {
-        // FIXME: cfg_attr
-        let is_prelude = use_item.has_atom_attr("prelude_import");
-        let attrs = self.parse_attrs(&use_item);
-        let visibility = RawVisibility::from_ast_with_hygiene(use_item.visibility(), &self.hygiene);
-
-        let mut buf = Vec::new();
-        ModPath::expand_use_item(
-            InFile { value: use_item, file_id: self.file_id },
-            &self.hygiene,
-            |path, _use_tree, is_glob, alias| {
-                let import_data = ImportData {
-                    path,
-                    alias,
-                    is_glob,
-                    is_prelude,
-                    is_extern_crate: false,
-                    is_macro_use: false,
-                    visibility: visibility.clone(),
-                };
-                buf.push(import_data);
-            },
-        );
-        for import_data in buf {
-            self.push_import(current_module, attrs.clone(), import_data);
-        }
-    }
-
-    fn add_extern_crate_item(
-        &mut self,
-        current_module: Option<Idx<ModuleData>>,
-        extern_crate: ast::ExternCrateItem,
-    ) {
-        if let Some(name_ref) = extern_crate.name_ref() {
-            let path = ModPath::from_name_ref(&name_ref);
-            let visibility =
-                RawVisibility::from_ast_with_hygiene(extern_crate.visibility(), &self.hygiene);
-            let alias = extern_crate.alias().map(|a| {
-                a.name().map(|it| it.as_name()).map_or(ImportAlias::Underscore, ImportAlias::Alias)
-            });
-            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,
-                visibility,
-            };
-            self.push_import(current_module, attrs, import_data);
-        }
-    }
-
-    fn add_macro(&mut self, current_module: Option<Idx<ModuleData>>, m: ast::MacroCall) {
-        let attrs = self.parse_attrs(&m);
-        let path = match m.path().and_then(|path| ModPath::from_src(path, &self.hygiene)) {
-            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_attr = attrs.by_key("macro_export");
-
-        let export = export_attr.exists();
-        let local_inner = if export {
-            export_attr.tt_values().map(|it| &it.token_trees).flatten().any(|it| match it {
-                tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => {
-                    ident.text.contains("local_inner_macros")
-                }
-                _ => false,
-            })
-        } else {
-            false
-        };
-
-        let builtin = attrs.by_key("rustc_builtin_macro").exists();
-
-        let m = self.raw_items.macros.alloc(MacroData {
-            ast_id,
-            path,
-            name,
-            export,
-            local_inner,
-            builtin,
-        });
-        self.push_item(current_module, attrs, RawItemKind::Macro(m));
-    }
-
-    fn add_impl(&mut self, current_module: Option<Idx<ModuleData>>, imp: ast::ImplDef) {
-        let attrs = self.parse_attrs(&imp);
-        let ast_id = self.source_ast_id_map.ast_id(&imp);
-        let imp = self.raw_items.impls.alloc(ImplData { ast_id });
-        self.push_item(current_module, attrs, RawItemKind::Impl(imp))
-    }
-
-    fn push_import(
-        &mut self,
-        current_module: Option<Idx<ModuleData>>,
-        attrs: Attrs,
-        data: ImportData,
-    ) {
-        let import = self.raw_items.imports.alloc(data);
-        self.push_item(current_module, attrs, RawItemKind::Import(import))
-    }
-
-    fn push_item(
-        &mut self,
-        current_module: Option<Idx<ModuleData>>,
-        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 {
-        Attrs::new(item, &self.hygiene)
-    }
-}
diff --git a/crates/ra_hir_def/src/src.rs b/crates/ra_hir_def/src/src.rs
index 46e90da700..043b93fad5 100644
--- a/crates/ra_hir_def/src/src.rs
+++ b/crates/ra_hir_def/src/src.rs
@@ -2,30 +2,37 @@
 
 use hir_expand::InFile;
 use ra_arena::map::ArenaMap;
-use ra_syntax::AstNode;
 
-use crate::{db::DefDatabase, AssocItemLoc, ItemLoc};
+use crate::{db::DefDatabase, item_tree::ItemTreeNode, AssocItemLoc, ItemLoc};
 
 pub trait HasSource {
     type Value;
     fn source(&self, db: &dyn DefDatabase) -> InFile<Self::Value>;
 }
 
-impl<N: AstNode> HasSource for AssocItemLoc<N> {
-    type Value = N;
+impl<N: ItemTreeNode> HasSource for AssocItemLoc<N> {
+    type Value = N::Source;
 
-    fn source(&self, db: &dyn DefDatabase) -> InFile<N> {
-        let node = self.ast_id.to_node(db.upcast());
-        InFile::new(self.ast_id.file_id, node)
+    fn source(&self, db: &dyn DefDatabase) -> InFile<N::Source> {
+        let tree = db.item_tree(self.id.file_id);
+        let ast_id_map = db.ast_id_map(self.id.file_id);
+        let root = db.parse_or_expand(self.id.file_id).unwrap();
+        let node = &tree[self.id.value];
+
+        InFile::new(self.id.file_id, ast_id_map.get(node.ast_id()).to_node(&root))
     }
 }
 
-impl<N: AstNode> HasSource for ItemLoc<N> {
-    type Value = N;
+impl<N: ItemTreeNode> HasSource for ItemLoc<N> {
+    type Value = N::Source;
+
+    fn source(&self, db: &dyn DefDatabase) -> InFile<N::Source> {
+        let tree = db.item_tree(self.id.file_id);
+        let ast_id_map = db.ast_id_map(self.id.file_id);
+        let root = db.parse_or_expand(self.id.file_id).unwrap();
+        let node = &tree[self.id.value];
 
-    fn source(&self, db: &dyn DefDatabase) -> InFile<N> {
-        let node = self.ast_id.to_node(db.upcast());
-        InFile::new(self.ast_id.file_id, node)
+        InFile::new(self.id.file_id, ast_id_map.get(node.ast_id()).to_node(&root))
     }
 }
 
diff --git a/crates/ra_hir_def/src/visibility.rs b/crates/ra_hir_def/src/visibility.rs
index 1482d3be04..8136cb50cc 100644
--- a/crates/ra_hir_def/src/visibility.rs
+++ b/crates/ra_hir_def/src/visibility.rs
@@ -6,7 +6,7 @@ use ra_syntax::ast;
 use crate::{
     db::DefDatabase,
     path::{ModPath, PathKind},
-    AssocContainerId, ModuleId,
+    ModuleId,
 };
 
 /// Visibility of an item, not yet resolved.
@@ -25,25 +25,6 @@ impl RawVisibility {
         RawVisibility::Module(path)
     }
 
-    pub(crate) fn default_for_container(container_id: AssocContainerId) -> Self {
-        match container_id {
-            AssocContainerId::TraitId(_) => RawVisibility::Public,
-            _ => RawVisibility::private(),
-        }
-    }
-
-    pub(crate) fn from_ast_with_default(
-        db: &dyn DefDatabase,
-        default: RawVisibility,
-        node: InFile<Option<ast::Visibility>>,
-    ) -> RawVisibility {
-        Self::from_ast_with_hygiene_and_default(
-            node.value,
-            default,
-            &Hygiene::new(db.upcast(), node.file_id),
-        )
-    }
-
     pub(crate) fn from_ast(
         db: &dyn DefDatabase,
         node: InFile<Option<ast::Visibility>>,
diff --git a/crates/ra_hir_expand/src/ast_id_map.rs b/crates/ra_hir_expand/src/ast_id_map.rs
index d19569245e..f4d31526a6 100644
--- a/crates/ra_hir_expand/src/ast_id_map.rs
+++ b/crates/ra_hir_expand/src/ast_id_map.rs
@@ -6,6 +6,8 @@
 //! changes.
 
 use std::{
+    any::type_name,
+    fmt,
     hash::{Hash, Hasher},
     marker::PhantomData,
 };
@@ -14,7 +16,6 @@ use ra_arena::{Arena, Idx};
 use ra_syntax::{ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr};
 
 /// `AstId` points to an AST node in a specific file.
-#[derive(Debug)]
 pub struct FileAstId<N: AstNode> {
     raw: ErasedFileAstId,
     _ty: PhantomData<fn() -> N>,
@@ -39,11 +40,17 @@ impl<N: AstNode> Hash for FileAstId<N> {
     }
 }
 
+impl<N: AstNode> fmt::Debug for FileAstId<N> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "FileAstId::<{}>({})", type_name::<N>(), self.raw.into_raw())
+    }
+}
+
 impl<N: AstNode> FileAstId<N> {
     // Can't make this a From implementation because of coherence
     pub fn upcast<M: AstNode>(self) -> FileAstId<M>
     where
-        M: From<N>,
+        N: Into<M>,
     {
         FileAstId { raw: self.raw, _ty: PhantomData }
     }
@@ -89,7 +96,7 @@ impl AstIdMap {
         }
     }
 
-    pub(crate) fn get<N: AstNode>(&self, id: FileAstId<N>) -> AstPtr<N> {
+    pub fn get<N: AstNode>(&self, id: FileAstId<N>) -> AstPtr<N> {
         self.arena[id.raw].clone().cast::<N>().unwrap()
     }
 
diff --git a/crates/ra_ide_db/src/change.rs b/crates/ra_ide_db/src/change.rs
index a95f6c13c1..b507000f22 100644
--- a/crates/ra_ide_db/src/change.rs
+++ b/crates/ra_ide_db/src/change.rs
@@ -199,7 +199,7 @@ impl RootDatabase {
             hir::db::InternEagerExpansionQuery
 
             // DefDatabase
-            hir::db::RawItemsQuery
+            hir::db::ItemTreeQuery
             hir::db::CrateDefMapQueryQuery
             hir::db::StructDataQuery
             hir::db::UnionDataQuery


Test patch:
diff --git a/crates/ra_hir_def/src/item_tree/tests.rs b/crates/ra_hir_def/src/item_tree/tests.rs
new file mode 100644
index 0000000000..dc035d809e
--- /dev/null
+++ b/crates/ra_hir_def/src/item_tree/tests.rs
@@ -0,0 +1,435 @@
+use super::{ItemTree, ModItem, ModKind};
+use crate::{db::DefDatabase, test_db::TestDB};
+use hir_expand::{db::AstDatabase, HirFileId, InFile};
+use insta::assert_snapshot;
+use ra_db::fixture::WithFixture;
+use ra_syntax::{ast, AstNode};
+use rustc_hash::FxHashSet;
+use std::sync::Arc;
+use stdx::format_to;
+
+fn test_inner_items(ra_fixture: &str) {
+    let (db, file_id) = TestDB::with_single_file(ra_fixture);
+    let file_id = HirFileId::from(file_id);
+    let tree = db.item_tree(file_id);
+    let root = db.parse_or_expand(file_id).unwrap();
+    let ast_id_map = db.ast_id_map(file_id);
+
+    // Traverse the item tree and collect all module/impl/trait-level items as AST nodes.
+    let mut outer_items = FxHashSet::default();
+    let mut worklist = tree.top_level_items().to_vec();
+    while let Some(item) = worklist.pop() {
+        let node: ast::ModuleItem = match item {
+            ModItem::Import(it) => tree.source(&db, InFile::new(file_id, it)).into(),
+            ModItem::ExternCrate(it) => tree.source(&db, InFile::new(file_id, it)).into(),
+            ModItem::Function(it) => tree.source(&db, InFile::new(file_id, it)).into(),
+            ModItem::Struct(it) => tree.source(&db, InFile::new(file_id, it)).into(),
+            ModItem::Union(it) => tree.source(&db, InFile::new(file_id, it)).into(),
+            ModItem::Enum(it) => tree.source(&db, InFile::new(file_id, it)).into(),
+            ModItem::Const(it) => tree.source(&db, InFile::new(file_id, it)).into(),
+            ModItem::Static(it) => tree.source(&db, InFile::new(file_id, it)).into(),
+            ModItem::TypeAlias(it) => tree.source(&db, InFile::new(file_id, it)).into(),
+            ModItem::Mod(it) => {
+                if let ModKind::Inline { items } = &tree[it].kind {
+                    worklist.extend(&**items);
+                }
+                tree.source(&db, InFile::new(file_id, it)).into()
+            }
+            ModItem::Trait(it) => {
+                worklist.extend(tree[it].items.iter().map(|item| ModItem::from(*item)));
+                tree.source(&db, InFile::new(file_id, it)).into()
+            }
+            ModItem::Impl(it) => {
+                worklist.extend(tree[it].items.iter().map(|item| ModItem::from(*item)));
+                tree.source(&db, InFile::new(file_id, it)).into()
+            }
+            ModItem::MacroCall(_) => continue,
+        };
+
+        outer_items.insert(node);
+    }
+
+    // Now descend the root node and check that all `ast::ModuleItem`s are either recorded above, or
+    // registered as inner items.
+    for item in root.descendants().skip(1).filter_map(ast::ModuleItem::cast) {
+        if outer_items.contains(&item) {
+            continue;
+        }
+
+        let ast_id = ast_id_map.ast_id(&item);
+        assert!(!tree.inner_items(ast_id).is_empty());
+    }
+}
+
+fn item_tree(ra_fixture: &str) -> Arc<ItemTree> {
+    let (db, file_id) = TestDB::with_single_file(ra_fixture);
+    db.item_tree(file_id.into())
+}
+
+fn print_item_tree(ra_fixture: &str) -> String {
+    let tree = item_tree(ra_fixture);
+    let mut out = String::new();
+
+    format_to!(out, "inner attrs: {:?}\n\n", tree.top_level_attrs());
+    format_to!(out, "top-level items:\n");
+    for item in tree.top_level_items() {
+        fmt_mod_item(&mut out, &tree, *item);
+        format_to!(out, "\n");
+    }
+
+    if !tree.inner_items.is_empty() {
+        format_to!(out, "\ninner items:\n\n");
+        for (ast_id, items) in &tree.inner_items {
+            format_to!(out, "for AST {:?}:\n", ast_id);
+            for inner in items {
+                fmt_mod_item(&mut out, &tree, *inner);
+                format_to!(out, "\n\n");
+            }
+        }
+    }
+
+    out
+}
+
+fn fmt_mod_item(out: &mut String, tree: &ItemTree, item: ModItem) {
+    let attrs = tree.attrs(item);
+    if !attrs.is_empty() {
+        format_to!(out, "#[{:?}]\n", attrs);
+    }
+
+    let mut children = String::new();
+    match item {
+        ModItem::ExternCrate(it) => {
+            format_to!(out, "{:?}", tree[it]);
+        }
+        ModItem::Import(it) => {
+            format_to!(out, "{:?}", tree[it]);
+        }
+        ModItem::Function(it) => {
+            format_to!(out, "{:?}", tree[it]);
+        }
+        ModItem::Struct(it) => {
+            format_to!(out, "{:?}", tree[it]);
+        }
+        ModItem::Union(it) => {
+            format_to!(out, "{:?}", tree[it]);
+        }
+        ModItem::Enum(it) => {
+            format_to!(out, "{:?}", tree[it]);
+        }
+        ModItem::Const(it) => {
+            format_to!(out, "{:?}", tree[it]);
+        }
+        ModItem::Static(it) => {
+            format_to!(out, "{:?}", tree[it]);
+        }
+        ModItem::Trait(it) => {
+            format_to!(out, "{:?}", tree[it]);
+            for item in &*tree[it].items {
+                fmt_mod_item(&mut children, tree, ModItem::from(*item));
+                format_to!(children, "\n");
+            }
+        }
+        ModItem::Impl(it) => {
+            format_to!(out, "{:?}", tree[it]);
+            for item in &*tree[it].items {
+                fmt_mod_item(&mut children, tree, ModItem::from(*item));
+                format_to!(children, "\n");
+            }
+        }
+        ModItem::TypeAlias(it) => {
+            format_to!(out, "{:?}", tree[it]);
+        }
+        ModItem::Mod(it) => {
+            format_to!(out, "{:?}", tree[it]);
+            match &tree[it].kind {
+                ModKind::Inline { items } => {
+                    for item in &**items {
+                        fmt_mod_item(&mut children, tree, *item);
+                        format_to!(children, "\n");
+                    }
+                }
+                ModKind::Outline {} => {}
+            }
+        }
+        ModItem::MacroCall(it) => {
+            format_to!(out, "{:?}", tree[it]);
+        }
+    }
+
+    for line in children.lines() {
+        format_to!(out, "\n> {}", line);
+    }
+}
+
+#[test]
+fn smoke() {
+    assert_snapshot!(print_item_tree(r"
+        #![attr]
+
+        #[attr_on_use]
+        use {a, b::*};
+
+        #[ext_crate]
+        extern crate krate;
+
+        #[on_trait]
+        trait Tr<U> {
+            #[assoc_ty]
+            type AssocTy: Tr<()>;
+
+            #[assoc_const]
+            const CONST: u8;
+
+            #[assoc_method]
+            fn method(&self);
+
+            #[assoc_dfl_method]
+            fn dfl_method(&mut self) {}
+        }
+
+        #[struct0]
+        struct Struct0<T = ()>;
+
+        #[struct1]
+        struct Struct1<T>(#[struct1fld] u8);
+
+        #[struct2]
+        struct Struct2<T> {
+            #[struct2fld]
+            fld: (T, ),
+        }
+
+        #[en]
+        enum En {
+            #[enum_variant]
+            Variant {
+                #[enum_field]
+                field: u8,
+            },
+        }
+
+        #[un]
+        union Un {
+            #[union_fld]
+            fld: u16,
+        }
+    "), @r###"
+inner attrs: Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr"))] }, input: None }]) }
+
+top-level items:
+#[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_on_use"))] }, input: None }]) }]
+Import { path: ModPath { kind: Plain, segments: [Name(Text("a"))] }, alias: None, visibility: RawVisibilityId("pub(self)"), is_glob: false, is_prelude: false, ast_id: FileAstId::<ra_syntax::ast::generated::nodes::UseItem>(0) }
+#[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_on_use"))] }, input: None }]) }]
+Import { path: ModPath { kind: Plain, segments: [Name(Text("b"))] }, alias: None, visibility: RawVisibilityId("pub(self)"), is_glob: true, is_prelude: false, ast_id: FileAstId::<ra_syntax::ast::generated::nodes::UseItem>(0) }
+#[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("ext_crate"))] }, input: None }]) }]
+ExternCrate { path: ModPath { kind: Plain, segments: [Name(Text("krate"))] }, alias: None, visibility: RawVisibilityId("pub(self)"), is_macro_use: false, ast_id: FileAstId::<ra_syntax::ast::generated::nodes::ExternCrateItem>(1) }
+#[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("on_trait"))] }, input: None }]) }]
+Trait { name: Name(Text("Tr")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(0), auto: false, items: [TypeAlias(Idx::<TypeAlias>(0)), Const(Idx::<Const>(0)), Function(Idx::<Function>(0)), Function(Idx::<Function>(1))], ast_id: FileAstId::<ra_syntax::ast::generated::nodes::TraitDef>(2) }
+> #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("assoc_ty"))] }, input: None }]) }]
+> TypeAlias { name: Name(Text("AssocTy")), visibility: RawVisibilityId("pub(self)"), bounds: [Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("Tr"))] }, generic_args: [Some(GenericArgs { args: [Type(Tuple([]))], has_self_type: false, bindings: [] })] })], generic_params: GenericParamsId(4294967295), type_ref: None, ast_id: FileAstId::<ra_syntax::ast::generated::nodes::TypeAliasDef>(8) }
+> #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("assoc_const"))] }, input: None }]) }]
+> Const { name: Some(Name(Text("CONST"))), visibility: RawVisibilityId("pub(self)"), type_ref: Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("u8"))] }, generic_args: [None] }), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::ConstDef>(9) }
+> #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("assoc_method"))] }, input: None }]) }]
+> Function { name: Name(Text("method")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: true, is_unsafe: false, params: [Reference(Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("Self"))] }, generic_args: [None] }), Shared)], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(10) }
+> #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("assoc_dfl_method"))] }, input: None }]) }]
+> Function { name: Name(Text("dfl_method")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: true, is_unsafe: false, params: [Reference(Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("Self"))] }, generic_args: [None] }), Mut)], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(11) }
+#[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("struct0"))] }, input: None }]) }]
+Struct { name: Name(Text("Struct0")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(1), fields: Unit, ast_id: FileAstId::<ra_syntax::ast::generated::nodes::StructDef>(3), kind: Unit }
+#[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("struct1"))] }, input: None }]) }]
+Struct { name: Name(Text("Struct1")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(2), fields: Tuple(Idx::<Field>(0)..Idx::<Field>(1)), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::StructDef>(4), kind: Tuple }
+#[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("struct2"))] }, input: None }]) }]
+Struct { name: Name(Text("Struct2")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(3), fields: Record(Idx::<Field>(1)..Idx::<Field>(2)), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::StructDef>(5), kind: Record }
+#[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("en"))] }, input: None }]) }]
+Enum { name: Name(Text("En")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), variants: Idx::<Variant>(0)..Idx::<Variant>(1), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::EnumDef>(6) }
+#[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("un"))] }, input: None }]) }]
+Union { name: Name(Text("Un")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), fields: Record(Idx::<Field>(3)..Idx::<Field>(4)), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::UnionDef>(7) }
+    "###);
+}
+
+#[test]
+fn simple_inner_items() {
+    let tree = print_item_tree(
+        r"
+        impl<T:A> D for Response<T> {
+            fn foo() {
+                end();
+                fn end<W: Write>() {
+                    let _x: T = loop {};
+                }
+            }
+        }
+    ",
+    );
+
+    assert_snapshot!(tree, @r###"
+inner attrs: Attrs { entries: None }
+
+top-level items:
+Impl { generic_params: GenericParamsId(0), target_trait: Some(Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("D"))] }, generic_args: [None] })), target_type: Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("Response"))] }, generic_args: [Some(GenericArgs { args: [Type(Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("T"))] }, generic_args: [None] }))], has_self_type: false, bindings: [] })] }), is_negative: false, items: [Function(Idx::<Function>(1))], ast_id: FileAstId::<ra_syntax::ast::generated::nodes::ImplDef>(0) }
+> Function { name: Name(Text("foo")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(1) }
+
+inner items:
+
+for AST FileAstId::<ra_syntax::ast::generated::nodes::ModuleItem>(2):
+Function { name: Name(Text("end")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(1), has_self_param: false, is_unsafe: false, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(2) }
+
+    "###);
+}
+
+#[test]
+fn extern_attrs() {
+    let tree = print_item_tree(
+        r#"
+        #[block_attr]
+        extern "C" {
+            #[attr_a]
+            fn a() {}
+            #[attr_b]
+            fn b() {}
+        }
+    "#,
+    );
+
+    assert_snapshot!(tree, @r###"
+inner attrs: Attrs { entries: None }
+
+top-level items:
+#[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_a"))] }, input: None }, Attr { path: ModPath { kind: Plain, segments: [Name(Text("block_attr"))] }, input: None }]) }]
+Function { name: Name(Text("a")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(1) }
+#[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_b"))] }, input: None }, Attr { path: ModPath { kind: Plain, segments: [Name(Text("block_attr"))] }, input: None }]) }]
+Function { name: Name(Text("b")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(2) }
+    "###);
+}
+
+#[test]
+fn trait_attrs() {
+    let tree = print_item_tree(
+        r#"
+        #[trait_attr]
+        trait Tr {
+            #[attr_a]
+            fn a() {}
+            #[attr_b]
+            fn b() {}
+        }
+    "#,
+    );
+
+    assert_snapshot!(tree, @r###"
+inner attrs: Attrs { entries: None }
+
+top-level items:
+#[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("trait_attr"))] }, input: None }]) }]
+Trait { name: Name(Text("Tr")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(0), auto: false, items: [Function(Idx::<Function>(0)), Function(Idx::<Function>(1))], ast_id: FileAstId::<ra_syntax::ast::generated::nodes::TraitDef>(0) }
+> #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_a"))] }, input: None }]) }]
+> Function { name: Name(Text("a")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(1) }
+> #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_b"))] }, input: None }]) }]
+> Function { name: Name(Text("b")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(2) }
+    "###);
+}
+
+#[test]
+fn impl_attrs() {
+    let tree = print_item_tree(
+        r#"
+        #[impl_attr]
+        impl Ty {
+            #[attr_a]
+            fn a() {}
+            #[attr_b]
+            fn b() {}
+        }
+    "#,
+    );
+
+    assert_snapshot!(tree, @r###"
+inner attrs: Attrs { entries: None }
+
+top-level items:
+#[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("impl_attr"))] }, input: None }]) }]
+Impl { generic_params: GenericParamsId(4294967295), target_trait: None, target_type: Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("Ty"))] }, generic_args: [None] }), is_negative: false, items: [Function(Idx::<Function>(0)), Function(Idx::<Function>(1))], ast_id: FileAstId::<ra_syntax::ast::generated::nodes::ImplDef>(0) }
+> #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_a"))] }, input: None }]) }]
+> Function { name: Name(Text("a")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(1) }
+> #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_b"))] }, input: None }]) }]
+> Function { name: Name(Text("b")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(2) }
+    "###);
+}
+
+#[test]
+fn cursed_inner_items() {
+    test_inner_items(
+        r"
+        struct S<T: Trait = [u8; { fn f() {} 0 }]>(T);
+
+        enum En {
+            Var1 {
+                t: [(); { trait Inner {} 0 }],
+            },
+
+            Var2([u16; { enum Inner {} 0 }]),
+        }
+
+        type Ty = [En; { struct Inner; 0 }];
+
+        impl En {
+            fn assoc() {
+                trait InnerTrait<T = [u8; { fn f() {} }]> {}
+                struct InnerStruct<T = [u8; { fn f() {} }]> {}
+                impl<T = [u8; { fn f() {} }]> InnerTrait for InnerStruct {}
+            }
+        }
+
+        trait Tr<T = [u8; { fn f() {} }]> {
+            type AssocTy = [u8; { fn f() {} }];
+
+            const AssocConst: [u8; { fn f() {} }];
+        }
+    ",
+    );
+}
+
+#[test]
+fn inner_item_attrs() {
+    let tree = print_item_tree(
+        r"
+        fn foo() {
+            #[on_inner]
+            fn inner() {}
+        }
+    ",
+    );
+
+    assert_snapshot!(tree, @r###"
+inner attrs: Attrs { entries: None }
+
+top-level items:
+Function { name: Name(Text("foo")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(0) }
+
+inner items:
+
+for AST FileAstId::<ra_syntax::ast::generated::nodes::ModuleItem>(1):
+#[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("on_inner"))] }, input: None }]) }]
+Function { name: Name(Text("inner")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(1) }
+
+    "###);
+}
+
+#[test]
+fn assoc_item_macros() {
+    let tree = print_item_tree(
+        r"
+        impl S {
+            items!();
+        }
+    ",
+    );
+
+    assert_snapshot!(tree, @r###"
+inner attrs: Attrs { entries: None }
+
+top-level items:
+Impl { generic_params: GenericParamsId(4294967295), target_trait: None, target_type: Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("S"))] }, generic_args: [None] }), is_negative: false, items: [MacroCall(Idx::<MacroCall>(0))], ast_id: FileAstId::<ra_syntax::ast::generated::nodes::ImplDef>(0) }
+> MacroCall { name: None, path: ModPath { kind: Plain, segments: [Name(Text("items"))] }, is_export: false, is_local_inner: false, is_builtin: false, ast_id: FileAstId::<ra_syntax::ast::generated::nodes::MacroCall>(1) }
+    "###);
+}
diff --git a/crates/ra_hir_def/src/nameres/tests/incremental.rs b/crates/ra_hir_def/src/nameres/tests/incremental.rs
index 87165ac33a..0c288a1085 100644
--- a/crates/ra_hir_def/src/nameres/tests/incremental.rs
+++ b/crates/ra_hir_def/src/nameres/tests/incremental.rs
@@ -57,44 +57,6 @@ fn typing_inside_a_function_should_not_invalidate_def_map() {
     );
 }
 
-#[test]
-fn adding_inner_items_should_not_invalidate_def_map() {
-    check_def_map_is_not_recomputed(
-        r"
-        //- /lib.rs
-        struct S { a: i32}
-        enum E { A }
-        trait T {
-            fn a() {}
-        }
-        mod foo;<|>
-        impl S {
-            fn a() {}
-        }
-        use crate::foo::bar::Baz;
-        //- /foo/mod.rs
-        pub mod bar;
-
-        //- /foo/bar.rs
-        pub struct Baz;
-        ",
-        r"
-        struct S { a: i32, b: () }
-        enum E { A, B }
-        trait T {
-            fn a() {}
-            fn b() {}
-        }
-        mod foo;<|>
-        impl S {
-            fn a() {}
-            fn b() {}
-        }
-        use crate::foo::bar::Baz;
-        ",
-    );
-}
-
 #[test]
 fn typing_inside_a_macro_should_not_invalidate_def_map() {
     let (mut db, pos) = TestDB::with_position(
diff --git a/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs b/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs
index b43b294cab..e9a5e4cba1 100644
--- a/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs
+++ b/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs
@@ -20,8 +20,11 @@ fn name_res_works_for_broken_modules() {
         ",
     );
     assert_snapshot!(map, @r###"
-        ⋮crate
-        ⋮Baz: _
+crate
+Baz: _
+foo: t
+
+crate::foo
     "###);
 }
 
@@ -719,10 +722,7 @@ fn unresolved_module_diagnostics() {
                         ),
                     ),
                 ),
-                value: FileAstId {
-                    raw: Idx::<SyntaxNodePtr>(1),
-                    _ty: PhantomData,
-                },
+                value: FileAstId::<ra_syntax::ast::generated::nodes::Module>(1),
             },
             candidate: "bar.rs",
         },
diff --git a/crates/ra_hir_ty/src/tests.rs b/crates/ra_hir_ty/src/tests.rs
index 1fe05c70c9..85ff26a368 100644
--- a/crates/ra_hir_ty/src/tests.rs
+++ b/crates/ra_hir_ty/src/tests.rs
@@ -67,8 +67,8 @@ fn type_at_pos_displayed(
     panic!("Can't find expression")
 }
 
-fn type_at(content: &str) -> String {
-    let (db, file_pos) = TestDB::with_position(content);
+fn type_at(ra_fixture: &str) -> String {
+    let (db, file_pos) = TestDB::with_position(ra_fixture);
     type_at_pos(&db, file_pos)
 }
 
@@ -164,13 +164,19 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
     visit_module(&db, &crate_def_map, module.local_id, &mut |it| defs.push(it));
     defs.sort_by_key(|def| match def {
         DefWithBodyId::FunctionId(it) => {
-            it.lookup(&db).ast_id.to_node(&db).syntax().text_range().start()
+            let loc = it.lookup(&db);
+            let tree = db.item_tree(loc.id.file_id);
+            tree.source(&db, loc.id).syntax().text_range().start()
         }
         DefWithBodyId::ConstId(it) => {
-            it.lookup(&db).ast_id.to_node(&db).syntax().text_range().start()
+            let loc = it.lookup(&db);
+            let tree = db.item_tree(loc.id.file_id);
+            tree.source(&db, loc.id).syntax().text_range().start()
         }
         DefWithBodyId::StaticId(it) => {
-            it.lookup(&db).ast_id.to_node(&db).syntax().text_range().start()
+            let loc = it.lookup(&db);
+            let tree = db.item_tree(loc.id.file_id);
+            tree.source(&db, loc.id).syntax().text_range().start()
         }
     });
     for def in defs {