Repository: rust-analyzer/rust-analyzer
Pull request #403 (2019-01-01 19:14:45)

initial support for macros
I'll write a more comprehensive description when this is closer to being done. Basically this investigates one question: "how do we represent code which is a result of a macro call". This is an interesting question: currently everything is `FileId` based, but macro expansion does not have a file!


Patch:
diff --git a/crates/ra_analysis/src/completion/complete_scope.rs b/crates/ra_analysis/src/completion/complete_scope.rs
index daf666505d..4dead36896 100644
--- a/crates/ra_analysis/src/completion/complete_scope.rs
+++ b/crates/ra_analysis/src/completion/complete_scope.rs
@@ -27,7 +27,7 @@ pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) ->
             match res.import {
                 None => true,
                 Some(import) => {
-                    let range = import.range(ctx.db, module.source().file_id());
+                    let range = import.range(ctx.db, module.file_id());
                     !range.is_subrange(&ctx.leaf.range())
                 }
             }
diff --git a/crates/ra_analysis/src/db.rs b/crates/ra_analysis/src/db.rs
index b072a5eba0..d7740f0c4e 100644
--- a/crates/ra_analysis/src/db.rs
+++ b/crates/ra_analysis/src/db.rs
@@ -1,7 +1,6 @@
 use std::{fmt, sync::Arc};
 use salsa::{self, Database};
 use ra_db::{LocationIntener, BaseDatabase};
