Repository: rust-analyzer/rust-analyzer
Pull request #2381 (2019-11-24 10:39:40)

Add proc-macro crate type handling
Resolves the libproc_macro crate in crates that are the proc-macro type.
This doesn't seem the ideal implementation though, since the compiler still requires you to write `extern crate proc_macro;` (even in 2018 edition).

matklad: bors r+

Thanks!
bors[bot]: # Canceled
JasperDeSutter: Whoops ninja commit there, did a minor cleanup of the matching 😄 
matklad: bors merge

On Sun, 24 Nov 2019 at 18:39, bors[bot] <notifications@github.com> wrote:

> Canceled
>
> —
> You are receiving this because you commented.
> Reply to this email directly, view it on GitHub
> <https://github.com/rust-analyzer/rust-analyzer/pull/2381?email_source=notifications&email_token=AANB3M3V4CIOL6HMCRSKDJ3QVKN4PA5CNFSM4JQ6OL72YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEFAODKQ#issuecomment-557900202>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AANB3M3PZYSUCP2L3KXARU3QVKN4PANCNFSM4JQ6OL7Q>
> .
>

bors[bot]: # Build failed
  * Rust
matklad: Formatting failure, just run `cargo fmt` or use `cargo xtask install-pre-commit-hook` to install a git hook
matklad: bors d+
bors[bot]: :v: JasperDeSutter can now approve this pull request. To approve and merge a pull request, simply reply with `bors r+`. More detailed instructions are available [here](https://bors.tech/documentation/getting-started/#reviewing-pull-requests).

Patch:
diff --git a/crates/ra_db/src/lib.rs b/crates/ra_db/src/lib.rs
index e8852531bf..f9d012cb01 100644
--- a/crates/ra_db/src/lib.rs
+++ b/crates/ra_db/src/lib.rs
@@ -15,20 +15,6 @@ pub use crate::{
 pub use relative_path::{RelativePath, RelativePathBuf};
 pub use salsa;
 
-#[macro_export]
-macro_rules! impl_intern_key {
-    ($name:ident) => {
-        impl $crate::salsa::InternKey for $name {
-            fn from_intern_id(v: $crate::salsa::InternId) -> Self {
-                $name(v)
-            }
-            fn as_intern_id(&self) -> $crate::salsa::InternId {
-                self.0
-            }
-        }
-    };
-}
-
 pub trait CheckCanceled {
     /// Aborts current query if there are pending changes.
     ///
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index b3e2ff1c28..fd7776fb79 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -12,14 +12,13 @@ use hir_def::{
     per_ns::PerNs,
     resolver::{HasResolver, TypeNs},
     type_ref::TypeRef,
-    AstItemDef, ConstId, ContainerId, EnumId, FunctionId, HasModule, ImplId, LocalEnumVariantId,
-    LocalImportId, LocalModuleId, LocalStructFieldId, Lookup, ModuleId, StaticId, StructId,
-    TraitId, TypeAliasId, UnionId,
+    ContainerId, HasModule, ImplId, LocalEnumVariantId, LocalImportId, LocalModuleId,
+    LocalStructFieldId, Lookup, ModuleId, UnionId,
 };
 use hir_expand::{
     diagnostics::DiagnosticSink,
     name::{self, AsName},
-    AstId, MacroDefId,
+    AstId,
 };
 use ra_db::{CrateId, Edition, FileId, FilePosition};
 use ra_syntax::{ast, AstNode, SyntaxNode};
@@ -27,6 +26,10 @@ use ra_syntax::{ast, AstNode, SyntaxNode};
 use crate::{
     db::{DefDatabase, HirDatabase},
     expr::{BindingAnnotation, Body, BodySourceMap, ExprValidator, Pat, PatId},
+    ids::{
+        AstItemDef, ConstId, EnumId, FunctionId, MacroDefId, StaticId, StructId, TraitId,
+        TypeAliasId,
+    },
     ty::{InferenceResult, Namespace, TraitRef},
     Either, HasSource, Name, Source, Ty,
 };
@@ -734,7 +737,7 @@ pub struct Static {
 
 impl Static {
     pub fn module(self, db: &impl DefDatabase) -> Module {
-        Module { id: self.id.lookup(db).module(db) }
+        Module { id: self.id.module(db) }
     }
 
     pub fn krate(self, db: &impl DefDatabase) -> Option<Crate> {
@@ -1037,7 +1040,7 @@ impl From<PerNs> for ScopeDef {
             .or_else(|| def.take_values())
             .map(|module_def_id| ScopeDef::ModuleDef(module_def_id.into()))
             .or_else(|| {
-                def.take_macros().map(|macro_def_id| ScopeDef::MacroDef(macro_def_id.into()))
+                def.get_macros().map(|macro_def_id| ScopeDef::MacroDef(macro_def_id.into()))
             })
             .unwrap_or(ScopeDef::Unknown)
     }
diff --git a/crates/ra_hir/src/code_model/src.rs b/crates/ra_hir/src/code_model/src.rs
index b9d21bdd72..b7bafe23df 100644
--- a/crates/ra_hir/src/code_model/src.rs
+++ b/crates/ra_hir/src/code_model/src.rs
@@ -1,11 +1,12 @@
 //! FIXME: write short doc here
 
-use hir_def::{AstItemDef, HasChildSource, HasSource as _, Lookup, VariantId};
+use hir_def::{HasChildSource, HasSource as _, Lookup, VariantId};
 use hir_expand::either::Either;
 use ra_syntax::ast::{self, AstNode};
 
 use crate::{
     db::{DefDatabase, HirDatabase},
+    ids::AstItemDef,
     Const, Enum, EnumVariant, FieldSource, Function, HasBody, Import, MacroDef, Module,
     ModuleSource, Static, Struct, StructField, Trait, TypeAlias, Union,
 };
@@ -88,7 +89,7 @@ impl HasSource for Const {
 impl HasSource for Static {
     type Ast = ast::StaticDef;
     fn source(self, db: &impl DefDatabase) -> Source<ast::StaticDef> {
-        self.id.lookup(db).source(db)
+        self.id.source(db)
     }
 }
 impl HasSource for Trait {
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs
index a9dab2d250..3ae5df8d5e 100644
--- a/crates/ra_hir/src/db.rs
+++ b/crates/ra_hir/src/db.rs
@@ -5,6 +5,7 @@ use std::sync::Arc;
 use ra_db::salsa;
 
 use crate::{
+    ids,
     ty::{
         method_resolution::CrateImplBlocks,
         traits::{AssocTyValue, Impl},
@@ -70,14 +71,11 @@ pub trait HirDatabase: DefDatabase {
 
     // Interned IDs for Chalk integration
     #[salsa::interned]
-    fn intern_type_ctor(&self, type_ctor: TypeCtor) -> crate::ty::TypeCtorId;
+    fn intern_type_ctor(&self, type_ctor: TypeCtor) -> ids::TypeCtorId;
     #[salsa::interned]
-    fn intern_chalk_impl(&self, impl_: Impl) -> crate::ty::traits::GlobalImplId;
+    fn intern_chalk_impl(&self, impl_: Impl) -> ids::GlobalImplId;
     #[salsa::interned]
-    fn intern_assoc_ty_value(
-        &self,
-        assoc_ty_value: AssocTyValue,
-    ) -> crate::ty::traits::AssocTyValueId;
+    fn intern_assoc_ty_value(&self, assoc_ty_value: AssocTyValue) -> ids::AssocTyValueId;
 
     #[salsa::invoke(crate::ty::traits::chalk::associated_ty_data_query)]
     fn associated_ty_data(
diff --git a/crates/ra_hir/src/debug.rs b/crates/ra_hir/src/debug.rs
index 7a2810f71a..8ec371f6eb 100644
--- a/crates/ra_hir/src/debug.rs
+++ b/crates/ra_hir/src/debug.rs
@@ -22,10 +22,9 @@
 
 use std::fmt;
 
-use hir_expand::HirFileId;
 use ra_db::{CrateId, FileId};
 
-use crate::{db::HirDatabase, Crate, Module, Name};
+use crate::{db::HirDatabase, Crate, HirFileId, Module, Name};
 
 impl Crate {
     pub fn debug(self, db: &impl HirDebugDatabase) -> impl fmt::Debug + '_ {
diff --git a/crates/ra_hir/src/diagnostics.rs b/crates/ra_hir/src/diagnostics.rs
index dafacba70c..7d1b648589 100644
--- a/crates/ra_hir/src/diagnostics.rs
+++ b/crates/ra_hir/src/diagnostics.rs
@@ -2,10 +2,9 @@
 
 use std::any::Any;
 
-use hir_expand::HirFileId;
 use ra_syntax::{ast, AstNode, AstPtr, SyntaxNodePtr};
 
-use crate::{db::AstDatabase, Name, Source};
+use crate::{db::AstDatabase, HirFileId, Name, Source};
 
 pub use hir_def::diagnostics::UnresolvedModule;
 pub use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink};
diff --git a/crates/ra_hir/src/from_source.rs b/crates/ra_hir/src/from_source.rs
index f506bba705..b86307c58a 100644
--- a/crates/ra_hir/src/from_source.rs
+++ b/crates/ra_hir/src/from_source.rs
@@ -1,6 +1,6 @@
 //! FIXME: write short doc here
 
-use hir_def::{AstItemDef, LocationCtx, ModuleId, StructId, StructOrUnionId, UnionId};
+use hir_def::{ModuleId, StructId, StructOrUnionId, UnionId};
 use hir_expand::{name::AsName, AstId, MacroDefId, MacroDefKind};
 use ra_syntax::{
     ast::{self, AstNode, NameOwner},
@@ -9,6 +9,7 @@ use ra_syntax::{
 
 use crate::{
     db::{AstDatabase, DefDatabase, HirDatabase},
+    ids::{AstItemDef, LocationCtx},
     AssocItem, Const, DefWithBody, Enum, EnumVariant, FieldSource, Function, HasBody, HasSource,
     ImplBlock, Local, MacroDef, Module, ModuleDef, ModuleSource, Source, Static, Struct,
     StructField, Trait, TypeAlias, Union, VariantDef,
@@ -104,21 +105,10 @@ impl FromSource for Const {
 impl FromSource for Static {
     type Ast = ast::StaticDef;
     fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> {
-        let module = match Container::find(db, src.as_ref().map(|it| it.syntax()))? {
-            Container::Module(it) => it,
-            Container::Trait(_) | Container::ImplBlock(_) => return None,
-        };
-        module
-            .declarations(db)
-            .into_iter()
-            .filter_map(|it| match it {
-                ModuleDef::Static(it) => Some(it),
-                _ => None,
-            })
-            .find(|it| same_source(&it.source(db), &src))
+        let id = from_source(db, src)?;
+        Some(Static { id })
     }
 }
-
 impl FromSource for TypeAlias {
     type Ast = ast::TypeAliasDef;
     fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> {
@@ -282,9 +272,7 @@ where
     let module_src = ModuleSource::from_child_node(db, src.as_ref().map(|it| it.syntax()));
     let module = Module::from_definition(db, Source::new(src.file_id, module_src))?;
     let ctx = LocationCtx::new(db, module.id, src.file_id);
-    let items = db.ast_id_map(src.file_id);
-    let item_id = items.ast_id(&src.value);
-    Some(DEF::from_ast_id(ctx, item_id))
+    Some(DEF::from_ast(ctx, &src.value))
 }
 
 enum Container {
diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs
new file mode 100644
index 0000000000..2b59365fb6
--- /dev/null
+++ b/crates/ra_hir/src/ids.rs
@@ -0,0 +1,45 @@
+//! hir makes 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`.
+
+use ra_db::salsa;
+
+pub use hir_def::{
+    AstItemDef, ConstId, EnumId, FunctionId, ItemLoc, LocationCtx, StaticId, StructId, TraitId,
+    TypeAliasId,
+};
+pub use hir_expand::{HirFileId, MacroCallId, MacroCallLoc, MacroDefId, MacroFile, MacroFileKind};
+
+macro_rules! impl_intern_key {
+    ($name:ident) => {
+        impl salsa::InternKey for $name {
+            fn from_intern_id(v: salsa::InternId) -> Self {
+                $name(v)
+            }
+            fn as_intern_id(&self) -> salsa::InternId {
+                self.0
+            }
+        }
+    };
+}
+
+/// This exists just for Chalk, because Chalk just has a single `StructId` where
+/// we have different kinds of ADTs, primitive types and special type
+/// constructors like tuples and function pointers.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct TypeCtorId(salsa::InternId);
+impl_intern_key!(TypeCtorId);
+
+/// This exists just for Chalk, because our ImplIds are only unique per module.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct GlobalImplId(salsa::InternId);
+impl_intern_key!(GlobalImplId);
+
+/// This exists just for Chalk, because it needs a unique ID for each associated
+/// type value in an impl (even synthetic ones).
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct AssocTyValueId(salsa::InternId);
+impl_intern_key!(AssocTyValueId);
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs
index 8c589c7282..e51d4d063c 100644
--- a/crates/ra_hir/src/lib.rs
+++ b/crates/ra_hir/src/lib.rs
@@ -31,6 +31,7 @@ pub mod debug;
 pub mod db;
 pub mod source_binder;
 
+mod ids;
 mod ty;
 mod impl_block;
 mod expr;
@@ -57,6 +58,7 @@ pub use crate::{
     },
     expr::ExprScopes,
     from_source::FromSource,
+    ids::{HirFileId, MacroCallId, MacroCallLoc, MacroDefId, MacroFile},
     source_binder::{PathResolution, ScopeEntryWithSyntax, SourceAnalyzer},
     ty::{
         display::HirDisplay,
@@ -71,6 +73,4 @@ pub use hir_def::{
     path::{Path, PathKind},
     type_ref::Mutability,
 };
-pub use hir_expand::{
-    either::Either, name::Name, HirFileId, MacroCallId, MacroCallLoc, MacroDefId, MacroFile, Source,
-};
+pub use hir_expand::{either::Either, name::Name, Source};
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs
index cfc4bd3260..797f90d501 100644
--- a/crates/ra_hir/src/source_binder.rs
+++ b/crates/ra_hir/src/source_binder.rs
@@ -13,9 +13,7 @@ use hir_def::{
     resolver::{self, resolver_for_scope, HasResolver, Resolver, TypeNs, ValueNs},
     DefWithBodyId,
 };
-use hir_expand::{
-    name::AsName, AstId, HirFileId, MacroCallId, MacroCallLoc, MacroFileKind, Source,
-};
+use hir_expand::{name::AsName, AstId, MacroCallId, MacroCallLoc, MacroFileKind, Source};
 use ra_syntax::{
     ast::{self, AstNode},
     match_ast, AstPtr,
@@ -26,10 +24,11 @@ use ra_syntax::{
 use crate::{
     db::HirDatabase,
     expr::{BodySourceMap, ExprScopes, ScopeId},
+    ids::LocationCtx,
     ty::method_resolution::{self, implements_trait},
     Adt, AssocItem, Const, DefWithBody, Either, Enum, EnumVariant, FromSource, Function,
-    GenericParam, HasBody, Local, MacroDef, Name, Path, ScopeDef, Static, Struct, Trait, Ty,
-    TypeAlias,
+    GenericParam, HasBody, HirFileId, Local, MacroDef, Module, Name, Path, ScopeDef, Static,
+    Struct, Trait, Ty, TypeAlias,
 };
 
 fn try_get_resolver_for_node(db: &impl HirDatabase, node: Source<&SyntaxNode>) -> Option<Resolver> {
@@ -68,12 +67,16 @@ fn def_with_body_from_child_node(
     db: &impl HirDatabase,
     child: Source<&SyntaxNode>,
 ) -> Option<DefWithBody> {
+    let module_source = crate::ModuleSource::from_child_node(db, child);
+    let module = Module::from_definition(db, Source::new(child.file_id, module_source))?;
+    let ctx = LocationCtx::new(db, module.id, child.file_id);
+
     child.value.ancestors().find_map(|node| {
         match_ast! {
             match node {
                 ast::FnDef(def)  => { return Function::from_source(db, child.with_value(def)).map(DefWithBody::from); },
                 ast::ConstDef(def) => { return Const::from_source(db, child.with_value(def)).map(DefWithBody::from); },
-                ast::StaticDef(def) => { return Static::from_source(db, child.with_value(def)).map(DefWithBody::from); },
+                ast::StaticDef(def) => { Some(Static { id: ctx.to_def(&def) }.into()) },
                 _ => { None },
             }
         }
@@ -541,7 +544,7 @@ fn adjust(
 }
 
 /// Given a `ast::MacroCall`, return what `MacroKindFile` it belongs to.
-/// FIXME: Not completed
+/// FIXME: Not completed  
 fn to_macro_file_kind(macro_call: &ast::MacroCall) -> MacroFileKind {
     let syn = macro_call.syntax();
     let parent = match syn.parent() {
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs
index 309bd2727d..95b8df1810 100644
--- a/crates/ra_hir/src/ty.rs
+++ b/crates/ra_hir/src/ty.rs
@@ -18,7 +18,6 @@ use std::sync::Arc;
 use std::{fmt, iter, mem};
 
 use hir_def::{generics::GenericParams, AdtId};
-use ra_db::{impl_intern_key, salsa};
 
 use crate::{
     db::HirDatabase, expr::ExprId, util::make_mut_slice, Adt, Crate, DefWithBody, FloatTy,
@@ -115,13 +114,6 @@ pub enum TypeCtor {
     Closure { def: DefWithBody, expr: ExprId },
 }
 
-/// This exists just for Chalk, because Chalk just has a single `StructId` where
-/// we have different kinds of ADTs, primitive types and special type
-/// constructors like tuples and function pointers.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct TypeCtorId(salsa::InternId);
-impl_intern_key!(TypeCtorId);
-
 impl TypeCtor {
     pub fn num_ty_params(self, db: &impl HirDatabase) -> usize {
         match self {
diff --git a/crates/ra_hir/src/ty/traits.rs b/crates/ra_hir/src/ty/traits.rs
index 268fa09e4a..45f7254380 100644
--- a/crates/ra_hir/src/ty/traits.rs
+++ b/crates/ra_hir/src/ty/traits.rs
@@ -3,7 +3,7 @@ use std::sync::{Arc, Mutex};
 
 use chalk_ir::{cast::Cast, family::ChalkIr};
 use log::debug;
-use ra_db::{impl_intern_key, salsa};
+use ra_db::salsa;
 use ra_prof::profile;
 use rustc_hash::FxHashSet;
 
@@ -304,10 +304,6 @@ pub enum Impl {
     /// Closure types implement the Fn traits synthetically.
     ClosureFnTraitImpl(ClosureFnTraitImplData),
 }
-/// This exists just for Chalk, because our ImplIds are only unique per module.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct GlobalImplId(salsa::InternId);
-impl_intern_key!(GlobalImplId);
 
 /// An associated type value. Usually this comes from a `type` declaration
 /// inside an impl block, but for built-in impls we have to synthesize it.
@@ -319,8 +315,3 @@ pub enum AssocTyValue {
     /// The output type of the Fn trait implementation.
     ClosureFnTraitImplOutput(ClosureFnTraitImplData),
 }
-/// This exists just for Chalk, because it needs a unique ID for each associated
-/// type value in an impl (even synthetic ones).
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct AssocTyValueId(salsa::InternId);
-impl_intern_key!(AssocTyValueId);
diff --git a/crates/ra_hir/src/ty/traits/chalk.rs b/crates/ra_hir/src/ty/traits/chalk.rs
index 9efdc53c45..53818a5e54 100644
--- a/crates/ra_hir/src/ty/traits/chalk.rs
+++ b/crates/ra_hir/src/ty/traits/chalk.rs
@@ -847,38 +847,38 @@ fn id_to_chalk<T: InternKey>(salsa_id: T) -> chalk_ir::RawId {
     chalk_ir::RawId { index: salsa_id.as_intern_id().as_u32() }
 }
 
-impl From<chalk_ir::StructId> for crate::ty::TypeCtorId {
+impl From<chalk_ir::StructId> for crate::ids::TypeCtorId {
     fn from(struct_id: chalk_ir::StructId) -> Self {
         id_from_chalk(struct_id.0)
     }
 }
 
-impl From<crate::ty::TypeCtorId> for chalk_ir::StructId {
-    fn from(type_ctor_id: crate::ty::TypeCtorId) -> Self {
+impl From<crate::ids::TypeCtorId> for chalk_ir::StructId {
+    fn from(type_ctor_id: crate::ids::TypeCtorId) -> Self {
         chalk_ir::StructId(id_to_chalk(type_ctor_id))
     }
 }
 
-impl From<chalk_ir::ImplId> for crate::ty::traits::GlobalImplId {
+impl From<chalk_ir::ImplId> for crate::ids::GlobalImplId {
     fn from(impl_id: chalk_ir::ImplId) -> Self {
         id_from_chalk(impl_id.0)
     }
 }
 
-impl From<crate::ty::traits::GlobalImplId> for chalk_ir::ImplId {
-    fn from(impl_id: crate::ty::traits::GlobalImplId) -> Self {
+impl From<crate::ids::GlobalImplId> for chalk_ir::ImplId {
+    fn from(impl_id: crate::ids::GlobalImplId) -> Self {
         chalk_ir::ImplId(id_to_chalk(impl_id))
     }
 }
 
-impl From<chalk_rust_ir::AssociatedTyValueId> for crate::ty::traits::AssocTyValueId {
+impl From<chalk_rust_ir::AssociatedTyValueId> for crate::ids::AssocTyValueId {
     fn from(id: chalk_rust_ir::AssociatedTyValueId) -> Self {
         id_from_chalk(id.0)
     }
 }
 
-impl From<crate::ty::traits::AssocTyValueId> for chalk_rust_ir::AssociatedTyValueId {
-    fn from(assoc_ty_value_id: crate::ty::traits::AssocTyValueId) -> Self {
+impl From<crate::ids::AssocTyValueId> for chalk_rust_ir::AssociatedTyValueId {
+    fn from(assoc_ty_value_id: crate::ids::AssocTyValueId) -> Self {
         chalk_rust_ir::AssociatedTyValueId(id_to_chalk(assoc_ty_value_id))
     }
 }
diff --git a/crates/ra_hir_def/src/attr.rs b/crates/ra_hir_def/src/attr.rs
index 53456fc085..48ce8cd936 100644
--- a/crates/ra_hir_def/src/attr.rs
+++ b/crates/ra_hir_def/src/attr.rs
@@ -2,8 +2,9 @@
 
 use std::{ops, sync::Arc};
 
-use hir_expand::{either::Either, hygiene::Hygiene, AstId, Source};
+use hir_expand::{either::Either, hygiene::Hygiene, AstId};
 use mbe::ast_to_token_tree;
+use ra_cfg::CfgOptions;
 use ra_syntax::{
     ast::{self, AstNode, AttrsOwner},
     SmolStr,
@@ -39,53 +40,50 @@ impl Attrs {
                     Some(it) => it,
                     None => return Attrs::default(),
                 };
-                Attrs::from_attrs_owner(db, src.as_ref().map(|it| it as &dyn AttrsOwner))
+                let hygiene = Hygiene::new(db, src.file_id);
+                Attr::from_attrs_owner(&src.value, &hygiene)
             }
             AttrDefId::StructFieldId(it) => {
                 let src = it.parent.child_source(db);
                 match &src.value[it.local_id] {
                     Either::A(_tuple) => Attrs::default(),
-                    Either::B(record) => Attrs::from_attrs_owner(db, src.with_value(record)),
+                    Either::B(record) => {
+                        let hygiene = Hygiene::new(db, src.file_id);
+                        Attr::from_attrs_owner(record, &hygiene)
+                    }
                 }
             }
-            AttrDefId::EnumVariantId(var_id) => {
-                let src = var_id.parent.child_source(db);
-                let src = src.as_ref().map(|it| &it[var_id.local_id]);
-                Attrs::from_attrs_owner(db, src.map(|it| it as &dyn AttrsOwner))
+            AttrDefId::EnumVariantId(it) => {
+                let src = it.parent.child_source(db);
+                let hygiene = Hygiene::new(db, src.file_id);
+                Attr::from_attrs_owner(&src.value[it.local_id], &hygiene)
             }
             AttrDefId::AdtId(it) => match it {
                 AdtId::StructId(it) => attrs_from_ast(it.0.lookup_intern(db).ast_id, db),
                 AdtId::EnumId(it) => attrs_from_ast(it.lookup_intern(db).ast_id, db),
                 AdtId::UnionId(it) => attrs_from_ast(it.0.lookup_intern(db).ast_id, db),
             },
+            AttrDefId::StaticId(it) => attrs_from_ast(it.lookup_intern(db).ast_id, db),
             AttrDefId::TraitId(it) => attrs_from_ast(it.lookup_intern(db).ast_id, db),
             AttrDefId::MacroDefId(it) => attrs_from_ast(it.ast_id, db),
             AttrDefId::ImplId(it) => attrs_from_ast(it.lookup_intern(db).ast_id, 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),
         }
     }
 
-    fn from_attrs_owner(db: &impl DefDatabase, owner: Source<&dyn AttrsOwner>) -> Attrs {
-        let hygiene = Hygiene::new(db, owner.file_id);
-        Attrs::new(owner.value, &hygiene)
-    }
-
-    pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Attrs {
-        let mut attrs = owner.attrs().peekable();
-        let entries = if attrs.peek().is_none() {
-            // Avoid heap allocation
-            None
-        } else {
-            Some(attrs.flat_map(|ast| Attr::from_src(ast, hygiene)).collect())
-        };
-        Attrs { entries }
+    pub fn has_atom(&self, atom: &str) -> bool {
+        self.iter().any(|it| it.is_simple_atom(atom))
     }
 
-    pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> {
-        AttrQuery { attrs: self, key }
+    pub fn find_string_value(&self, key: &str) -> Option<SmolStr> {
+        self.iter().filter(|attr| attr.is_simple_atom(key)).find_map(|attr| {
+            match attr.input.as_ref()? {
+                AttrInput::Literal(it) => Some(it.clone()),
+                _ => None,
+            }
+        })
     }
 }
 
@@ -102,7 +100,7 @@ pub enum AttrInput {
 }
 
 impl Attr {
-    fn from_src(ast: ast::Attr, hygiene: &Hygiene) -> Option<Attr> {
+    pub(crate) fn from_src(ast: ast::Attr, hygiene: &Hygiene) -> Option<Attr> {
         let path = Path::from_src(ast.path()?, hygiene)?;
         let input = match ast.input() {
             None => None,
@@ -118,37 +116,46 @@ impl Attr {
 
         Some(Attr { path, input })
     }
-}
 
-pub struct AttrQuery<'a> {
-    attrs: &'a Attrs,
-    key: &'static str,
-}
+    pub fn from_attrs_owner(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Attrs {
+        let mut attrs = owner.attrs().peekable();
+        let entries = if attrs.peek().is_none() {
+            // Avoid heap allocation
+            None
+        } else {
+            Some(attrs.flat_map(|ast| Attr::from_src(ast, hygiene)).collect())
+        };
+        Attrs { entries }
+    }
 
-impl<'a> AttrQuery<'a> {
-    pub fn tt_values(self) -> impl Iterator<Item = &'a Subtree> {
-        self.attrs().filter_map(|attr| match attr.input.as_ref()? {
-            AttrInput::TokenTree(it) => Some(it),
-            _ => None,
-        })
+    pub fn is_simple_atom(&self, name: &str) -> bool {
+        // FIXME: Avoid cloning
+        self.path.as_ident().map_or(false, |s| s.to_string() == name)
     }
 
-    pub fn string_value(self) -> Option<&'a SmolStr> {
-        self.attrs().find_map(|attr| match attr.input.as_ref()? {
-            AttrInput::Literal(it) => Some(it),
+    // FIXME: handle cfg_attr :-)
+    pub fn as_cfg(&self) -> Option<&Subtree> {
+        if !self.is_simple_atom("cfg") {
+            return None;
+        }
+        match &self.input {
+            Some(AttrInput::TokenTree(subtree)) => Some(subtree),
             _ => None,
-        })
+        }
     }
 
-    pub fn exists(self) -> bool {
-        self.attrs().next().is_some()
+    pub fn as_path(&self) -> Option<&SmolStr> {
+        if !self.is_simple_atom("path") {
+            return None;
+        }
+        match &self.input {
+            Some(AttrInput::Literal(it)) => Some(it),
+            _ => None,
+        }
     }
 
-    fn attrs(self) -> impl Iterator<Item = &'a Attr> {
-        let key = self.key;
-        self.attrs
-            .iter()
-            .filter(move |attr| attr.path.as_ident().map_or(false, |s| s.to_string() == key))
+    pub fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> Option<bool> {
+        cfg_options.is_cfg_enabled(self.as_cfg()?)
     }
 }
 
@@ -157,8 +164,8 @@ where
     N: ast::AttrsOwner,
     D: DefDatabase,
 {
-    let src = Source::new(src.file_id(), src.to_node(db));
-    Attrs::from_attrs_owner(db, src.as_ref().map(|it| it as &dyn AttrsOwner))
+    let hygiene = Hygiene::new(db, src.file_id());
+    Attr::from_attrs_owner(&src.to_node(db), &hygiene)
 }
 
 fn attrs_from_loc<T, D>(node: T, db: &D) -> Attrs
@@ -168,5 +175,6 @@ where
     D: DefDatabase,
 {
     let src = node.source(db);
-    Attrs::from_attrs_owner(db, src.as_ref().map(|it| it as &dyn AttrsOwner))
+    let hygiene = Hygiene::new(db, src.file_id);
+    Attr::from_attrs_owner(&src.value, &hygiene)
 }
diff --git a/crates/ra_hir_def/src/body.rs b/crates/ra_hir_def/src/body.rs
index c06997cf10..225638b422 100644
--- a/crates/ra_hir_def/src/body.rs
+++ b/crates/ra_hir_def/src/body.rs
@@ -17,7 +17,7 @@ use crate::{
     expr::{Expr, ExprId, Pat, PatId},
     nameres::CrateDefMap,
     path::Path,
-    DefWithBodyId, HasModule, HasSource, Lookup, ModuleId,
+    AstItemDef, DefWithBodyId, HasModule, HasSource, Lookup, ModuleId,
 };
 
 pub struct Expander {
@@ -82,7 +82,7 @@ impl Expander {
     }
 
     fn resolve_path_as_macro(&self, db: &impl DefDatabase, path: &Path) -> Option<MacroDefId> {
-        self.crate_def_map.resolve_path(db, self.module.module_id, path).0.take_macros()
+        self.crate_def_map.resolve_path(db, self.module.module_id, path).0.get_macros()
     }
 }
 
@@ -160,7 +160,6 @@ impl Body {
                 (src.file_id, c.module(db), src.value.body())
             }
             DefWithBodyId::StaticId(s) => {
-                let s = s.lookup(db);
                 let src = s.source(db);
                 (src.file_id, s.module(db), src.value.body())
             }
diff --git a/crates/ra_hir_def/src/data.rs b/crates/ra_hir_def/src/data.rs
index 68bea34df7..f0b3e198a3 100644
--- a/crates/ra_hir_def/src/data.rs
+++ b/crates/ra_hir_def/src/data.rs
@@ -200,17 +200,18 @@ pub struct ConstData {
 impl ConstData {
     pub(crate) fn const_data_query(db: &impl DefDatabase, konst: ConstId) -> Arc<ConstData> {
         let node = konst.lookup(db).source(db).value;
-        Arc::new(ConstData::new(&node))
+        const_data_for(&node)
     }
 
     pub(crate) fn static_data_query(db: &impl DefDatabase, konst: StaticId) -> Arc<ConstData> {
-        let node = konst.lookup(db).source(db).value;
-        Arc::new(ConstData::new(&node))
+        let node = konst.source(db).value;
+        const_data_for(&node)
     }
+}
 
-    fn new<N: NameOwner + TypeAscriptionOwner>(node: &N) -> ConstData {
-        let name = node.name().map(|n| n.as_name());
-        let type_ref = TypeRef::from_ast_opt(node.ascribed_type());
-        ConstData { name, type_ref }
-    }
+fn const_data_for<N: NameOwner + TypeAscriptionOwner>(node: &N) -> Arc<ConstData> {
+    let name = node.name().map(|n| n.as_name());
+    let type_ref = TypeRef::from_ast_opt(node.ascribed_type());
+    let sig = ConstData { name, type_ref };
+    Arc::new(sig)
 }
diff --git a/crates/ra_hir_def/src/db.rs b/crates/ra_hir_def/src/db.rs
index 32adb11bdc..7fec2e8c01 100644
--- a/crates/ra_hir_def/src/db.rs
+++ b/crates/ra_hir_def/src/db.rs
@@ -18,8 +18,7 @@ use crate::{
         CrateDefMap,
     },
     AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, FunctionId, FunctionLoc, GenericDefId,
-    ImplId, ItemLoc, ModuleId, StaticId, StaticLoc, StructOrUnionId, TraitId, TypeAliasId,
-    TypeAliasLoc,
+    ImplId, ItemLoc, ModuleId, StaticId, StructOrUnionId, TraitId, TypeAliasId, TypeAliasLoc,
 };
 
 #[salsa::query_group(InternDatabaseStorage)]
@@ -33,7 +32,7 @@ pub trait InternDatabase: SourceDatabase {
     #[salsa::interned]
     fn intern_const(&self, loc: ConstLoc) -> ConstId;
     #[salsa::interned]
-    fn intern_static(&self, loc: StaticLoc) -> StaticId;
+    fn intern_static(&self, loc: ItemLoc<ast::StaticDef>) -> StaticId;
     #[salsa::interned]
     fn intern_trait(&self, loc: ItemLoc<ast::TraitDef>) -> TraitId;
     #[salsa::interned]
diff --git a/crates/ra_hir_def/src/docs.rs b/crates/ra_hir_def/src/docs.rs
index 90a8627bc6..69846fd1b2 100644
--- a/crates/ra_hir_def/src/docs.rs
+++ b/crates/ra_hir_def/src/docs.rs
@@ -1,7 +1,4 @@
-//! Defines hir documentation.
-//!
-//! This really shouldn't exist, instead, we should deshugar doc comments into attributes, see
-//! https://github.com/rust-analyzer/rust-analyzer/issues/2148#issuecomment-550519102
+//! FIXME: write short doc here
 
 use std::sync::Arc;
 
@@ -55,10 +52,10 @@ impl Documentation {
                 let src = it.parent.child_source(db);
                 docs_from_ast(&src.value[it.local_id])
             }
+            AttrDefId::StaticId(it) => docs_from_ast(&it.source(db).value),
             AttrDefId::TraitId(it) => docs_from_ast(&it.source(db).value),
             AttrDefId::MacroDefId(it) => docs_from_ast(&it.ast_id.to_node(db)),
             AttrDefId::ConstId(it) => docs_from_ast(&it.lookup(db).source(db).value),
-            AttrDefId::StaticId(it) => docs_from_ast(&it.lookup(db).source(db).value),
             AttrDefId::FunctionId(it) => docs_from_ast(&it.lookup(db).source(db).value),
             AttrDefId::TypeAliasId(it) => docs_from_ast(&it.lookup(db).source(db).value),
             AttrDefId::ImplId(_) => None,
diff --git a/crates/ra_hir_def/src/lang_item.rs b/crates/ra_hir_def/src/lang_item.rs
index 3b9fb03285..df951c5330 100644
--- a/crates/ra_hir_def/src/lang_item.rs
+++ b/crates/ra_hir_def/src/lang_item.rs
@@ -113,8 +113,8 @@ impl LangItems {
         T: Into<AttrDefId> + Copy,
     {
         let attrs = db.attrs(item.into());
-        if let Some(lang_item_name) = attrs.by_key("lang").string_value() {
-            self.items.entry(lang_item_name.clone()).or_insert_with(|| constructor(item));
+        if let Some(lang_item_name) = attrs.find_string_value("lang") {
+            self.items.entry(lang_item_name).or_insert_with(|| constructor(item));
         }
     }
 }
diff --git a/crates/ra_hir_def/src/lib.rs b/crates/ra_hir_def/src/lib.rs
index f63c3dd64b..1d195d65d0 100644
--- a/crates/ra_hir_def/src/lib.rs
+++ b/crates/ra_hir_def/src/lib.rs
@@ -8,23 +8,20 @@
 //! actually true.
 
 pub mod db;
-
 pub mod attr;
 pub mod path;
 pub mod type_ref;
 pub mod builtin_type;
-pub mod diagnostics;
-pub mod per_ns;
-
 pub mod adt;
-pub mod data;
-pub mod generics;
-pub mod lang_item;
-pub mod docs;
-
+pub mod diagnostics;
 pub mod expr;
 pub mod body;
+pub mod generics;
 pub mod resolver;
+pub mod data;
+pub mod lang_item;
+pub mod docs;
+pub mod per_ns;
 
 mod trace;
 mod nameres;
@@ -38,7 +35,7 @@ use std::hash::{Hash, Hasher};
 
 use hir_expand::{ast_id_map::FileAstId, db::AstDatabase, AstId, HirFileId, MacroDefId, Source};
 use ra_arena::{impl_arena_id, map::ArenaMap, RawId};
-use ra_db::{impl_intern_key, salsa, CrateId};
+use ra_db::{salsa, CrateId};
 use ra_syntax::{ast, AstNode};
 
 use crate::{builtin_type::BuiltinType, db::InternDatabase};
@@ -59,6 +56,19 @@ pub struct ModuleId {
 pub struct LocalModuleId(RawId);
 impl_arena_id!(LocalModuleId);
 
+macro_rules! impl_intern_key {
+    ($name:ident) => {
+        impl salsa::InternKey for $name {
+            fn from_intern_id(v: salsa::InternId) -> Self {
+                $name(v)
+            }
+            fn as_intern_id(&self) -> salsa::InternId {
+                self.0
+            }
+        }
+    };
+}
+
 #[derive(Debug)]
 pub struct ItemLoc<N: AstNode> {
     pub(crate) module: ModuleId,
@@ -97,10 +107,25 @@ impl<'a, DB> LocationCtx<&'a DB> {
     }
 }
 
+impl<'a, DB: AstDatabase + InternDatabase> LocationCtx<&'a DB> {
+    pub fn to_def<N, DEF>(self, ast: &N) -> DEF
+    where
+        N: AstNode,
+        DEF: AstItemDef<N>,
+    {
+        DEF::from_ast(self, ast)
+    }
+}
+
 pub trait AstItemDef<N: AstNode>: salsa::InternKey + Clone {
     fn intern(db: &impl InternDatabase, loc: ItemLoc<N>) -> Self;
     fn lookup_intern(self, db: &impl InternDatabase) -> ItemLoc<N>;
 
+    fn from_ast(ctx: LocationCtx<&(impl AstDatabase + InternDatabase)>, ast: &N) -> Self {
+        let items = ctx.db.ast_id_map(ctx.file_id);
+        let item_id = items.ast_id(ast);
+        Self::from_ast_id(ctx, item_id)
+    }
     fn from_ast_id(ctx: LocationCtx<&impl InternDatabase>, ast_id: FileAstId<N>) -> Self {
         let loc = ItemLoc { module: ctx.module, ast_id: AstId::new(ctx.file_id, ast_id) };
         Self::intern(ctx.db, loc)
@@ -233,24 +258,12 @@ impl Lookup for ConstId {
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct StaticId(salsa::InternId);
 impl_intern_key!(StaticId);
-
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub struct StaticLoc {
-    pub container: ModuleId,
-    pub ast_id: AstId<ast::StaticDef>,
-}
-
-impl Intern for StaticLoc {
-    type ID = StaticId;
-    fn intern(self, db: &impl db::DefDatabase) -> StaticId {
-        db.intern_static(self)
+impl AstItemDef<ast::StaticDef> for StaticId {
+    fn intern(db: &impl InternDatabase, loc: ItemLoc<ast::StaticDef>) -> Self {
+        db.intern_static(loc)
     }
-}
-
-impl Lookup for StaticId {
-    type Data = StaticLoc;
-    fn lookup(&self, db: &impl db::DefDatabase) -> StaticLoc {
-        db.lookup_intern_static(*self)
+    fn lookup_intern(self, db: &impl InternDatabase) -> ItemLoc<ast::StaticDef> {
+        db.lookup_intern_static(self)
     }
 }
 
@@ -481,12 +494,6 @@ impl HasModule for ConstLoc {
     }
 }
 
-impl HasModule for StaticLoc {
-    fn module(&self, _db: &impl db::DefDatabase) -> ModuleId {
-        self.container
-    }
-}
-
 pub trait HasSource {
     type Value;
     fn source(&self, db: &impl db::DefDatabase) -> Source<Self::Value>;
@@ -519,15 +526,6 @@ impl HasSource for ConstLoc {
     }
 }
 
-impl HasSource for StaticLoc {
-    type Value = ast::StaticDef;
-
-    fn source(&self, db: &impl db::DefDatabase) -> Source<ast::StaticDef> {
-        let node = self.ast_id.to_node(db);
-        Source::new(self.ast_id.file_id(), node)
-    }
-}
-
 pub trait HasChildSource {
     type ChildId;
     type Value;
diff --git a/crates/ra_hir_def/src/nameres.rs b/crates/ra_hir_def/src/nameres.rs
index 5919771b09..3b2e996470 100644
--- a/crates/ra_hir_def/src/nameres.rs
+++ b/crates/ra_hir_def/src/nameres.rs
@@ -66,7 +66,7 @@ use ra_arena::Arena;
 use ra_db::{CrateId, Edition, FileId};
 use ra_prof::profile;
 use ra_syntax::ast;
-use rustc_hash::FxHashMap;
+use rustc_hash::{FxHashMap, FxHashSet};
 
 use crate::{
     builtin_type::BuiltinType,
@@ -90,6 +90,18 @@ pub struct CrateDefMap {
     root: LocalModuleId,
     modules: Arena<LocalModuleId, ModuleData>,
 
+    /// Some macros are not well-behavior, which leads to infinite loop
+    /// e.g. macro_rules! foo { ($ty:ty) => { foo!($ty); } }
+    /// We mark it down and skip it in collector
+    ///
+    /// FIXME:
+    /// Right now it only handle a poison macro in a single crate,
+    /// such that if other crate try to call that macro,
+    /// the whole process will do again until it became poisoned in that crate.
+    /// We should handle this macro set globally
+    /// However, do we want to put it as a global variable?
+    poison_macros: FxHashSet<MacroDefId>,
+
     diagnostics: Vec<DefDiagnostic>,
 }
 
@@ -169,7 +181,7 @@ impl ModuleScope {
     pub fn macros<'a>(&'a self) -> impl Iterator<Item = (&'a Name, MacroDefId)> + 'a {
         self.items
             .iter()
-            .filter_map(|(name, res)| res.def.take_macros().map(|macro_| (name, macro_)))
+            .filter_map(|(name, res)| res.def.get_macros().map(|macro_| (name, macro_)))
     }
 
     /// Iterate over all legacy textual scoped macros visable at the end of the module
@@ -222,6 +234,7 @@ impl CrateDefMap {
                 prelude: None,
                 root,
                 modules,
+                poison_macros: FxHashSet::default(),
                 diagnostics: Vec::new(),
             }
         };
@@ -254,6 +267,16 @@ impl CrateDefMap {
         self.diagnostics.iter().for_each(|it| it.add_to(db, module, sink))
     }
 
+    pub fn resolve_path(
+        &self,
+        db: &impl DefDatabase,
+        original_module: LocalModuleId,
+        path: &Path,
+    ) -> (PerNs, Option<usize>) {
+        let res = self.resolve_path_fp_with_macro(db, ResolveMode::Other, original_module, path);
+        (res.resolved_def, res.segment_index)
+    }
+
     pub fn modules(&self) -> impl Iterator<Item = LocalModuleId> + '_ {
         self.modules.iter().map(|(id, _data)| id)
     }
@@ -264,16 +287,6 @@ impl CrateDefMap {
             .filter(move |(_id, data)| data.definition == Some(file_id))
             .map(|(id, _data)| id)
     }
-
-    pub(crate) fn resolve_path(
-        &self,
-        db: &impl DefDatabase,
-        original_module: LocalModuleId,
-        path: &Path,
-    ) -> (PerNs, Option<usize>) {
-        let res = self.resolve_path_fp_with_macro(db, ResolveMode::Other, original_module, path);
-        (res.resolved_def, res.segment_index)
-    }
 }
 
 impl ModuleData {
diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs
index df01a20e1a..b02364e86f 100644
--- a/crates/ra_hir_def/src/nameres/collector.rs
+++ b/crates/ra_hir_def/src/nameres/collector.rs
@@ -7,8 +7,8 @@ use hir_expand::{
 };
 use ra_cfg::CfgOptions;
 use ra_db::{CrateId, FileId};
-use ra_syntax::ast;
-use rustc_hash::{FxHashMap, FxHashSet};
+use ra_syntax::{ast, SmolStr};
+use rustc_hash::FxHashMap;
 use test_utils::tested_by;
 
 use crate::{
@@ -21,7 +21,7 @@ use crate::{
     path::{Path, PathKind},
     per_ns::PerNs,
     AdtId, AstId, AstItemDef, ConstLoc, ContainerId, EnumId, EnumVariantId, FunctionLoc, ImplId,
-    Intern, LocalImportId, LocalModuleId, LocationCtx, ModuleDefId, ModuleId, StaticLoc, StructId,
+    Intern, LocalImportId, LocalModuleId, LocationCtx, ModuleDefId, ModuleId, StaticId, StructId,
     StructOrUnionId, TraitId, TypeAliasLoc, UnionId,
 };
 
@@ -57,7 +57,6 @@ pub(super) fn collect_defs(db: &impl DefDatabase, mut def_map: CrateDefMap) -> C
         unexpanded_macros: Vec::new(),
         mod_dirs: FxHashMap::default(),
         macro_stack_monitor: MacroStackMonitor::default(),
-        poison_macros: FxHashSet::default(),
         cfg_options,
     };
     collector.collect();
@@ -104,17 +103,6 @@ struct DefCollector<'a, DB> {
     /// Some macro use `$tt:tt which mean we have to handle the macro perfectly
     /// To prevent stack overflow, we add a deep counter here for prevent that.
     macro_stack_monitor: MacroStackMonitor,
-    /// Some macros are not well-behavior, which leads to infinite loop
-    /// e.g. macro_rules! foo { ($ty:ty) => { foo!($ty); } }
-    /// We mark it down and skip it in collector
-    ///
-    /// FIXME:
-    /// Right now it only handle a poison macro in a single crate,
-    /// such that if other crate try to call that macro,
-    /// the whole process will do again until it became poisoned in that crate.
-    /// We should handle this macro set globally
-    /// However, do we want to put it as a global variable?
-    poison_macros: FxHashSet<MacroDefId>,
 
     cfg_options: &'a CfgOptions,
 }
@@ -476,7 +464,7 @@ where
                 path,
             );
 
-            if let Some(def) = resolved_res.resolved_def.take_macros() {
+            if let Some(def) = resolved_res.resolved_def.get_macros() {
                 let call_id = self.db.intern_macro(MacroCallLoc { def, ast_id: *ast_id });
                 resolved.push((*module_id, call_id, def));
                 res = ReachedFixedPoint::No;
@@ -501,7 +489,7 @@ where
         macro_call_id: MacroCallId,
         macro_def_id: MacroDefId,
     ) {
-        if self.poison_macros.contains(&macro_def_id) {
+        if self.def_map.poison_macros.contains(&macro_def_id) {
             return;
         }
 
@@ -521,7 +509,7 @@ where
             .collect(raw_items.items());
         } else {
             log::error!("Too deep macro expansion: {:?}", macro_call_id);
-            self.poison_macros.insert(macro_def_id);
+            self.def_map.poison_macros.insert(macro_def_id);
         }
 
         self.macro_stack_monitor.decrease(macro_def_id);
@@ -599,8 +587,8 @@ where
     }
 
     fn collect_module(&mut self, module: &raw::ModuleData, attrs: &Attrs) {
-        let path_attr = attrs.by_key("path").string_value();
-        let is_macro_use = attrs.by_key("macro_use").exists();
+        let path_attr = self.path_attr(attrs);
+        let is_macro_use = attrs.has_atom("macro_use");
         match module {
             // inline module, just recurse
             raw::ModuleData::Definition { name, items, ast_id } => {
@@ -715,10 +703,7 @@ where
                 PerNs::values(def.into())
             }
             raw::DefKind::Static(ast_id) => {
-                let def = StaticLoc { container: module, ast_id: AstId::new(self.file_id, ast_id) }
-                    .intern(self.def_collector.db);
-
-                PerNs::values(def.into())
+                PerNs::values(StaticId::from_ast_id(ctx, ast_id).into())
             }
             raw::DefKind::Trait(ast_id) => PerNs::types(TraitId::from_ast_id(ctx, ast_id).into()),
             raw::DefKind::TypeAlias(ast_id) => {
@@ -796,11 +781,11 @@ where
     }
 
     fn is_cfg_enabled(&self, attrs: &Attrs) -> bool {
-        // FIXME: handle cfg_attr :-)
-        attrs
-            .by_key("cfg")
-            .tt_values()
-            .all(|tt| self.def_collector.cfg_options.is_cfg_enabled(tt) != Some(false))
+        attrs.iter().all(|attr| attr.is_cfg_enabled(&self.def_collector.cfg_options) != Some(false))
+    }
+
+    fn path_attr<'a>(&self, attrs: &'a Attrs) -> Option<&'a SmolStr> {
+        attrs.iter().find_map(|attr| attr.as_path())
     }
 }
 
@@ -822,7 +807,7 @@ mod tests {
         db: &impl DefDatabase,
         def_map: CrateDefMap,
         monitor: MacroStackMonitor,
-    ) -> (CrateDefMap, FxHashSet<MacroDefId>) {
+    ) -> CrateDefMap {
         let mut collector = DefCollector {
             db,
             def_map,
@@ -831,18 +816,13 @@ mod tests {
             unexpanded_macros: Vec::new(),
             mod_dirs: FxHashMap::default(),
             macro_stack_monitor: monitor,
-            poison_macros: FxHashSet::default(),
             cfg_options: &CfgOptions::default(),
         };
         collector.collect();
-        (collector.def_map, collector.poison_macros)
+        collector.finish()
     }
 
-    fn do_limited_resolve(
-        code: &str,
-        limit: u32,
-        poison_limit: u32,
-    ) -> (CrateDefMap, FxHashSet<MacroDefId>) {
+    fn do_limited_resolve(code: &str, limit: u32, poison_limit: u32) -> CrateDefMap {
         let (db, _file_id) = TestDB::with_single_file(&code);
         let krate = db.test_crate();
 
@@ -857,6 +837,7 @@ mod tests {
                 prelude: None,
                 root,
                 modules,
+                poison_macros: FxHashSet::default(),
                 diagnostics: Vec::new(),
             }
         };
@@ -886,7 +867,7 @@ foo!(KABOOM);
 
     #[test]
     fn test_macro_expand_poisoned() {
-        let (_, poison_macros) = do_limited_resolve(
+        let def = do_limited_resolve(
             r#"
         macro_rules! foo {
             ($ty:ty) => { foo!($ty); }
@@ -897,12 +878,12 @@ foo!(KABOOM);
             16,
         );
 
-        assert_eq!(poison_macros.len(), 1);
+        assert_eq!(def.poison_macros.len(), 1);
     }
 
     #[test]
     fn test_macro_expand_normal() {
-        let (_, poison_macros) = do_limited_resolve(
+        let def = do_limited_resolve(
             r#"
         macro_rules! foo {
             ($ident:ident) => { struct $ident {} }
@@ -913,6 +894,6 @@ foo!(Bar);
             16,
         );
 
-        assert_eq!(poison_macros.len(), 0);
+        assert_eq!(def.poison_macros.len(), 0);
     }
 }
diff --git a/crates/ra_hir_def/src/nameres/raw.rs b/crates/ra_hir_def/src/nameres/raw.rs
index 2ec84f2cc9..552cbe5448 100644
--- a/crates/ra_hir_def/src/nameres/raw.rs
+++ b/crates/ra_hir_def/src/nameres/raw.rs
@@ -1,9 +1,4 @@
-//! 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.
+//! FIXME: write short doc here
 
 use std::{ops::Index, sync::Arc};
 
@@ -22,7 +17,10 @@ use ra_syntax::{
 use test_utils::tested_by;
 
 use crate::{
-    attr::Attrs, db::DefDatabase, path::Path, FileAstId, HirFileId, LocalImportId, Source,
+    attr::{Attr, Attrs},
+    db::DefDatabase,
+    path::Path,
+    FileAstId, HirFileId, LocalImportId, Source,
 };
 
 /// `RawItems` is a set of top-level items in a file (except for impls).
@@ -409,6 +407,6 @@ impl RawItemsCollector {
     }
 
     fn parse_attrs(&self, item: &impl ast::AttrsOwner) -> Attrs {
-        Attrs::new(item, &self.hygiene)
+        Attr::from_attrs_owner(item, &self.hygiene)
     }
 }
diff --git a/crates/ra_hir_def/src/path.rs b/crates/ra_hir_def/src/path.rs
index 7b2723d571..626ebffdc8 100644
--- a/crates/ra_hir_def/src/path.rs
+++ b/crates/ra_hir_def/src/path.rs
@@ -1,4 +1,4 @@
-//! A desugared representation of paths like `crate::foo` or `<Type as Trait>::bar`.
+//! FIXME: write short doc here
 
 use std::{iter, sync::Arc};
 
@@ -66,7 +66,7 @@ pub enum PathKind {
 
 impl Path {
     /// Calls `cb` with all paths, represented by this use item.
-    pub(crate) fn expand_use_item(
+    pub fn expand_use_item(
         item_src: Source<ast::UseItem>,
         hygiene: &Hygiene,
         mut cb: impl FnMut(Path, &ast::UseTree, bool, Option<Name>),
@@ -76,10 +76,7 @@ impl Path {
         }
     }
 
-    pub(crate) fn from_simple_segments(
-        kind: PathKind,
-        segments: impl IntoIterator<Item = Name>,
-    ) -> Path {
+    pub fn from_simple_segments(kind: PathKind, segments: impl IntoIterator<Item = Name>) -> Path {
         Path {
             kind,
             segments: segments
@@ -97,7 +94,7 @@ impl Path {
 
     /// Converts an `ast::Path` to `Path`. Works with use trees.
     /// It correctly handles `$crate` based path from macro call.
-    pub(crate) fn from_src(mut path: ast::Path, hygiene: &Hygiene) -> Option<Path> {
+    pub fn from_src(mut path: ast::Path, hygiene: &Hygiene) -> Option<Path> {
         let mut kind = PathKind::Plain;
         let mut segments = Vec::new();
         loop {
@@ -230,7 +227,7 @@ impl Path {
 }
 
 impl GenericArgs {
-    pub(crate) fn from_ast(node: ast::TypeArgList) -> Option<GenericArgs> {
+    pub fn from_ast(node: ast::TypeArgList) -> Option<GenericArgs> {
         let mut args = Vec::new();
         for type_arg in node.type_args() {
             let type_ref = TypeRef::from_ast_opt(type_arg.type_ref());
diff --git a/crates/ra_hir_def/src/per_ns.rs b/crates/ra_hir_def/src/per_ns.rs
index 06ef6c9fc9..717ed1ef90 100644
--- a/crates/ra_hir_def/src/per_ns.rs
+++ b/crates/ra_hir_def/src/per_ns.rs
@@ -44,6 +44,10 @@ impl PerNs {
         self.types.is_none() && self.values.is_none() && self.macros.is_none()
     }
 
+    pub fn is_all(&self) -> bool {
+        self.types.is_some() && self.values.is_some() && self.macros.is_some()
+    }
+
     pub fn take_types(self) -> Option<ModuleDefId> {
         self.types
     }
@@ -52,10 +56,14 @@ impl PerNs {
         self.values
     }
 
-    pub fn take_macros(self) -> Option<MacroDefId> {
+    pub fn get_macros(&self) -> Option<MacroDefId> {
         self.macros
     }
 
+    pub fn only_macros(&self) -> PerNs {
+        PerNs { types: None, values: None, macros: self.macros }
+    }
+
     pub fn or(self, other: PerNs) -> PerNs {
         PerNs {
             types: self.types.or(other.types),
diff --git a/crates/ra_hir_def/src/resolver.rs b/crates/ra_hir_def/src/resolver.rs
index 7182b8a4d6..b56de44dd7 100644
--- a/crates/ra_hir_def/src/resolver.rs
+++ b/crates/ra_hir_def/src/resolver.rs
@@ -308,7 +308,7 @@ impl Resolver {
 
     pub fn resolve_path_as_macro(&self, db: &impl DefDatabase, path: &Path) -> Option<MacroDefId> {
         let (item_map, module) = self.module()?;
-        item_map.resolve_path(db, module, path).0.take_macros()
+        item_map.resolve_path(db, module, path).0.get_macros()
     }
 
     pub fn process_all_names(&self, db: &impl DefDatabase, f: &mut dyn FnMut(Name, ScopeDef)) {
@@ -540,7 +540,7 @@ impl HasResolver for ConstId {
 
 impl HasResolver for StaticId {
     fn resolver(self, db: &impl DefDatabase) -> Resolver {
-        self.lookup(db).container.resolver(db)
+        self.module(db).resolver(db)
     }
 }
 
diff --git a/crates/ra_hir_def/src/type_ref.rs b/crates/ra_hir_def/src/type_ref.rs
index 5f10e9a88b..8af061116d 100644
--- a/crates/ra_hir_def/src/type_ref.rs
+++ b/crates/ra_hir_def/src/type_ref.rs
@@ -64,7 +64,7 @@ pub enum TypeBound {
 
 impl TypeRef {
     /// Converts an `ast::TypeRef` to a `hir::TypeRef`.
-    pub(crate) fn from_ast(node: ast::TypeRef) -> Self {
+    pub fn from_ast(node: ast::TypeRef) -> Self {
         match node {
             ast::TypeRef::ParenType(inner) => TypeRef::from_ast_opt(inner.type_ref()),
             ast::TypeRef::TupleType(inner) => {
@@ -113,7 +113,7 @@ impl TypeRef {
         }
     }
 
-    pub(crate) fn from_ast_opt(node: Option<ast::TypeRef>) -> Self {
+    pub fn from_ast_opt(node: Option<ast::TypeRef>) -> Self {
         if let Some(node) = node {
             TypeRef::from_ast(node)
         } else {
@@ -121,7 +121,7 @@ impl TypeRef {
         }
     }
 
-    pub(crate) fn unit() -> TypeRef {
+    pub fn unit() -> TypeRef {
         TypeRef::Tuple(Vec::new())
     }
 }
@@ -135,7 +135,7 @@ pub(crate) fn type_bounds_from_ast(type_bounds_opt: Option<ast::TypeBoundList>)
 }
 
 impl TypeBound {
-    pub(crate) fn from_ast(node: ast::TypeBound) -> Self {
+    pub fn from_ast(node: ast::TypeBound) -> Self {
         match node.kind() {
             ast::TypeBoundKind::PathType(path_type) => {
                 let path = match path_type.path() {
diff --git a/crates/ra_ide_api/src/completion/presentation.rs b/crates/ra_ide_api/src/completion/presentation.rs
index bac3f7582f..896ad15173 100644
--- a/crates/ra_ide_api/src/completion/presentation.rs
+++ b/crates/ra_ide_api/src/completion/presentation.rs
@@ -288,7 +288,7 @@ impl Completions {
 }
 
 fn is_deprecated(node: impl HasAttrs, db: &impl HirDatabase) -> bool {
-    node.attrs(db).by_key("deprecated").exists()
+    node.attrs(db).has_atom("deprecated")
 }
 
 fn has_non_default_type_params(def: hir::GenericDef, db: &db::RootDatabase) -> bool {
diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs
index c128e608d8..351997dcdb 100644
--- a/crates/ra_project_model/src/cargo_workspace.rs
+++ b/crates/ra_project_model/src/cargo_workspace.rs
@@ -54,11 +54,13 @@ struct TargetData {
     name: String,
     root: PathBuf,
     kind: TargetKind,
+    is_proc_macro: bool,
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
 pub enum TargetKind {
     Bin,
+    /// Any kind of Cargo lib crate-type (dylib, rlib, proc-macro, ...).
     Lib,
     Example,
     Test,
@@ -74,6 +76,7 @@ impl TargetKind {
                 "test" => TargetKind::Test,
                 "bench" => TargetKind::Bench,
                 "example" => TargetKind::Example,
+                "proc-macro" => TargetKind::Lib,
                 _ if kind.contains("lib") => TargetKind::Lib,
                 _ => continue,
             };
@@ -123,6 +126,9 @@ impl Target {
     pub fn kind(self, ws: &CargoWorkspace) -> TargetKind {
         ws.targets[self].kind
     }
+    pub fn is_proc_macro(self, ws: &CargoWorkspace) -> bool {
+        ws.targets[self].is_proc_macro
+    }
 }
 
 impl CargoWorkspace {
@@ -155,11 +161,13 @@ impl CargoWorkspace {
             let pkg_data = &mut packages[pkg];
             pkg_by_id.insert(id, pkg);
             for meta_tgt in meta_pkg.targets {
+                let is_proc_macro = meta_tgt.kind.as_slice() == &["proc-macro"];
                 let tgt = targets.alloc(TargetData {
                     pkg,
                     name: meta_tgt.name,
                     root: meta_tgt.src_path.clone(),
                     kind: TargetKind::new(meta_tgt.kind.as_slice()),
+                    is_proc_macro,
                 });
                 pkg_data.targets.push(tgt);
             }
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs
index 638ca2f8b9..77f729a25f 100644
--- a/crates/ra_project_model/src/lib.rs
+++ b/crates/ra_project_model/src/lib.rs
@@ -209,8 +209,9 @@ impl ProjectWorkspace {
                 }
 
                 let libcore = sysroot.core().and_then(|it| sysroot_crates.get(&it).copied());
-                let liballoc = sysroot.alloc().and_then(|it| sysroot_crates.get(&it).copied());
                 let libstd = sysroot.std().and_then(|it| sysroot_crates.get(&it).copied());
+                let libproc_macro =
+                    sysroot.proc_macro().and_then(|it| sysroot_crates.get(&it).copied());
 
                 let mut pkg_to_lib_crate = FxHashMap::default();
                 let mut pkg_crates = FxHashMap::default();
@@ -237,6 +238,21 @@ impl ProjectWorkspace {
                                 lib_tgt = Some(crate_id);
                                 pkg_to_lib_crate.insert(pkg, crate_id);
                             }
+                            if tgt.is_proc_macro(&cargo) {
+                                if let Some(proc_macro) = libproc_macro {
+                                    if let Err(_) = crate_graph.add_dep(
+                                        crate_id,
+                                        "proc_macro".into(),
+                                        proc_macro,
+                                    ) {
+                                        log::error!(
+                                            "cyclic dependency on proc_macro for {}",
+                                            pkg.name(&cargo)
+                                        )
+                                    }
+                                }
+                            }
+
                             pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
                         }
                     }
@@ -262,11 +278,6 @@ impl ProjectWorkspace {
                                 log::error!("cyclic dependency on core for {}", pkg.name(&cargo))
                             }
                         }
-                        if let Some(alloc) = liballoc {
-                            if let Err(_) = crate_graph.add_dep(from, "alloc".into(), alloc) {
-                                log::error!("cyclic dependency on alloc for {}", pkg.name(&cargo))
-                            }
-                        }
                         if let Some(std) = libstd {
                             if let Err(_) = crate_graph.add_dep(from, "std".into(), std) {
                                 log::error!("cyclic dependency on std for {}", pkg.name(&cargo))
diff --git a/crates/ra_project_model/src/sysroot.rs b/crates/ra_project_model/src/sysroot.rs
index bec9439d73..8a7757c411 100644
--- a/crates/ra_project_model/src/sysroot.rs
+++ b/crates/ra_project_model/src/sysroot.rs
@@ -31,14 +31,14 @@ impl Sysroot {
         self.by_name("core")
     }
 
-    pub fn alloc(&self) -> Option<SysrootCrate> {
-        self.by_name("alloc")
-    }
-
     pub fn std(&self) -> Option<SysrootCrate> {
         self.by_name("std")
     }
 
+    pub fn proc_macro(&self) -> Option<SysrootCrate> {
+        self.by_name("proc_macro")
+    }
+
     pub fn crates<'a>(&'a self) -> impl Iterator<Item = SysrootCrate> + ExactSizeIterator + 'a {
         self.crates.iter().map(|(id, _data)| id)
     }
@@ -74,7 +74,7 @@ impl Sysroot {
             }
         }
         if let Some(alloc) = sysroot.by_name("alloc") {
-            if let Some(core) = sysroot.by_name("core") {
+            if let Some(core) = sysroot.core() {
                 sysroot.crates[alloc].deps.push(core);
             }
         }


Test patch:
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs
index 3209c66bd0..17a50cf741 100644
--- a/crates/ra_hir/src/ty/tests.rs
+++ b/crates/ra_hir/src/ty/tests.rs
@@ -2550,6 +2550,8 @@ fn test() {
     [233; 246) 'GLOBAL_STATIC': u32
     [256; 257) 'w': u32
     [260; 277) 'GLOBAL...IC_MUT': u32
+    [118; 120) '99': u32
+    [161; 163) '99': u32
     "###
     );
 }