-use hir::{self, DefId, DefLoc};
 
 use crate::{
     symbol_index,
@@ -15,7 +14,8 @@ pub(crate) struct RootDatabase {
 
 #[derive(Default)]
 struct IdMaps {
-    defs: LocationIntener<DefLoc, DefId>,
+    defs: LocationIntener<hir::DefLoc, hir::DefId>,
+    macros: LocationIntener<hir::MacroCallLoc, hir::MacroCallId>,
 }
 
 impl fmt::Debug for IdMaps {
@@ -59,12 +59,18 @@ impl salsa::ParallelDatabase for RootDatabase {
 
 impl BaseDatabase for RootDatabase {}
 
-impl AsRef<LocationIntener<DefLoc, DefId>> for RootDatabase {
-    fn as_ref(&self) -> &LocationIntener<DefLoc, DefId> {
+impl AsRef<LocationIntener<hir::DefLoc, hir::DefId>> for RootDatabase {
+    fn as_ref(&self) -> &LocationIntener<hir::DefLoc, hir::DefId> {
         &self.id_maps.defs
     }
 }
 
+impl AsRef<LocationIntener<hir::MacroCallLoc, hir::MacroCallId>> for RootDatabase {
+    fn as_ref(&self) -> &LocationIntener<hir::MacroCallLoc, hir::MacroCallId> {
+        &self.id_maps.macros
+    }
+}
+
 salsa::database_storage! {
     pub(crate) struct RootDatabaseStorage for RootDatabase {
         impl ra_db::FilesDatabase {
@@ -85,6 +91,8 @@ salsa::database_storage! {
             fn library_symbols() for symbol_index::LibrarySymbolsQuery;
         }
         impl hir::db::HirDatabase {
+            fn hir_source_file() for hir::db::HirSourceFileQuery;
+            fn expand_macro_invocation() for hir::db::ExpandMacroCallQuery;
             fn module_tree() for hir::db::ModuleTreeQuery;
             fn fn_scopes() for hir::db::FnScopesQuery;
             fn file_items() for hir::db::SourceFileItemsQuery;
diff --git a/crates/ra_analysis/src/extend_selection.rs b/crates/ra_analysis/src/extend_selection.rs
index 805e9059ef..f1b77f9819 100644
--- a/crates/ra_analysis/src/extend_selection.rs
+++ b/crates/ra_analysis/src/extend_selection.rs
@@ -18,15 +18,15 @@ pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRang
 }
 
 fn extend_selection_in_macro(
-    db: &RootDatabase,
+    _db: &RootDatabase,
     source_file: &SourceFileNode,
     frange: FileRange,
 ) -> Option<TextRange> {
     let macro_call = find_macro_call(source_file.syntax(), frange.range)?;
-    let exp = crate::macros::expand(db, frange.file_id, macro_call)?;
-    let dst_range = exp.map_range_forward(frange.range)?;
-    let dst_range = ra_editor::extend_selection(exp.source_file().syntax(), dst_range)?;
-    let src_range = exp.map_range_back(dst_range)?;
+    let (off, exp) = hir::MacroDef::ast_expand(macro_call)?;
+    let dst_range = exp.map_range_forward(frange.range - off)?;
+    let dst_range = ra_editor::extend_selection(exp.syntax().borrowed(), dst_range)?;
+    let src_range = exp.map_range_back(dst_range)? + off;
     Some(src_range)
 }
 
diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs
index 5669aa94d3..ec7da437a1 100644
--- a/crates/ra_analysis/src/imp.rs
+++ b/crates/ra_analysis/src/imp.rs
@@ -230,7 +230,7 @@ impl AnalysisImpl {
             Some(it) => it,
         };
         let root = descr.crate_root();
-        let file_id = root.source().file_id();
+        let file_id = root.file_id();
 
         let crate_graph = self.db.crate_graph();
         let crate_id = crate_graph.crate_id_for_crate_root(file_id);
@@ -283,7 +283,7 @@ impl AnalysisImpl {
                     if let Some(child_module) =
                         source_binder::module_from_declaration(&*self.db, position.file_id, module)?
                     {
-                        let file_id = child_module.source().file_id();
+                        let file_id = child_module.file_id();
                         let name = match child_module.name() {
                             Some(name) => name.to_string().into(),
                             None => "".into(),
diff --git a/crates/ra_analysis/src/lib.rs b/crates/ra_analysis/src/lib.rs
index e6cfaecc3c..08ecb125a3 100644
--- a/crates/ra_analysis/src/lib.rs
+++ b/crates/ra_analysis/src/lib.rs
@@ -19,7 +19,6 @@ mod runnables;
 
 mod extend_selection;
 mod syntax_highlighting;
-mod macros;
 
 use std::{fmt, sync::Arc};
 
diff --git a/crates/ra_analysis/src/macros.rs b/crates/ra_analysis/src/macros.rs
deleted file mode 100644
index b9feb7fad4..0000000000
--- a/crates/ra_analysis/src/macros.rs
+++ /dev/null
@@ -1,75 +0,0 @@
-/// Begining of macro expansion.
-///
-/// This code should be moved out of ra_analysis into hir (?) ideally.
-use ra_syntax::{ast, AstNode, SourceFileNode, TextRange};
-
-use crate::{db::RootDatabase, FileId};
-
-pub(crate) fn expand(
-    _db: &RootDatabase,
-    _file_id: FileId,
-    macro_call: ast::MacroCall,
-) -> Option<MacroExpansion> {
-    let path = macro_call.path()?;
-    if path.qualifier().is_some() {
-        return None;
-    }
-    let name_ref = path.segment()?.name_ref()?;
-    if name_ref.text() != "ctry" {
-        return None;
-    }
-
-    let arg = macro_call.token_tree()?;
-    let text = format!(
-        r"
-        fn dummy() {{
-            match {} {{
-                None => return Ok(None),
-                Some(it) => it,
-            }}
-        }}",
-        arg.syntax().text()
-    );
-    let file = SourceFileNode::parse(&text);
-    let match_expr = file.syntax().descendants().find_map(ast::MatchExpr::cast)?;
-    let match_arg = match_expr.expr()?;
-    let ranges_map = vec![(arg.syntax().range(), match_arg.syntax().range())];
-    let res = MacroExpansion {
-        source_file: file,
-        ranges_map,
-    };
-    Some(res)
-}
-
-pub(crate) struct MacroExpansion {
-    pub(crate) source_file: SourceFileNode,
-    pub(crate) ranges_map: Vec<(TextRange, TextRange)>,
-}
-
-impl MacroExpansion {
-    pub(crate) fn source_file(&self) -> &SourceFileNode {
-        &self.source_file
-    }
-    pub(crate) fn map_range_back(&self, tgt_range: TextRange) -> Option<TextRange> {
-        for (s_range, t_range) in self.ranges_map.iter() {
-            if tgt_range.is_subrange(&t_range) {
-                let tgt_at_zero_range = tgt_range - tgt_range.start();
-                let tgt_range_offset = tgt_range.start() - t_range.start();
-                let src_range = tgt_at_zero_range + tgt_range_offset + s_range.start();
-                return Some(src_range);
-            }
-        }
-        None
-    }
-    pub(crate) fn map_range_forward(&self, src_range: TextRange) -> Option<TextRange> {
-        for (s_range, t_range) in self.ranges_map.iter() {
-            if src_range.is_subrange(&s_range) {
-                let src_at_zero_range = src_range - src_range.start();
-                let src_range_offset = src_range.start() - s_range.start();
-                let src_range = src_at_zero_range + src_range_offset + t_range.start();
-                return Some(src_range);
-            }
-        }
-        None
-    }
-}
diff --git a/crates/ra_analysis/src/syntax_highlighting.rs b/crates/ra_analysis/src/syntax_highlighting.rs
index 7e9139a740..ccea4aee39 100644
--- a/crates/ra_analysis/src/syntax_highlighting.rs
+++ b/crates/ra_analysis/src/syntax_highlighting.rs
@@ -15,13 +15,13 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Cancelable<Vec<Hi
         .descendants()
         .filter_map(ast::MacroCall::cast)
     {
-        if let Some(exp) = crate::macros::expand(db, file_id, macro_call) {
-            let mapped_ranges = ra_editor::highlight(exp.source_file().syntax())
+        if let Some((off, exp)) = hir::MacroDef::ast_expand(macro_call) {
+            let mapped_ranges = ra_editor::highlight(exp.syntax().borrowed())
                 .into_iter()
                 .filter_map(|r| {
                     let mapped_range = exp.map_range_back(r.range)?;
                     let res = HighlightedRange {
-                        range: mapped_range,
+                        range: mapped_range + off,
                         tag: r.tag,
                     };
                     Some(res)
@@ -44,7 +44,7 @@ mod tests {
             fn main() {
                 ctry!({ let x = 92; x});
             }
-        ",
+            ",
         );
         let highlights = analysis.highlight(file_id).unwrap();
         assert_eq_dbg(
@@ -60,4 +60,26 @@ mod tests {
             &highlights,
         )
     }
+
+    // FIXME: this test is not really necessary: artifact of the inital hacky
+    // macros implementation.
+    #[test]
+    fn highlight_query_group_macro() {
+        let (analysis, file_id) = single_file(
+            "
+            salsa::query_group! {
+                pub trait HirDatabase: SyntaxDatabase {}
+            }
+            ",
+        );
+        let highlights = analysis.highlight(file_id).unwrap();
+        assert_eq_dbg(
+            r#"[HighlightedRange { range: [20; 32), tag: "macro" },
+                HighlightedRange { range: [13; 18), tag: "text" },
+                HighlightedRange { range: [51; 54), tag: "keyword" },
+                HighlightedRange { range: [55; 60), tag: "keyword" },
+                HighlightedRange { range: [61; 72), tag: "function" }]"#,
+            &highlights,
+        )
+    }
 }
diff --git a/crates/ra_hir/src/adt.rs b/crates/ra_hir/src/adt.rs
index e839a5a90d..c6463235c4 100644
--- a/crates/ra_hir/src/adt.rs
+++ b/crates/ra_hir/src/adt.rs
@@ -1,9 +1,10 @@
 use std::sync::Arc;
 
+use ra_db::Cancelable;
 use ra_syntax::ast::{self, NameOwner, StructFlavor};
 
 use crate::{
-    DefId, Cancelable, Name, AsName,
+    DefId, Name, AsName,
     db::HirDatabase,
     type_ref::TypeRef,
 };
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs
index 5a8ca3b47a..73a4cdc5c8 100644
--- a/crates/ra_hir/src/db.rs
+++ b/crates/ra_hir/src/db.rs
@@ -1,13 +1,14 @@
 use std::sync::Arc;
 
-use ra_syntax::SyntaxNode;
-use ra_db::{SourceRootId, LocationIntener, SyntaxDatabase, FileId, Cancelable};
+use ra_syntax::{SyntaxNode, SourceFileNode};
+use ra_db::{SourceRootId, LocationIntener, SyntaxDatabase, Cancelable};
 
 use crate::{
-    DefLoc, DefId, Name,
+    DefLoc, DefId, MacroCallLoc, MacroCallId, Name, HirFileId,
     SourceFileItems, SourceItemId,
     query_definitions,
     FnScopes,
+    macros::MacroExpansion,
     module::{ModuleId, ModuleTree, ModuleSource,
     nameres::{ItemMap, InputModuleItems}},
     ty::{InferenceResult, Ty},
@@ -18,7 +19,17 @@ salsa::query_group! {
 
 pub trait HirDatabase: SyntaxDatabase
     + AsRef<LocationIntener<DefLoc, DefId>>
+    + AsRef<LocationIntener<MacroCallLoc, MacroCallId>>
 {
+    fn hir_source_file(file_id: HirFileId) -> SourceFileNode {
+        type HirSourceFileQuery;
+        use fn HirFileId::hir_source_file;
+    }
+    fn expand_macro_invocation(invoc: MacroCallId) -> Option<Arc<MacroExpansion>> {
+        type ExpandMacroCallQuery;
+        use fn crate::macros::expand_macro_invocation;
+    }
+
     fn fn_scopes(def_id: DefId) -> Arc<FnScopes> {
         type FnScopesQuery;
         use fn query_definitions::fn_scopes;
@@ -49,7 +60,7 @@ pub trait HirDatabase: SyntaxDatabase
         use fn crate::ty::type_for_field;
     }
 
-    fn file_items(file_id: FileId) -> Arc<SourceFileItems> {
+    fn file_items(file_id: HirFileId) -> Arc<SourceFileItems> {
         type SourceFileItemsQuery;
         use fn query_definitions::file_items;
     }
diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs
new file mode 100644
index 0000000000..a09dee8b1d
--- /dev/null
+++ b/crates/ra_hir/src/ids.rs
@@ -0,0 +1,280 @@
+use ra_db::{SourceRootId, LocationIntener, Cancelable, FileId};
+use ra_syntax::{SourceFileNode, SyntaxKind, SyntaxNode, SyntaxNodeRef, SourceFile, AstNode, ast};
+
+use crate::{
+    HirDatabase, PerNs, ModuleId, Module, Def, Function, Struct, Enum,
+    arena::{Arena, Id},
+};
+
+/// hir makes a heavy use of ids: integer (u32) handlers to various things. You
+/// can think of id as a pointer (but without a lifetime) or a file descriptor
+/// (but for hir objects).
+///
+/// This module defines a bunch of ids we are using. The most important ones are
+/// probably `HirFileId` and `DefId`.
+
+/// Input to the analyzer is a set of file, where each file is indetified by
+/// `FileId` and contains source code. However, another source of source code in
+/// Rust are macros: each macro can be thought of as producing a "temporary
+/// file". To assign id to such file, we use the id of a macro call that
+/// produced the file. So, a `HirFileId` is either a `FileId` (source code
+/// written by user), or a `MacroCallId` (source code produced by macro).
+///
+/// What is a `MacroCallId`? Simplifying, it's a `HirFileId` of a file containin
+/// the call plus the offset of the macro call in the file. Note that this is a
+/// recursive definition! Nethetheless, size_of of `HirFileId` is finite
+/// (because everything bottoms out at the real `FileId`) and small
+/// (`MacroCallId` uses location interner).
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct HirFileId(HirFileIdRepr);
+
+impl HirFileId {
+    /// For macro-expansion files, returns the file original source file the
+    /// expansionoriginated from.
+    pub(crate) fn original_file(self, db: &impl HirDatabase) -> FileId {
+        match self.0 {
+            HirFileIdRepr::File(file_id) => file_id,
+            HirFileIdRepr::Macro(macro_call_id) => {
+                let loc = macro_call_id.loc(db);
+                loc.source_item_id.file_id.original_file(db)
+            }
+        }
+    }
+
+    pub(crate) fn as_original_file(self) -> FileId {
+        match self.0 {
+            HirFileIdRepr::File(file_id) => file_id,
+            HirFileIdRepr::Macro(_r) => panic!("macro generated file: {:?}", self),
+        }
+    }
+
+    pub(crate) fn hir_source_file(db: &impl HirDatabase, file_id: HirFileId) -> SourceFileNode {
+        match file_id.0 {
+            HirFileIdRepr::File(file_id) => db.source_file(file_id),
+            HirFileIdRepr::Macro(m) => {
+                if let Some(exp) = db.expand_macro_invocation(m) {
+                    return exp.file();
+                }
+                // returning an empty string looks fishy...
+                SourceFileNode::parse("")
+            }
+        }
+    }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+enum HirFileIdRepr {
+    File(FileId),
+    Macro(MacroCallId),
+}
+
+impl From<FileId> for HirFileId {
+    fn from(file_id: FileId) -> HirFileId {
+        HirFileId(HirFileIdRepr::File(file_id))
+    }
+}
+
+impl From<MacroCallId> for HirFileId {
+    fn from(macro_call_id: MacroCallId) -> HirFileId {
+        HirFileId(HirFileIdRepr::Macro(macro_call_id))
+    }
+}
+
+/// `MacroCallId` identifies a particular macro invocation, like
+/// `println!("Hello, {}", world)`.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct MacroCallId(u32);
+ra_db::impl_numeric_id!(MacroCallId);
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct MacroCallLoc {
+    pub(crate) source_root_id: SourceRootId,
+    pub(crate) module_id: ModuleId,
+    pub(crate) source_item_id: SourceItemId,
+}
+
+impl MacroCallId {
+    pub(crate) fn loc(
+        self,
+        db: &impl AsRef<LocationIntener<MacroCallLoc, MacroCallId>>,
+    ) -> MacroCallLoc {
+        db.as_ref().id2loc(self)
+    }
+}
+
+impl MacroCallLoc {
+    #[allow(unused)]
+    pub(crate) fn id(
+        &self,
+        db: &impl AsRef<LocationIntener<MacroCallLoc, MacroCallId>>,
+    ) -> MacroCallId {
+        db.as_ref().loc2id(&self)
+    }
+}
+
+/// Def's are a core concept of hir. A `Def` is an Item (function, module, etc)
+/// in a specific module.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct DefId(u32);
+ra_db::impl_numeric_id!(DefId);
+
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+pub struct DefLoc {
+    pub(crate) kind: DefKind,
+    pub(crate) source_root_id: SourceRootId,
+    pub(crate) module_id: ModuleId,
+    pub(crate) source_item_id: SourceItemId,
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+pub(crate) enum DefKind {
+    Module,
+    Function,
+    Struct,
+    Enum,
+    Item,
+
+    StructCtor,
+}
+
+impl DefId {
+    pub(crate) fn loc(self, db: &impl AsRef<LocationIntener<DefLoc, DefId>>) -> DefLoc {
+        db.as_ref().id2loc(self)
+    }
+
+    pub fn resolve(self, db: &impl HirDatabase) -> Cancelable<Def> {
+        let loc = self.loc(db);
+        let res = match loc.kind {
+            DefKind::Module => {
+                let module = Module::new(db, loc.source_root_id, loc.module_id)?;
+                Def::Module(module)
+            }
+            DefKind::Function => {
+                let function = Function::new(self);
+                Def::Function(function)
+            }
+            DefKind::Struct => {
+                let struct_def = Struct::new(self);
+                Def::Struct(struct_def)
+            }
+            DefKind::Enum => {
+                let enum_def = Enum::new(self);
+                Def::Enum(enum_def)
+            }
+            DefKind::StructCtor => Def::Item,
+            DefKind::Item => Def::Item,
+        };
+        Ok(res)
+    }
+
+    /// For a module, returns that module; for any other def, returns the containing module.
+    pub fn module(self, db: &impl HirDatabase) -> Cancelable<Module> {
+        let loc = self.loc(db);
+        Module::new(db, loc.source_root_id, loc.module_id)
+    }
+}
+
+impl DefLoc {
+    pub(crate) fn id(&self, db: &impl AsRef<LocationIntener<DefLoc, DefId>>) -> DefId {
+        db.as_ref().loc2id(&self)
+    }
+}
+
+impl DefKind {
+    pub(crate) fn for_syntax_kind(kind: SyntaxKind) -> PerNs<DefKind> {
+        match kind {
+            SyntaxKind::FN_DEF => PerNs::values(DefKind::Function),
+            SyntaxKind::MODULE => PerNs::types(DefKind::Module),
+            SyntaxKind::STRUCT_DEF => PerNs::both(DefKind::Struct, DefKind::StructCtor),
+            SyntaxKind::ENUM_DEF => PerNs::types(DefKind::Enum),
+            // These define items, but don't have their own DefKinds yet:
+            SyntaxKind::TRAIT_DEF => PerNs::types(DefKind::Item),
+            SyntaxKind::TYPE_DEF => PerNs::types(DefKind::Item),
+            SyntaxKind::CONST_DEF => PerNs::values(DefKind::Item),
+            SyntaxKind::STATIC_DEF => PerNs::values(DefKind::Item),
+            _ => PerNs::none(),
+        }
+    }
+}
+
+/// Identifier of item within a specific file. This is stable over reparses, so
+/// it's OK to use it as a salsa key/value.
+pub(crate) type SourceFileItemId = Id<SyntaxNode>;
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct SourceItemId {
+    pub(crate) file_id: HirFileId,
+    /// None for the whole file.
+    pub(crate) item_id: Option<SourceFileItemId>,
+}
+
+/// Maps item's `SyntaxNode`s to `SourceFileItemId` and back.
+#[derive(Debug, PartialEq, Eq)]
+pub struct SourceFileItems {
+    file_id: HirFileId,
+    arena: Arena<SyntaxNode>,
+}
+
+impl SourceFileItems {
+    pub(crate) fn new(file_id: HirFileId, source_file: SourceFile) -> SourceFileItems {
+        let mut res = SourceFileItems {
+            file_id,
+            arena: Arena::default(),
+        };
+        res.init(source_file);
+        res
+    }
+
+    fn init(&mut self, source_file: SourceFile) {
+        source_file.syntax().descendants().for_each(|it| {
+            if let Some(module_item) = ast::ModuleItem::cast(it) {
+                self.alloc(module_item.syntax().owned());
+            } else if let Some(macro_call) = ast::MacroCall::cast(it) {
+                self.alloc(macro_call.syntax().owned());
+            }
+        });
+    }
+
+    fn alloc(&mut self, item: SyntaxNode) -> SourceFileItemId {
+        self.arena.alloc(item)
+    }
+    pub(crate) fn id_of(&self, file_id: HirFileId, item: SyntaxNodeRef) -> SourceFileItemId {
+        assert_eq!(
+            self.file_id, file_id,
+            "SourceFileItems: wrong file, expected {:?}, got {:?}",
+            self.file_id, file_id
+        );
+        self.id_of_unchecked(item)
+    }
+    pub(crate) fn id_of_unchecked(&self, item: SyntaxNodeRef) -> SourceFileItemId {
+        if let Some((id, _)) = self.arena.iter().find(|(_id, i)| i.borrowed() == item) {
+            return id;
+        }
+        // This should not happen. Let's try to give a sensible diagnostics.
+        if let Some((id, i)) = self.arena.iter().find(|(_id, i)| i.range() == item.range()) {
+            // FIXME(#288): whyyy are we getting here?
+            log::error!(
+                "unequal syntax nodes with the same range:\n{:?}\n{:?}",
+                item,
+                i
+            );
+            return id;
+        }
+        panic!(
+            "Can't find {:?} in SourceFileItems:\n{:?}",
+            item,
+            self.arena.iter().map(|(_id, i)| i).collect::<Vec<_>>(),
+        );
+    }
+    pub fn id_of_source_file(&self) -> SourceFileItemId {
+        let (id, _syntax) = self.arena.iter().next().unwrap();
+        id
+    }
+}
+
+impl std::ops::Index<SourceFileItemId> for SourceFileItems {
+    type Output = SyntaxNode;
+    fn index(&self, idx: SourceFileItemId) -> &SyntaxNode {
+        &self.arena[idx]
+    }
+}
diff --git a/crates/ra_hir/src/krate.rs b/crates/ra_hir/src/krate.rs
index 89b1e639ef..a0821d15dd 100644
--- a/crates/ra_hir/src/krate.rs
+++ b/crates/ra_hir/src/krate.rs
@@ -1,6 +1,6 @@
-pub use ra_db::CrateId;
+pub use ra_db::{CrateId, Cancelable};
 
-use crate::{HirDatabase, Module, Cancelable, Name, AsName};
+use crate::{HirDatabase, Module, Name, AsName, HirFileId};
 
 /// hir::Crate describes a single crate. It's the main inteface with which
 /// crate's dependencies interact. Mostly, it should be just a proxy for the
@@ -35,6 +35,7 @@ impl Crate {
         let crate_graph = db.crate_graph();
         let file_id = crate_graph.crate_root(self.crate_id);
         let source_root_id = db.file_source_root(file_id);
+        let file_id = HirFileId::from(file_id);
         let module_tree = db.module_tree(source_root_id)?;
         // FIXME: teach module tree about crate roots instead of guessing
         let (module_id, _) = ctry!(module_tree
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs
index 5bbb09c01c..8ee52a466d 100644
--- a/crates/ra_hir/src/lib.rs
+++ b/crates/ra_hir/src/lib.rs
@@ -22,7 +22,10 @@ mod path;
 mod arena;
 pub mod source_binder;
 
+mod ids;
+mod macros;
 mod name;
+// can't use `crate` or `r#crate` here :(
 mod krate;
 mod module;
 mod function;
@@ -30,21 +33,18 @@ mod adt;
 mod type_ref;
 mod ty;
 
-use std::ops::Index;
-
-use ra_syntax::{SyntaxNodeRef, SyntaxNode, SyntaxKind};
-use ra_db::{LocationIntener, SourceRootId, FileId, Cancelable};
-
 use crate::{
     db::HirDatabase,
-    arena::{Arena, Id},
     name::{AsName, KnownName},
+    ids::{DefKind, SourceItemId, SourceFileItemId, SourceFileItems},
 };
 
 pub use self::{
     path::{Path, PathKind},
     name::Name,
     krate::Crate,
+    ids::{HirFileId, DefId, DefLoc, MacroCallId, MacroCallLoc},
+    macros::{MacroDef, MacroInput, MacroExpansion},
     module::{Module, ModuleId, Problem, nameres::{ItemMap, PerNs, Namespace}, ModuleScope, Resolution},
     function::{Function, FnScopes},
     adt::{Struct, Enum},
@@ -53,60 +53,6 @@ pub use self::{
 
 pub use self::function::FnSignatureInfo;
 
-/// Def's are a core concept of hir. A `Def` is an Item (function, module, etc)
-/// in a specific module.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct DefId(u32);
-ra_db::impl_numeric_id!(DefId);
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-pub(crate) enum DefKind {
-    Module,
-    Function,
-    Struct,
-    Enum,
-    Item,
-
-    StructCtor,
-}
-
-#[derive(Clone, Debug, PartialEq, Eq, Hash)]
-pub struct DefLoc {
-    pub(crate) kind: DefKind,
-    source_root_id: SourceRootId,
-    module_id: ModuleId,
-    source_item_id: SourceItemId,
-}
-
-impl DefKind {
-    pub(crate) fn for_syntax_kind(kind: SyntaxKind) -> PerNs<DefKind> {
-        match kind {
-            SyntaxKind::FN_DEF => PerNs::values(DefKind::Function),
-            SyntaxKind::MODULE => PerNs::types(DefKind::Module),
-            SyntaxKind::STRUCT_DEF => PerNs::both(DefKind::Struct, DefKind::StructCtor),
-            SyntaxKind::ENUM_DEF => PerNs::types(DefKind::Enum),
-            // These define items, but don't have their own DefKinds yet:
-            SyntaxKind::TRAIT_DEF => PerNs::types(DefKind::Item),
-            SyntaxKind::TYPE_DEF => PerNs::types(DefKind::Item),
-            SyntaxKind::CONST_DEF => PerNs::values(DefKind::Item),
-            SyntaxKind::STATIC_DEF => PerNs::values(DefKind::Item),
-            _ => PerNs::none(),
-        }
-    }
-}
-
-impl DefId {
-    pub(crate) fn loc(self, db: &impl AsRef<LocationIntener<DefLoc, DefId>>) -> DefLoc {
-        db.as_ref().id2loc(self)
-    }
-}
-
-impl DefLoc {
-    pub(crate) fn id(&self, db: &impl AsRef<LocationIntener<DefLoc, DefId>>) -> DefId {
-        db.as_ref().loc2id(&self)
-    }
-}
-
 pub enum Def {
     Module(Module),
     Function(Function),
@@ -114,106 +60,3 @@ pub enum Def {
     Enum(Enum),
     Item,
 }
-
-impl DefId {
-    pub fn resolve(self, db: &impl HirDatabase) -> Cancelable<Def> {
-        let loc = self.loc(db);
-        let res = match loc.kind {
-            DefKind::Module => {
-                let module = Module::new(db, loc.source_root_id, loc.module_id)?;
-                Def::Module(module)
-            }
-            DefKind::Function => {
-                let function = Function::new(self);
-                Def::Function(function)
-            }
-            DefKind::Struct => {
-                let struct_def = Struct::new(self);
-                Def::Struct(struct_def)
-            }
-            DefKind::Enum => {
-                let enum_def = Enum::new(self);
-                Def::Enum(enum_def)
-            }
-            DefKind::StructCtor => Def::Item,
-            DefKind::Item => Def::Item,
-        };
-        Ok(res)
-    }
-
-    /// For a module, returns that module; for any other def, returns the containing module.
-    pub fn module(self, db: &impl HirDatabase) -> Cancelable<Module> {
-        let loc = self.loc(db);
-        Module::new(db, loc.source_root_id, loc.module_id)
-    }
-}
-
-/// Identifier of item within a specific file. This is stable over reparses, so
-/// it's OK to use it as a salsa key/value.
-pub(crate) type SourceFileItemId = Id<SyntaxNode>;
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct SourceItemId {
-    file_id: FileId,
-    /// None for the whole file.
-    item_id: Option<SourceFileItemId>,
-}
-
-/// Maps item's `SyntaxNode`s to `SourceFileItemId` and back.
-#[derive(Debug, PartialEq, Eq)]
-pub struct SourceFileItems {
-    file_id: FileId,
-    arena: Arena<SyntaxNode>,
-}
-
-impl SourceFileItems {
-    fn new(file_id: FileId) -> SourceFileItems {
-        SourceFileItems {
-            file_id,
-            arena: Arena::default(),
-        }
-    }
-
-    fn alloc(&mut self, item: SyntaxNode) -> SourceFileItemId {
-        self.arena.alloc(item)
-    }
-    pub fn id_of(&self, file_id: FileId, item: SyntaxNodeRef) -> SourceFileItemId {
-        assert_eq!(
-            self.file_id, file_id,
-            "SourceFileItems: wrong file, expected {:?}, got {:?}",
-            self.file_id, file_id
-        );
-        self.id_of_unchecked(item)
-    }
-    fn id_of_unchecked(&self, item: SyntaxNodeRef) -> SourceFileItemId {
-        if let Some((id, _)) = self.arena.iter().find(|(_id, i)| i.borrowed() == item) {
-            return id;
-        }
-        // This should not happen. Let's try to give a sensible diagnostics.
-        if let Some((id, i)) = self.arena.iter().find(|(_id, i)| i.range() == item.range()) {
-            // FIXME(#288): whyyy are we getting here?
-            log::error!(
-                "unequal syntax nodes with the same range:\n{:?}\n{:?}",
-                item,
-                i
-            );
-            return id;
-        }
-        panic!(
-            "Can't find {:?} in SourceFileItems:\n{:?}",
-            item,
-            self.arena.iter().map(|(_id, i)| i).collect::<Vec<_>>(),
-        );
-    }
-    pub fn id_of_source_file(&self) -> SourceFileItemId {
-        let (id, _syntax) = self.arena.iter().next().unwrap();
-        id
-    }
-}
-
-impl Index<SourceFileItemId> for SourceFileItems {
-    type Output = SyntaxNode;
-    fn index(&self, idx: SourceFileItemId) -> &SyntaxNode {
-        &self.arena[idx]
-    }
-}
diff --git a/crates/ra_hir/src/macros.rs b/crates/ra_hir/src/macros.rs
new file mode 100644
index 0000000000..b7b75e7022
--- /dev/null
+++ b/crates/ra_hir/src/macros.rs
@@ -0,0 +1,181 @@
+/// Machinery for macro expansion.
+///
+/// One of the more complicated things about macros is managing the source code
+/// that is produced after expansion. See `HirFileId` and `MacroCallId` for how
+/// do we do that.
+///
+/// When file-management question is resolved, all that is left is a token tree
+/// to token tree transformation plus hygent. We don't have either of thouse
+/// yet, so all macros are string based at the moment!
+use std::sync::Arc;
+
+use ra_db::LocalSyntaxPtr;
+use ra_syntax::{
+    TextRange, TextUnit, SourceFileNode, AstNode, SyntaxNode,
+    ast::{self, NameOwner},
+};
+
+use crate::{HirDatabase, MacroCallId};
+
+// Hard-coded defs for now :-(
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum MacroDef {
+    CTry,
+    QueryGroup,
+}
+
+impl MacroDef {
+    /// Expands macro call, returning the expansion and offset to be used to
+    /// convert ranges between expansion and original source.
+    pub fn ast_expand(macro_call: ast::MacroCall) -> Option<(TextUnit, MacroExpansion)> {
+        let (def, input) = MacroDef::from_call(macro_call)?;
+        let exp = def.expand(input)?;
+        let off = macro_call.token_tree()?.syntax().range().start();
+        Some((off, exp))
+    }
+
+    fn from_call(macro_call: ast::MacroCall) -> Option<(MacroDef, MacroInput)> {
+        let def = {
+            let path = macro_call.path()?;
+            let name_ref = path.segment()?.name_ref()?;
+            if name_ref.text() == "ctry" {
+                MacroDef::CTry
+            } else if name_ref.text() == "query_group" {
+                MacroDef::QueryGroup
+            } else {
+                return None;
+            }
+        };
+
+        let input = {
+            let arg = macro_call.token_tree()?.syntax();
+            MacroInput {
+                text: arg.text().to_string(),
+            }
+        };
+        Some((def, input))
+    }
+
+    fn expand(self, input: MacroInput) -> Option<MacroExpansion> {
+        match self {
+            MacroDef::CTry => self.expand_ctry(input),
+            MacroDef::QueryGroup => self.expand_query_group(input),
+        }
+    }
+    fn expand_ctry(self, input: MacroInput) -> Option<MacroExpansion> {
+        let text = format!(
+            r"
+                fn dummy() {{
+                    match {} {{
+                        None => return Ok(None),
+                        Some(it) => it,
+                    }}
+                }}",
+            input.text
+        );
+        let file = SourceFileNode::parse(&text);
+        let match_expr = file.syntax().descendants().find_map(ast::MatchExpr::cast)?;
+        let match_arg = match_expr.expr()?;
+        let ptr = LocalSyntaxPtr::new(match_arg.syntax());
+        let src_range = TextRange::offset_len(0.into(), TextUnit::of_str(&input.text));
+        let ranges_map = vec![(src_range, match_arg.syntax().range())];
+        let res = MacroExpansion {
+            text,
+            ranges_map,
+            ptr,
+        };
+        Some(res)
+    }
+    fn expand_query_group(self, input: MacroInput) -> Option<MacroExpansion> {
+        let anchor = "trait ";
+        let pos = input.text.find(anchor)? + anchor.len();
+        let trait_name = input.text[pos..]
+            .chars()
+            .take_while(|c| c.is_alphabetic())
+            .collect::<String>();
+        if trait_name.is_empty() {
+            return None;
+        }
+        let src_range = TextRange::offset_len((pos as u32).into(), TextUnit::of_str(&trait_name));
+        let text = format!(r"trait {} {{ }}", trait_name);
+        let file = SourceFileNode::parse(&text);
+        let trait_def = file.syntax().descendants().find_map(ast::TraitDef::cast)?;
+        let name = trait_def.name()?;
+        let ptr = LocalSyntaxPtr::new(trait_def.syntax());
+        let ranges_map = vec![(src_range, name.syntax().range())];
+        let res = MacroExpansion {
+            text,
+            ranges_map,
+            ptr,
+        };
+        Some(res)
+    }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct MacroInput {
+    // Should be token trees
+    pub text: String,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct MacroExpansion {
+    /// The result of macro expansion. Should be token tree as well.
+    text: String,
+    /// Correspondence between ranges in the original source code and ranges in
+    /// the macro.
+    ranges_map: Vec<(TextRange, TextRange)>,
+    /// Implementation detail: internally, a macro is expanded to the whole file,
+    /// even if it is an expression. This `ptr` selects the actual expansion from
+    /// the expanded file.
+    ptr: LocalSyntaxPtr,
+}
+
+impl MacroExpansion {
+    // FIXME: does not really make sense, macro expansion is not neccessary a
+    // whole file. See `MacroExpansion::ptr` as well.
+    pub(crate) fn file(&self) -> SourceFileNode {
+        SourceFileNode::parse(&self.text)
+    }
+
+    pub fn syntax(&self) -> SyntaxNode {
+        self.ptr.resolve(&self.file())
+    }
+    /// Maps range in the source code to the range in the expanded code.
+    pub fn map_range_forward(&self, src_range: TextRange) -> Option<TextRange> {
+        for (s_range, t_range) in self.ranges_map.iter() {
+            if src_range.is_subrange(&s_range) {
+                let src_at_zero_range = src_range - src_range.start();
+                let src_range_offset = src_range.start() - s_range.start();
+                let src_range = src_at_zero_range + src_range_offset + t_range.start();
+                return Some(src_range);
+            }
+        }
+        None
+    }
+    /// Maps range in the expanded code to the range in the source code.
+    pub fn map_range_back(&self, tgt_range: TextRange) -> Option<TextRange> {
+        for (s_range, t_range) in self.ranges_map.iter() {
+            if tgt_range.is_subrange(&t_range) {
+                let tgt_at_zero_range = tgt_range - tgt_range.start();
+                let tgt_range_offset = tgt_range.start() - t_range.start();
+                let src_range = tgt_at_zero_range + tgt_range_offset + s_range.start();
+                return Some(src_range);
+            }
+        }
+        None
+    }
+}
+
+pub(crate) fn expand_macro_invocation(
+    db: &impl HirDatabase,
+    invoc: MacroCallId,
+) -> Option<Arc<MacroExpansion>> {
+    let loc = invoc.loc(db);
+    let syntax = db.file_item(loc.source_item_id);
+    let syntax = syntax.borrowed();
+    let macro_call = ast::MacroCall::cast(syntax).unwrap();
+
+    let (def, input) = MacroDef::from_call(macro_call)?;
+    def.expand(input).map(Arc::new)
+}
diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs
index a2507c9b52..89b18194ac 100644
--- a/crates/ra_hir/src/mock.rs
+++ b/crates/ra_hir/src/mock.rs
@@ -6,7 +6,7 @@ use ra_db::{LocationIntener, BaseDatabase, FilePosition, FileId, CrateGraph, Sou
 use relative_path::RelativePathBuf;
 use test_utils::{parse_fixture, CURSOR_MARKER, extract_offset};
 
-use crate::{db, DefId, DefLoc};
+use crate::{db, DefId, DefLoc, MacroCallId, MacroCallLoc};
 
 pub const WORKSPACE: SourceRootId = SourceRootId(0);
 
@@ -95,6 +95,7 @@ impl MockDatabase {
 #[derive(Debug, Default)]
 struct IdMaps {
     defs: LocationIntener<DefLoc, DefId>,
+    macros: LocationIntener<MacroCallLoc, MacroCallId>,
 }
 
 impl salsa::Database for MockDatabase {
@@ -144,6 +145,11 @@ impl AsRef<LocationIntener<DefLoc, DefId>> for MockDatabase {
         &self.id_maps.defs
     }
 }
+impl AsRef<LocationIntener<MacroCallLoc, MacroCallId>> for MockDatabase {
+    fn as_ref(&self) -> &LocationIntener<MacroCallLoc, MacroCallId> {
+        &self.id_maps.macros
+    }
+}
 
 impl MockDatabase {
     pub(crate) fn log(&self, f: impl FnOnce()) -> Vec<salsa::Event<MockDatabase>> {
@@ -183,6 +189,8 @@ salsa::database_storage! {
             fn file_lines() for ra_db::FileLinesQuery;
         }
         impl db::HirDatabase {
+            fn hir_source_file() for db::HirSourceFileQuery;
+            fn expand_macro_invocation() for db::ExpandMacroCallQuery;
             fn module_tree() for db::ModuleTreeQuery;
             fn fn_scopes() for db::FnScopesQuery;
             fn file_items() for db::SourceFileItemsQuery;
diff --git a/crates/ra_hir/src/module.rs b/crates/ra_hir/src/module.rs
index 87e30191fb..26171d27c3 100644
--- a/crates/ra_hir/src/module.rs
+++ b/crates/ra_hir/src/module.rs
@@ -15,6 +15,7 @@ use relative_path::RelativePathBuf;
 use crate::{
     Def, DefKind, DefLoc, DefId,
     Name, Path, PathKind, HirDatabase, SourceItemId, SourceFileItemId, Crate,
+    HirFileId,
     arena::{Arena, Id},
 };
 
@@ -48,13 +49,17 @@ impl Module {
     /// Returns `None` for the root module
     pub fn parent_link_source(&self, db: &impl HirDatabase) -> Option<(FileId, ast::ModuleNode)> {
         let link = self.module_id.parent_link(&self.tree)?;
-        let file_id = link.owner(&self.tree).source(&self.tree).file_id();
+        let file_id = link
+            .owner(&self.tree)
+            .source(&self.tree)
+            .file_id()
+            .as_original_file();
         let src = link.bind_source(&self.tree, db);
         Some((file_id, src))
     }
 
-    pub fn source(&self) -> ModuleSource {
-        self.module_id.source(&self.tree)
+    pub fn file_id(&self) -> FileId {
+        self.source().file_id().as_original_file()
     }
 
     /// Parent module. Returns `None` if this is a root module.
@@ -69,7 +74,7 @@ impl Module {
     /// Returns the crate this module is part of.
     pub fn krate(&self, db: &impl HirDatabase) -> Option<Crate> {
         let root_id = self.module_id.crate_root(&self.tree);
-        let file_id = root_id.source(&self.tree).file_id();
+        let file_id = root_id.source(&self.tree).file_id().as_original_file();
         let crate_graph = db.crate_graph();
         let crate_id = crate_graph.crate_id_for_crate_root(file_id)?;
         Some(Crate::new(crate_id))
@@ -162,6 +167,10 @@ impl Module {
     pub fn problems(&self, db: &impl HirDatabase) -> Vec<(SyntaxNode, Problem)> {
         self.module_id.problems(&self.tree, db)
     }
+
+    pub(crate) fn source(&self) -> ModuleSource {
+        self.module_id.source(&self.tree)
+    }
 }
 
 /// Physically, rust source is organized as a set of files, but logically it is
@@ -291,18 +300,18 @@ pub struct ModuleData {
 
 impl ModuleSource {
     // precondition: item_id **must** point to module
-    fn new(file_id: FileId, item_id: Option<SourceFileItemId>) -> ModuleSource {
+    fn new(file_id: HirFileId, item_id: Option<SourceFileItemId>) -> ModuleSource {
         let source_item_id = SourceItemId { file_id, item_id };
         ModuleSource(source_item_id)
     }
 
-    pub(crate) fn new_file(file_id: FileId) -> ModuleSource {
+    pub(crate) fn new_file(file_id: HirFileId) -> ModuleSource {
         ModuleSource::new(file_id, None)
     }
 
     pub(crate) fn new_inline(
         db: &impl HirDatabase,
-        file_id: FileId,
+        file_id: HirFileId,
         m: ast::Module,
     ) -> ModuleSource {
         assert!(!m.has_semi());
@@ -311,7 +320,7 @@ impl ModuleSource {
         ModuleSource::new(file_id, Some(item_id))
     }
 
-    pub fn file_id(self) -> FileId {
+    pub(crate) fn file_id(self) -> HirFileId {
         self.0.file_id
     }
 
diff --git a/crates/ra_hir/src/module/imp.rs b/crates/ra_hir/src/module/imp.rs
index eded85a633..3849026dbd 100644
--- a/crates/ra_hir/src/module/imp.rs
+++ b/crates/ra_hir/src/module/imp.rs
@@ -64,7 +64,7 @@ fn create_module_tree<'a>(
 
     let source_root = db.source_root(source_root);
     for &file_id in source_root.files.values() {
-        let source = ModuleSource::new_file(file_id);
+        let source = ModuleSource::new_file(file_id.into());
         if visited.contains(&source) {
             continue; // TODO: use explicit crate_roots here
         }
@@ -123,7 +123,7 @@ fn build_subtree(
                             visited,
                             roots,
                             Some(link),
-                            ModuleSource::new_file(file_id),
+                            ModuleSource::new_file(file_id.into()),
                         ),
                     })
                     .collect::<Cancelable<Vec<_>>>()?;
@@ -155,7 +155,7 @@ fn resolve_submodule(
     name: &Name,
 ) -> (Vec<FileId>, Option<Problem>) {
     // FIXME: handle submodules of inline modules properly
-    let file_id = source.file_id();
+    let file_id = source.file_id().original_file(db);
     let source_root_id = db.file_source_root(file_id);
     let path = db.file_relative_path(file_id);
     let root = RelativePathBuf::default();
diff --git a/crates/ra_hir/src/module/nameres.rs b/crates/ra_hir/src/module/nameres.rs
index 68eb02a980..40aa33ffa7 100644
--- a/crates/ra_hir/src/module/nameres.rs
+++ b/crates/ra_hir/src/module/nameres.rs
@@ -22,10 +22,10 @@ use ra_syntax::{
     SyntaxKind::{self, *},
     ast::{self, AstNode}
 };
-use ra_db::SourceRootId;
+use ra_db::{SourceRootId, Cancelable, FileId};
 
 use crate::{
-    Cancelable, FileId,
+    HirFileId,
     DefId, DefLoc, DefKind,
     SourceItemId, SourceFileItemId, SourceFileItems,
     Path, PathKind,
@@ -70,7 +70,7 @@ pub struct InputModuleItems {
 
 #[derive(Debug, PartialEq, Eq)]
 struct ModuleItem {
-    id: SourceFileItemId,
+    id: SourceItemId,
     name: Name,
     kind: SyntaxKind,
     vis: Vis,
@@ -95,9 +95,11 @@ pub struct NamedImport {
 }
 
 impl NamedImport {
+    // FIXME: this is only here for one use-case in completion. Seems like a
+    // pretty gross special case.
     pub fn range(&self, db: &impl HirDatabase, file_id: FileId) -> TextRange {
         let source_item_id = SourceItemId {
-            file_id,
+            file_id: file_id.into(),
             item_id: Some(self.file_item_id),
         };
         let syntax = db.file_item(source_item_id);
@@ -209,24 +211,28 @@ impl<T> PerNs<T> {
 }
 
 impl InputModuleItems {
-    pub(crate) fn new<'a>(
+    pub(crate) fn add_item(
+        &mut self,
+        file_id: HirFileId,
         file_items: &SourceFileItems,
-        items: impl Iterator<Item = ast::ModuleItem<'a>>,
-    ) -> InputModuleItems {
-        let mut res = InputModuleItems::default();
-        for item in items {
-            res.add_item(file_items, item);
-        }
-        res
-    }
-
-    fn add_item(&mut self, file_items: &SourceFileItems, item: ast::ModuleItem) -> Option<()> {
+        item: ast::ModuleItem,
+    ) -> Option<()> {
         match item {
-            ast::ModuleItem::StructDef(it) => self.items.push(ModuleItem::new(file_items, it)?),
-            ast::ModuleItem::EnumDef(it) => self.items.push(ModuleItem::new(file_items, it)?),
-            ast::ModuleItem::FnDef(it) => self.items.push(ModuleItem::new(file_items, it)?),
-            ast::ModuleItem::TraitDef(it) => self.items.push(ModuleItem::new(file_items, it)?),
-            ast::ModuleItem::TypeDef(it) => self.items.push(ModuleItem::new(file_items, it)?),
+            ast::ModuleItem::StructDef(it) => {
+                self.items.push(ModuleItem::new(file_id, file_items, it)?)
+            }
+            ast::ModuleItem::EnumDef(it) => {
+                self.items.push(ModuleItem::new(file_id, file_items, it)?)
+            }
+            ast::ModuleItem::FnDef(it) => {
+                self.items.push(ModuleItem::new(file_id, file_items, it)?)
+            }
+            ast::ModuleItem::TraitDef(it) => {
+                self.items.push(ModuleItem::new(file_id, file_items, it)?)
+            }
+            ast::ModuleItem::TypeDef(it) => {
+                self.items.push(ModuleItem::new(file_id, file_items, it)?)
+            }
             ast::ModuleItem::ImplItem(_) => {
                 // impls don't define items
             }
@@ -234,9 +240,15 @@ impl InputModuleItems {
             ast::ModuleItem::ExternCrateItem(_) => {
                 // TODO
             }
-            ast::ModuleItem::ConstDef(it) => self.items.push(ModuleItem::new(file_items, it)?),
-            ast::ModuleItem::StaticDef(it) => self.items.push(ModuleItem::new(file_items, it)?),
-            ast::ModuleItem::Module(it) => self.items.push(ModuleItem::new(file_items, it)?),
+            ast::ModuleItem::ConstDef(it) => {
+                self.items.push(ModuleItem::new(file_id, file_items, it)?)
+            }
+            ast::ModuleItem::StaticDef(it) => {
+                self.items.push(ModuleItem::new(file_id, file_items, it)?)
+            }
+            ast::ModuleItem::Module(it) => {
+                self.items.push(ModuleItem::new(file_id, file_items, it)?)
+            }
         }
         Some(())
     }
@@ -258,11 +270,16 @@ impl InputModuleItems {
 }
 
 impl ModuleItem {
-    fn new<'a>(file_items: &SourceFileItems, item: impl ast::NameOwner<'a>) -> Option<ModuleItem> {
+    fn new<'a>(
+        file_id: HirFileId,
+        file_items: &SourceFileItems,
+        item: impl ast::NameOwner<'a>,
+    ) -> Option<ModuleItem> {
         let name = item.name()?.as_name();
         let kind = item.syntax().kind();
         let vis = Vis::Other;
-        let id = file_items.id_of_unchecked(item.syntax());
+        let item_id = Some(file_items.id_of_unchecked(item.syntax()));
+        let id = SourceItemId { file_id, item_id };
         let res = ModuleItem {
             id,
             name,
@@ -302,7 +319,7 @@ where
 
     pub(crate) fn resolve(mut self) -> Cancelable<ItemMap> {
         for (&module_id, items) in self.input.iter() {
-            self.populate_module(module_id, items)?;
+            self.populate_module(module_id, Arc::clone(items))?;
         }
 
         for &module_id in self.input.keys() {
@@ -312,9 +329,11 @@ where
         Ok(self.result)
     }
 
-    fn populate_module(&mut self, module_id: ModuleId, input: &InputModuleItems) -> Cancelable<()> {
-        let file_id = module_id.source(&self.module_tree).file_id();
-
+    fn populate_module(
+        &mut self,
+        module_id: ModuleId,
+        input: Arc<InputModuleItems>,
+    ) -> Cancelable<()> {
         let mut module_items = ModuleScope::default();
 
         // Populate extern crates prelude
@@ -322,7 +341,8 @@ where
             let root_id = module_id.crate_root(&self.module_tree);
             let file_id = root_id.source(&self.module_tree).file_id();
             let crate_graph = self.db.crate_graph();
-            if let Some(crate_id) = crate_graph.crate_id_for_crate_root(file_id) {
+            if let Some(crate_id) = crate_graph.crate_id_for_crate_root(file_id.as_original_file())
+            {
                 let krate = Crate::new(crate_id);
                 for dep in krate.dependencies(self.db) {
                     if let Some(module) = dep.krate.root_module(self.db)? {
@@ -362,10 +382,7 @@ where
                     kind: k,
                     source_root_id: self.source_root,
                     module_id,
-                    source_item_id: SourceItemId {
-                        file_id,
-                        item_id: Some(item.id),
-                    },
+                    source_item_id: item.id,
                 };
                 def_loc.id(self.db)
             });
diff --git a/crates/ra_hir/src/query_definitions.rs b/crates/ra_hir/src/query_definitions.rs
index 721bd4195a..a5d99bedae 100644
--- a/crates/ra_hir/src/query_definitions.rs
+++ b/crates/ra_hir/src/query_definitions.rs
@@ -8,10 +8,11 @@ use ra_syntax::{
     AstNode, SyntaxNode,
     ast::{self, NameOwner, ModuleItemOwner}
 };
-use ra_db::{SourceRootId, FileId, Cancelable,};
+use ra_db::{SourceRootId, Cancelable,};
 
 use crate::{
-    SourceFileItems, SourceItemId, DefKind, Function, DefId, Name, AsName,
+    SourceFileItems, SourceItemId, DefKind, Function, DefId, Name, AsName, HirFileId,
+    MacroCallLoc,
     db::HirDatabase,
     function::FnScopes,
     module::{
@@ -47,25 +48,17 @@ pub(super) fn enum_data(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<
     Ok(Arc::new(EnumData::new(enum_def.borrowed())))
 }
 
-pub(super) fn file_items(db: &impl HirDatabase, file_id: FileId) -> Arc<SourceFileItems> {
-    let mut res = SourceFileItems::new(file_id);
-    let source_file = db.source_file(file_id);
+pub(super) fn file_items(db: &impl HirDatabase, file_id: HirFileId) -> Arc<SourceFileItems> {
+    let source_file = db.hir_source_file(file_id);
     let source_file = source_file.borrowed();
-    source_file
-        .syntax()
-        .descendants()
-        .filter_map(ast::ModuleItem::cast)
-        .map(|it| it.syntax().owned())
-        .for_each(|it| {
-            res.alloc(it);
-        });
+    let res = SourceFileItems::new(file_id, source_file);
     Arc::new(res)
 }
 
 pub(super) fn file_item(db: &impl HirDatabase, source_item_id: SourceItemId) -> SyntaxNode {
     match source_item_id.item_id {
         Some(id) => db.file_items(source_item_id.file_id)[id].clone(),
-        None => db.source_file(source_item_id.file_id).syntax().owned(),
+        None => db.hir_source_file(source_item_id.file_id).syntax().owned(),
     }
 }
 
@@ -87,7 +80,7 @@ pub(crate) fn submodules(
 
     fn collect_submodules<'a>(
         db: &impl HirDatabase,
-        file_id: FileId,
+        file_id: HirFileId,
         root: impl ast::ModuleItemOwner<'a>,
     ) -> Vec<Submodule> {
         modules(root)
@@ -119,24 +112,48 @@ pub(crate) fn modules<'a>(
 
 pub(super) fn input_module_items(
     db: &impl HirDatabase,
-    source_root: SourceRootId,
+    source_root_id: SourceRootId,
     module_id: ModuleId,
 ) -> Cancelable<Arc<InputModuleItems>> {
-    let module_tree = db.module_tree(source_root)?;
+    let module_tree = db.module_tree(source_root_id)?;
     let source = module_id.source(&module_tree);
-    let file_items = db.file_items(source.file_id());
-    let res = match source.resolve(db) {
-        ModuleSourceNode::SourceFile(it) => {
-            let items = it.borrowed().items();
-            InputModuleItems::new(&file_items, items)
+    let file_id = source.file_id();
+    let file_items = db.file_items(file_id);
+    let fill = |acc: &mut InputModuleItems, items: &mut Iterator<Item = ast::ItemOrMacro>| {
+        for item in items {
+            match item {
+                ast::ItemOrMacro::Item(it) => {
+                    acc.add_item(file_id, &file_items, it);
+                }
+                ast::ItemOrMacro::Macro(macro_call) => {
+                    let item_id = file_items.id_of_unchecked(macro_call.syntax());
+                    let loc = MacroCallLoc {
+                        source_root_id,
+                        module_id,
+                        source_item_id: SourceItemId {
+                            file_id,
+                            item_id: Some(item_id),
+                        },
+                    };
+                    let id = loc.id(db);
+                    let file_id = HirFileId::from(id);
+                    let file_items = db.file_items(file_id);
+                    //FIXME: expand recursively
+                    for item in db.hir_source_file(file_id).borrowed().items() {
+                        acc.add_item(file_id, &file_items, item);
+                    }
+                }
+            }
         }
+    };
+
+    let mut res = InputModuleItems::default();
+    match source.resolve(db) {
+        ModuleSourceNode::SourceFile(it) => fill(&mut res, &mut it.borrowed().items_with_macros()),
         ModuleSourceNode::Module(it) => {
-            let items = it
-                .borrowed()
-                .item_list()
-                .into_iter()
-                .flat_map(|it| it.items());
-            InputModuleItems::new(&file_items, items)
+            if let Some(item_list) = it.borrowed().item_list() {
+                fill(&mut res, &mut item_list.items_with_macros())
+            }
         }
     };
     Ok(Arc::new(res))
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs
index a0d1daf711..24490d119c 100644
--- a/crates/ra_hir/src/source_binder.rs
+++ b/crates/ra_hir/src/source_binder.rs
@@ -20,7 +20,7 @@ use crate::{
 
 /// Locates the module by `FileId`. Picks topmost module in the file.
 pub fn module_from_file_id(db: &impl HirDatabase, file_id: FileId) -> Cancelable<Option<Module>> {
-    let module_source = ModuleSource::new_file(file_id);
+    let module_source = ModuleSource::new_file(file_id.into());
     module_from_source(db, module_source)
 }
 
@@ -50,8 +50,8 @@ pub fn module_from_position(
 ) -> Cancelable<Option<Module>> {
     let file = db.source_file(position.file_id);
     let module_source = match find_node_at_offset::<ast::Module>(file.syntax(), position.offset) {
-        Some(m) if !m.has_semi() => ModuleSource::new_inline(db, position.file_id, m),
-        _ => ModuleSource::new_file(position.file_id),
+        Some(m) if !m.has_semi() => ModuleSource::new_inline(db, position.file_id.into(), m),
+        _ => ModuleSource::new_file(position.file_id.into()),
     };
     module_from_source(db, module_source)
 }
@@ -67,9 +67,9 @@ pub fn module_from_child_node(
         .filter_map(ast::Module::cast)
         .find(|it| !it.has_semi())
     {
-        ModuleSource::new_inline(db, file_id, m)
+        ModuleSource::new_inline(db, file_id.into(), m)
     } else {
-        ModuleSource::new_file(file_id)
+        ModuleSource::new_file(file_id.into())
     };
     module_from_source(db, module_source)
 }
@@ -78,7 +78,7 @@ fn module_from_source(
     db: &impl HirDatabase,
     module_source: ModuleSource,
 ) -> Cancelable<Option<Module>> {
-    let source_root_id = db.file_source_root(module_source.file_id());
+    let source_root_id = db.file_source_root(module_source.file_id().as_original_file());
     let module_tree = db.module_tree(source_root_id)?;
     let m = module_tree
         .modules_with_sources()
diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs
index 8fb6b64083..3e948800ed 100644
--- a/crates/ra_syntax/src/ast.rs
+++ b/crates/ra_syntax/src/ast.rs
@@ -48,10 +48,40 @@ pub trait FnDefOwner<'a>: AstNode<'a> {
     }
 }
 
+// ModuleItem
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum ItemOrMacro<'a> {
+    Item(ModuleItem<'a>),
+    Macro(MacroCall<'a>),
+}
+
+impl<'a> AstNode<'a> for ItemOrMacro<'a> {
+    fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
+        let res = if let Some(item) = ModuleItem::cast(syntax) {
+            ItemOrMacro::Item(item)
+        } else if let Some(macro_call) = MacroCall::cast(syntax) {
+            ItemOrMacro::Macro(macro_call)
+        } else {
+            return None;
+        };
+        Some(res)
+    }
+    fn syntax(self) -> SyntaxNodeRef<'a> {
+        match self {
+            ItemOrMacro::Item(it) => it.syntax(),
+            ItemOrMacro::Macro(it) => it.syntax(),
+        }
+    }
+}
+
 pub trait ModuleItemOwner<'a>: AstNode<'a> {
     fn items(self) -> AstChildren<'a, ModuleItem<'a>> {
         children(self)
     }
+
+    fn items_with_macros(self) -> AstChildren<'a, ItemOrMacro<'a>> {
+        children(self)
+    }
 }
 
 pub trait TypeParamsOwner<'a>: AstNode<'a> {
diff --git a/crates/ra_syntax/src/lib.rs b/crates/ra_syntax/src/lib.rs
index 34a3aabef8..6753c513f3 100644
--- a/crates/ra_syntax/src/lib.rs
+++ b/crates/ra_syntax/src/lib.rs
@@ -51,7 +51,7 @@ use ra_text_edit::AtomTextEdit;
 use crate::yellow::GreenNode;
 
 /// `SourceFileNode` represents a parse tree for a single Rust file.
-pub use crate::ast::SourceFileNode;
+pub use crate::ast::{SourceFile, SourceFileNode};
 
 impl SourceFileNode {
     fn new(green: GreenNode, errors: Vec<SyntaxError>) -> SourceFileNode {


Test patch:
diff --git a/crates/ra_hir/src/module/nameres/tests.rs b/crates/ra_hir/src/module/nameres/tests.rs
index ca20f064f1..a6a0bea316 100644
--- a/crates/ra_hir/src/module/nameres/tests.rs
+++ b/crates/ra_hir/src/module/nameres/tests.rs
@@ -77,6 +77,35 @@ fn item_map_smoke_test() {
     );
 }
 
+#[test]
+fn item_map_contains_items_from_expansions() {
+    let (item_map, module_id) = item_map(
+        "
+        //- /lib.rs
+        mod foo;
+
+        use crate::foo::bar::Baz;
+        <|>
+
+        //- /foo/mod.rs
+        pub mod bar;
+
+        //- /foo/bar.rs
+        salsa::query_group! {
+            trait Baz {}
+        }
+    ",
+    );
+    check_module_item_map(
+        &item_map,
+        module_id,
+        "
+            Baz: t
+            foo: t
+        ",
+    );
+}
+
 #[test]
 fn item_map_using_self() {
     let (item_map, module_id) = item_map(
@@ -141,6 +170,59 @@ fn item_map_across_crates() {
 
 #[test]
 fn typing_inside_a_function_should_not_invalidate_item_map() {
+    let (mut db, pos) = MockDatabase::with_position(
+        "
+        //- /lib.rs
+        mod foo;
+
+        use crate::foo::bar::Baz;
+
+        //- /foo/mod.rs
+        pub mod bar;
+
+        //- /foo/bar.rs
+        <|>
+        salsa::query_group! {
+            trait Baz {
+                fn foo() -> i32 { 1 + 1 }
+            }
+        }
+        ",
+    );
+    let source_root = db.file_source_root(pos.file_id);
+    {
+        let events = db.log_executed(|| {
+            db.item_map(source_root).unwrap();
+        });
+        assert!(format!("{:?}", events).contains("item_map"))
+    }
+
+    let new_text = "
+        salsa::query_group! {
+            trait Baz {
+                fn foo() -> i32 { 92 }
+            }
+        }
+    "
+    .to_string();
+
+    db.query_mut(ra_db::FileTextQuery)
+        .set(pos.file_id, Arc::new(new_text));
+
+    {
+        let events = db.log_executed(|| {
+            db.item_map(source_root).unwrap();
+        });
+        assert!(
+            !format!("{:?}", events).contains("item_map"),
+            "{:#?}",
+            events
+        )
+    }
+}
+
+#[test]
+fn typing_inside_a_function_inside_a_macro_should_not_invalidate_item_map() {
     let (mut db, pos) = MockDatabase::with_position(
         "
         //- /lib.rs
@@ -183,7 +265,7 @@ fn typing_inside_a_function_should_not_invalidate_item_map() {
             db.item_map(source_root).unwrap();
         });
         assert!(
-            !format!("{:?}", events).contains("_item_map"),
+            !format!("{:?}", events).contains("item_map"),
             "{:#?}",
             events
         )