aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_def
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir_def')
-rw-r--r--crates/hir_def/src/attr.rs208
-rw-r--r--crates/hir_def/src/item_tree.rs5
-rw-r--r--crates/hir_def/src/item_tree/lower.rs7
-rw-r--r--crates/hir_def/src/nameres/collector.rs59
-rw-r--r--crates/hir_def/src/nameres/tests.rs4
-rw-r--r--crates/hir_def/src/nameres/tests/diagnostics.rs18
-rw-r--r--crates/hir_def/src/nameres/tests/macros.rs8
-rw-r--r--crates/hir_def/src/path.rs12
8 files changed, 232 insertions, 89 deletions
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs
index 9cd0b72aa..042e119b1 100644
--- a/crates/hir_def/src/attr.rs
+++ b/crates/hir_def/src/attr.rs
@@ -5,20 +5,21 @@ use std::{ops, sync::Arc};
5use base_db::CrateId; 5use base_db::CrateId;
6use cfg::{CfgExpr, CfgOptions}; 6use cfg::{CfgExpr, CfgOptions};
7use either::Either; 7use either::Either;
8use hir_expand::{hygiene::Hygiene, AstId, InFile}; 8use hir_expand::{hygiene::Hygiene, name::AsName, AstId, InFile};
9use itertools::Itertools; 9use itertools::Itertools;
10use mbe::ast_to_token_tree; 10use mbe::ast_to_token_tree;
11use syntax::{ 11use syntax::{
12 ast::{self, AstNode, AttrsOwner}, 12 ast::{self, AstNode, AttrsOwner},
13 match_ast, AstToken, SmolStr, SyntaxNode, 13 match_ast, AstToken, SmolStr, SyntaxNode,
14}; 14};
15use test_utils::mark;
15use tt::Subtree; 16use tt::Subtree;
16 17
17use crate::{ 18use crate::{
18 db::DefDatabase, 19 db::DefDatabase,
19 item_tree::{ItemTreeId, ItemTreeNode}, 20 item_tree::{ItemTreeId, ItemTreeNode},
20 nameres::ModuleSource, 21 nameres::ModuleSource,
21 path::ModPath, 22 path::{ModPath, PathKind},
22 src::HasChildSource, 23 src::HasChildSource,
23 AdtId, AttrDefId, Lookup, 24 AdtId, AttrDefId, Lookup,
24}; 25};
@@ -41,7 +42,7 @@ impl From<Documentation> for String {
41 42
42/// Syntactical attributes, without filtering of `cfg_attr`s. 43/// Syntactical attributes, without filtering of `cfg_attr`s.
43#[derive(Default, Debug, Clone, PartialEq, Eq)] 44#[derive(Default, Debug, Clone, PartialEq, Eq)]
44pub struct RawAttrs { 45pub(crate) struct RawAttrs {
45 entries: Option<Arc<[Attr]>>, 46 entries: Option<Arc<[Attr]>>,
46} 47}
47 48
@@ -71,35 +72,34 @@ impl ops::Deref for Attrs {
71} 72}
72 73
73impl RawAttrs { 74impl RawAttrs {
74 pub const EMPTY: Self = Self { entries: None }; 75 pub(crate) const EMPTY: Self = Self { entries: None };
75 76
76 pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Self { 77 pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Self {
77 let (inner_attrs, inner_docs) = inner_attributes(owner.syntax()) 78 let attrs: Vec<_> = collect_attrs(owner).collect();
78 .map_or((None, None), |(attrs, docs)| ((Some(attrs), Some(docs))));
79
80 let outer_attrs = owner.attrs().filter(|attr| attr.excl_token().is_none());
81 let attrs = outer_attrs
82 .chain(inner_attrs.into_iter().flatten())
83 .map(|attr| (attr.syntax().text_range().start(), Attr::from_src(attr, hygiene)));
84
85 let outer_docs =
86 ast::CommentIter::from_syntax_node(owner.syntax()).filter(ast::Comment::is_outer);
87 let docs = outer_docs.chain(inner_docs.into_iter().flatten()).map(|docs_text| {
88 (
89 docs_text.syntax().text_range().start(),
90 docs_text.doc_comment().map(|doc| Attr {
91 input: Some(AttrInput::Literal(SmolStr::new(doc))),
92 path: ModPath::from(hir_expand::name!(doc)),
93 }),
94 )
95 });
96 // sort here by syntax node offset because the source can have doc attributes and doc strings be interleaved
97 let attrs: Vec<_> = docs.chain(attrs).sorted_by_key(|&(offset, _)| offset).collect();
98 let entries = if attrs.is_empty() { 79 let entries = if attrs.is_empty() {
99 // Avoid heap allocation 80 // Avoid heap allocation
100 None 81 None
101 } else { 82 } else {
102 Some(attrs.into_iter().flat_map(|(_, attr)| attr).collect()) 83 Some(
84 attrs
85 .into_iter()
86 .enumerate()
87 .flat_map(|(i, attr)| match attr {
88 Either::Left(attr) => Attr::from_src(attr, hygiene).map(|attr| (i, attr)),
89 Either::Right(comment) => comment.doc_comment().map(|doc| {
90 (
91 i,
92 Attr {
93 index: 0,
94 input: Some(AttrInput::Literal(SmolStr::new(doc))),
95 path: ModPath::from(hir_expand::name!(doc)),
96 },
97 )
98 }),
99 })
100 .map(|(i, attr)| Attr { index: i as u32, ..attr })
101 .collect(),
102 )
103 }; 103 };
104 Self { entries } 104 Self { entries }
105 } 105 }
@@ -122,9 +122,69 @@ impl RawAttrs {
122 } 122 }
123 123
124 /// Processes `cfg_attr`s, returning the resulting semantic `Attrs`. 124 /// Processes `cfg_attr`s, returning the resulting semantic `Attrs`.
125 pub(crate) fn filter(self, _db: &dyn DefDatabase, _krate: CrateId) -> Attrs { 125 pub(crate) fn filter(self, db: &dyn DefDatabase, krate: CrateId) -> Attrs {
126 // FIXME actually implement this 126 let has_cfg_attrs = self.iter().any(|attr| {
127 Attrs(self) 127 attr.path.as_ident().map_or(false, |name| *name == hir_expand::name![cfg_attr])
128 });
129 if !has_cfg_attrs {
130 return Attrs(self);
131 }
132
133 let crate_graph = db.crate_graph();
134 let new_attrs = self
135 .iter()
136 .filter_map(|attr| {
137 let attr = attr.clone();
138 let is_cfg_attr =
139 attr.path.as_ident().map_or(false, |name| *name == hir_expand::name![cfg_attr]);
140 if !is_cfg_attr {
141 return Some(attr);
142 }
143
144 let subtree = match &attr.input {
145 Some(AttrInput::TokenTree(it)) => it,
146 _ => return Some(attr),
147 };
148
149 // Input subtree is: `(cfg, attr)`
150 // Split it up into a `cfg` and an `attr` subtree.
151 // FIXME: There should be a common API for this.
152 let mut saw_comma = false;
153 let (mut cfg, attr): (Vec<_>, Vec<_>) =
154 subtree.clone().token_trees.into_iter().partition(|tree| {
155 if saw_comma {
156 return false;
157 }
158
159 match tree {
160 tt::TokenTree::Leaf(tt::Leaf::Punct(p)) if p.char == ',' => {
161 saw_comma = true;
162 }
163 _ => {}
164 }
165
166 true
167 });
168 cfg.pop(); // `,` ends up in here
169
170 let attr = Subtree { delimiter: None, token_trees: attr };
171 let cfg = Subtree { delimiter: subtree.delimiter, token_trees: cfg };
172 let cfg = CfgExpr::parse(&cfg);
173
174 let cfg_options = &crate_graph[krate].cfg_options;
175 if cfg_options.check(&cfg) == Some(false) {
176 None
177 } else {
178 mark::hit!(cfg_attr_active);
179
180 let attr = ast::Attr::parse(&format!("#[{}]", attr)).ok()?;
181 let hygiene = Hygiene::new_unhygienic(); // FIXME
182 Attr::from_src(attr, &hygiene)
183 }
184 })
185 .collect();
186
187 Attrs(RawAttrs { entries: Some(new_attrs) })
128 } 188 }
129} 189}
130 190
@@ -180,24 +240,11 @@ impl Attrs {
180 raw_attrs.filter(db, def.krate(db)) 240 raw_attrs.filter(db, def.krate(db))
181 } 241 }
182 242
183 pub fn merge(&self, other: Attrs) -> Attrs {
184 match (&self.0.entries, &other.0.entries) {
185 (None, None) => Attrs::EMPTY,
186 (Some(entries), None) | (None, Some(entries)) => {
187 Attrs(RawAttrs { entries: Some(entries.clone()) })
188 }
189 (Some(a), Some(b)) => {
190 Attrs(RawAttrs { entries: Some(a.iter().chain(b.iter()).cloned().collect()) })
191 }
192 }
193 }
194
195 pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> { 243 pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> {
196 AttrQuery { attrs: self, key } 244 AttrQuery { attrs: self, key }
197 } 245 }
198 246
199 pub fn cfg(&self) -> Option<CfgExpr> { 247 pub fn cfg(&self) -> Option<CfgExpr> {
200 // FIXME: handle cfg_attr :-)
201 let mut cfgs = self.by_key("cfg").tt_values().map(CfgExpr::parse).collect::<Vec<_>>(); 248 let mut cfgs = self.by_key("cfg").tt_values().map(CfgExpr::parse).collect::<Vec<_>>();
202 match cfgs.len() { 249 match cfgs.len() {
203 0 => None, 250 0 => None,
@@ -268,6 +315,7 @@ fn inner_attributes(
268 315
269#[derive(Debug, Clone, PartialEq, Eq)] 316#[derive(Debug, Clone, PartialEq, Eq)]
270pub struct Attr { 317pub struct Attr {
318 index: u32,
271 pub(crate) path: ModPath, 319 pub(crate) path: ModPath,
272 pub(crate) input: Option<AttrInput>, 320 pub(crate) input: Option<AttrInput>,
273} 321}
@@ -294,7 +342,59 @@ impl Attr {
294 } else { 342 } else {
295 None 343 None
296 }; 344 };
297 Some(Attr { path, input }) 345 Some(Attr { index: 0, path, input })
346 }
347
348 /// Maps this lowered `Attr` back to its original syntax node.
349 ///
350 /// `owner` must be the original owner of the attribute.
351 ///
352 /// Note that the returned syntax node might be a `#[cfg_attr]`, or a doc comment, instead of
353 /// the attribute represented by `Attr`.
354 pub fn to_src(&self, owner: &dyn AttrsOwner) -> Either<ast::Attr, ast::Comment> {
355 collect_attrs(owner).nth(self.index as usize).unwrap_or_else(|| {
356 panic!("cannot find `Attr` at index {} in {}", self.index, owner.syntax())
357 })
358 }
359
360 /// Parses this attribute as a `#[derive]`, returns an iterator that yields all contained paths
361 /// to derive macros.
362 ///
363 /// Returns `None` when the attribute is not a well-formed `#[derive]` attribute.
364 pub(crate) fn parse_derive(&self) -> Option<impl Iterator<Item = ModPath>> {
365 if self.path.as_ident() != Some(&hir_expand::name![derive]) {
366 return None;
367 }
368
369 match &self.input {
370 Some(AttrInput::TokenTree(args)) => {
371 let mut counter = 0;
372 let paths = args
373 .token_trees
374 .iter()
375 .group_by(move |tt| {
376 match tt {
377 tt::TokenTree::Leaf(tt::Leaf::Punct(p)) if p.char == ',' => {
378 counter += 1;
379 }
380 _ => {}
381 }
382 counter
383 })
384 .into_iter()
385 .map(|(_, tts)| {
386 let segments = tts.filter_map(|tt| match tt {
387 tt::TokenTree::Leaf(tt::Leaf::Ident(id)) => Some(id.as_name()),
388 _ => None,
389 });
390 ModPath::from_segments(PathKind::Plain, segments)
391 })
392 .collect::<Vec<_>>();
393
394 Some(paths.into_iter())
395 }
396 _ => None,
397 }
298 } 398 }
299} 399}
300 400
@@ -323,7 +423,7 @@ impl<'a> AttrQuery<'a> {
323 self.attrs().next().is_some() 423 self.attrs().next().is_some()
324 } 424 }
325 425
326 fn attrs(self) -> impl Iterator<Item = &'a Attr> { 426 pub(crate) fn attrs(self) -> impl Iterator<Item = &'a Attr> {
327 let key = self.key; 427 let key = self.key;
328 self.attrs 428 self.attrs
329 .iter() 429 .iter()
@@ -344,3 +444,23 @@ fn attrs_from_item_tree<N: ItemTreeNode>(id: ItemTreeId<N>, db: &dyn DefDatabase
344 let mod_item = N::id_to_mod_item(id.value); 444 let mod_item = N::id_to_mod_item(id.value);
345 tree.raw_attrs(mod_item.into()).clone() 445 tree.raw_attrs(mod_item.into()).clone()
346} 446}
447
448fn collect_attrs(owner: &dyn AttrsOwner) -> impl Iterator<Item = Either<ast::Attr, ast::Comment>> {
449 let (inner_attrs, inner_docs) = inner_attributes(owner.syntax())
450 .map_or((None, None), |(attrs, docs)| ((Some(attrs), Some(docs))));
451
452 let outer_attrs = owner.attrs().filter(|attr| attr.excl_token().is_none());
453 let attrs = outer_attrs
454 .chain(inner_attrs.into_iter().flatten())
455 .map(|attr| (attr.syntax().text_range().start(), Either::Left(attr)));
456
457 let outer_docs =
458 ast::CommentIter::from_syntax_node(owner.syntax()).filter(ast::Comment::is_outer);
459 let docs = outer_docs
460 .chain(inner_docs.into_iter().flatten())
461 .map(|docs_text| (docs_text.syntax().text_range().start(), Either::Right(docs_text)));
462 // sort here by syntax node offset because the source can have doc attributes and doc strings be interleaved
463 let attrs: Vec<_> = docs.chain(attrs).sorted_by_key(|&(offset, _)| offset).collect();
464
465 attrs.into_iter().map(|(_, attr)| attr)
466}
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs
index 5eb7cae7f..100dbf5d6 100644
--- a/crates/hir_def/src/item_tree.rs
+++ b/crates/hir_def/src/item_tree.rs
@@ -12,7 +12,7 @@ use std::{
12}; 12};
13 13
14use arena::{Arena, Idx, RawId}; 14use arena::{Arena, Idx, RawId};
15use ast::{AstNode, AttrsOwner, NameOwner, StructKind}; 15use ast::{AstNode, NameOwner, StructKind};
16use base_db::CrateId; 16use base_db::CrateId;
17use either::Either; 17use either::Either;
18use hir_expand::{ 18use hir_expand::{
@@ -495,7 +495,6 @@ pub struct Import {
495 pub alias: Option<ImportAlias>, 495 pub alias: Option<ImportAlias>,
496 pub visibility: RawVisibilityId, 496 pub visibility: RawVisibilityId,
497 pub is_glob: bool, 497 pub is_glob: bool,
498 pub is_prelude: bool,
499 /// AST ID of the `use` or `extern crate` item this import was derived from. Note that many 498 /// AST ID of the `use` or `extern crate` item this import was derived from. Note that many
500 /// `Import`s can map to the same `use` item. 499 /// `Import`s can map to the same `use` item.
501 pub ast_id: FileAstId<ast::Use>, 500 pub ast_id: FileAstId<ast::Use>,
@@ -511,8 +510,6 @@ pub struct ExternCrate {
511 pub name: Name, 510 pub name: Name,
512 pub alias: Option<ImportAlias>, 511 pub alias: Option<ImportAlias>,
513 pub visibility: RawVisibilityId, 512 pub visibility: RawVisibilityId,
514 /// Whether this is a `#[macro_use] extern crate ...`.
515 pub is_macro_use: bool,
516 pub ast_id: FileAstId<ast::ExternCrate>, 513 pub ast_id: FileAstId<ast::ExternCrate>,
517} 514}
518 515
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs
index c8f090c22..3b206ef85 100644
--- a/crates/hir_def/src/item_tree/lower.rs
+++ b/crates/hir_def/src/item_tree/lower.rs
@@ -485,8 +485,6 @@ impl Ctx {
485 } 485 }
486 486
487 fn lower_use(&mut self, use_item: &ast::Use) -> Vec<FileItemTreeId<Import>> { 487 fn lower_use(&mut self, use_item: &ast::Use) -> Vec<FileItemTreeId<Import>> {
488 // FIXME: cfg_attr
489 let is_prelude = use_item.has_atom_attr("prelude_import");
490 let visibility = self.lower_visibility(use_item); 488 let visibility = self.lower_visibility(use_item);
491 let ast_id = self.source_ast_id_map.ast_id(use_item); 489 let ast_id = self.source_ast_id_map.ast_id(use_item);
492 490
@@ -502,7 +500,6 @@ impl Ctx {
502 alias, 500 alias,
503 visibility, 501 visibility,
504 is_glob, 502 is_glob,
505 is_prelude,
506 ast_id, 503 ast_id,
507 index: imports.len(), 504 index: imports.len(),
508 }))); 505 })));
@@ -522,10 +519,8 @@ impl Ctx {
522 }); 519 });
523 let visibility = self.lower_visibility(extern_crate); 520 let visibility = self.lower_visibility(extern_crate);
524 let ast_id = self.source_ast_id_map.ast_id(extern_crate); 521 let ast_id = self.source_ast_id_map.ast_id(extern_crate);
525 // FIXME: cfg_attr
526 let is_macro_use = extern_crate.has_atom_attr("macro_use");
527 522
528 let res = ExternCrate { name, alias, visibility, is_macro_use, ast_id }; 523 let res = ExternCrate { name, alias, visibility, ast_id };
529 Some(id(self.data().extern_crates.alloc(res))) 524 Some(id(self.data().extern_crates.alloc(res)))
530 } 525 }
531 526
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index b114a6fe4..a636ec77d 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -136,23 +136,35 @@ struct Import {
136} 136}
137 137
138impl Import { 138impl Import {
139 fn from_use(tree: &ItemTree, id: ItemTreeId<item_tree::Import>) -> Self { 139 fn from_use(
140 db: &dyn DefDatabase,
141 krate: CrateId,
142 tree: &ItemTree,
143 id: ItemTreeId<item_tree::Import>,
144 ) -> Self {
140 let it = &tree[id.value]; 145 let it = &tree[id.value];
146 let attrs = &tree.attrs(db, krate, ModItem::from(id.value).into());
141 let visibility = &tree[it.visibility]; 147 let visibility = &tree[it.visibility];
142 Self { 148 Self {
143 path: it.path.clone(), 149 path: it.path.clone(),
144 alias: it.alias.clone(), 150 alias: it.alias.clone(),
145 visibility: visibility.clone(), 151 visibility: visibility.clone(),
146 is_glob: it.is_glob, 152 is_glob: it.is_glob,
147 is_prelude: it.is_prelude, 153 is_prelude: attrs.by_key("prelude_import").exists(),
148 is_extern_crate: false, 154 is_extern_crate: false,
149 is_macro_use: false, 155 is_macro_use: false,
150 source: ImportSource::Import(id), 156 source: ImportSource::Import(id),
151 } 157 }
152 } 158 }
153 159
154 fn from_extern_crate(tree: &ItemTree, id: ItemTreeId<item_tree::ExternCrate>) -> Self { 160 fn from_extern_crate(
161 db: &dyn DefDatabase,
162 krate: CrateId,
163 tree: &ItemTree,
164 id: ItemTreeId<item_tree::ExternCrate>,
165 ) -> Self {
155 let it = &tree[id.value]; 166 let it = &tree[id.value];
167 let attrs = &tree.attrs(db, krate, ModItem::from(id.value).into());
156 let visibility = &tree[it.visibility]; 168 let visibility = &tree[it.visibility];
157 Self { 169 Self {
158 path: ModPath::from_segments(PathKind::Plain, iter::once(it.name.clone())), 170 path: ModPath::from_segments(PathKind::Plain, iter::once(it.name.clone())),
@@ -161,7 +173,7 @@ impl Import {
161 is_glob: false, 173 is_glob: false,
162 is_prelude: false, 174 is_prelude: false,
163 is_extern_crate: true, 175 is_extern_crate: true,
164 is_macro_use: it.is_macro_use, 176 is_macro_use: attrs.by_key("macro_use").exists(),
165 source: ImportSource::ExternCrate(id), 177 source: ImportSource::ExternCrate(id),
166 } 178 }
167 } 179 }
@@ -930,7 +942,12 @@ impl ModCollector<'_, '_> {
930 if attrs.cfg().map_or(true, |cfg| self.is_cfg_enabled(&cfg)) { 942 if attrs.cfg().map_or(true, |cfg| self.is_cfg_enabled(&cfg)) {
931 if let ModItem::ExternCrate(id) = item { 943 if let ModItem::ExternCrate(id) = item {
932 let import = self.item_tree[*id].clone(); 944 let import = self.item_tree[*id].clone();
933 if import.is_macro_use { 945 let attrs = self.item_tree.attrs(
946 self.def_collector.db,
947 krate,
948 ModItem::from(*id).into(),
949 );
950 if attrs.by_key("macro_use").exists() {
934 self.def_collector.import_macros_from_extern_crate(self.module_id, &import); 951 self.def_collector.import_macros_from_extern_crate(self.module_id, &import);
935 } 952 }
936 } 953 }
@@ -956,6 +973,8 @@ impl ModCollector<'_, '_> {
956 self.def_collector.unresolved_imports.push(ImportDirective { 973 self.def_collector.unresolved_imports.push(ImportDirective {
957 module_id: self.module_id, 974 module_id: self.module_id,
958 import: Import::from_use( 975 import: Import::from_use(
976 self.def_collector.db,
977 krate,
959 &self.item_tree, 978 &self.item_tree,
960 InFile::new(self.file_id, import_id), 979 InFile::new(self.file_id, import_id),
961 ), 980 ),
@@ -966,6 +985,8 @@ impl ModCollector<'_, '_> {
966 self.def_collector.unresolved_imports.push(ImportDirective { 985 self.def_collector.unresolved_imports.push(ImportDirective {
967 module_id: self.module_id, 986 module_id: self.module_id,
968 import: Import::from_extern_crate( 987 import: Import::from_extern_crate(
988 self.def_collector.db,
989 krate,
969 &self.item_tree, 990 &self.item_tree,
970 InFile::new(self.file_id, import_id), 991 InFile::new(self.file_id, import_id),
971 ), 992 ),
@@ -1268,20 +1289,20 @@ impl ModCollector<'_, '_> {
1268 } 1289 }
1269 1290
1270 fn collect_derives(&mut self, attrs: &Attrs, ast_id: FileAstId<ast::Item>) { 1291 fn collect_derives(&mut self, attrs: &Attrs, ast_id: FileAstId<ast::Item>) {
1271 for derive_subtree in attrs.by_key("derive").tt_values() { 1292 for derive in attrs.by_key("derive").attrs() {
1272 // for #[derive(Copy, Clone)], `derive_subtree` is the `(Copy, Clone)` subtree 1293 match derive.parse_derive() {
1273 for tt in &derive_subtree.token_trees { 1294 Some(derive_macros) => {
1274 let ident = match &tt { 1295 for path in derive_macros {
1275 tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => ident, 1296 let ast_id = AstIdWithPath::new(self.file_id, ast_id, path);
1276 tt::TokenTree::Leaf(tt::Leaf::Punct(_)) => continue, // , is ok 1297 self.def_collector
1277 _ => continue, // anything else would be an error (which we currently ignore) 1298 .unexpanded_attribute_macros
1278 }; 1299 .push(DeriveDirective { module_id: self.module_id, ast_id });
1279 let path = ModPath::from_tt_ident(ident); 1300 }
1280 1301 }
1281 let ast_id = AstIdWithPath::new(self.file_id, ast_id, path); 1302 None => {
1282 self.def_collector 1303 // FIXME: diagnose
1283 .unexpanded_attribute_macros 1304 log::debug!("malformed derive: {:?}", derive);
1284 .push(DeriveDirective { module_id: self.module_id, ast_id }); 1305 }
1285 } 1306 }
1286 } 1307 }
1287 } 1308 }
diff --git a/crates/hir_def/src/nameres/tests.rs b/crates/hir_def/src/nameres/tests.rs
index a4d1fb8f3..c459fa66d 100644
--- a/crates/hir_def/src/nameres/tests.rs
+++ b/crates/hir_def/src/nameres/tests.rs
@@ -13,8 +13,8 @@ use test_utils::mark;
13 13
14use crate::{db::DefDatabase, nameres::*, test_db::TestDB}; 14use crate::{db::DefDatabase, nameres::*, test_db::TestDB};
15 15
16fn compute_crate_def_map(fixture: &str) -> Arc<CrateDefMap> { 16fn compute_crate_def_map(ra_fixture: &str) -> Arc<CrateDefMap> {
17 let db = TestDB::with_files(fixture); 17 let db = TestDB::with_files(ra_fixture);
18 let krate = db.crate_graph().iter().next().unwrap(); 18 let krate = db.crate_graph().iter().next().unwrap();
19 db.crate_def_map(krate) 19 db.crate_def_map(krate)
20} 20}
diff --git a/crates/hir_def/src/nameres/tests/diagnostics.rs b/crates/hir_def/src/nameres/tests/diagnostics.rs
index 1a7b98831..58d69d3c6 100644
--- a/crates/hir_def/src/nameres/tests/diagnostics.rs
+++ b/crates/hir_def/src/nameres/tests/diagnostics.rs
@@ -1,4 +1,5 @@
1use base_db::fixture::WithFixture; 1use base_db::fixture::WithFixture;
2use test_utils::mark;
2 3
3use crate::test_db::TestDB; 4use crate::test_db::TestDB;
4 5
@@ -119,3 +120,20 @@ fn inactive_item() {
119 "#, 120 "#,
120 ); 121 );
121} 122}
123
124/// Tests that `cfg` attributes behind `cfg_attr` is handled properly.
125#[test]
126fn inactive_via_cfg_attr() {
127 mark::check!(cfg_attr_active);
128 check_diagnostics(
129 r#"
130 //- /lib.rs
131 #[cfg_attr(not(never), cfg(no))] fn f() {}
132 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no is disabled
133
134 #[cfg_attr(not(never), cfg(not(no)))] fn f() {}
135
136 #[cfg_attr(never, cfg(no))] fn g() {}
137 "#,
138 );
139}
diff --git a/crates/hir_def/src/nameres/tests/macros.rs b/crates/hir_def/src/nameres/tests/macros.rs
index 6fe2ee78a..f9bf5bc72 100644
--- a/crates/hir_def/src/nameres/tests/macros.rs
+++ b/crates/hir_def/src/nameres/tests/macros.rs
@@ -632,11 +632,11 @@ pub struct bar;
632#[test] 632#[test]
633fn expand_derive() { 633fn expand_derive() {
634 let map = compute_crate_def_map( 634 let map = compute_crate_def_map(
635 " 635 r#"
636 //- /main.rs crate:main deps:core 636 //- /main.rs crate:main deps:core
637 use core::*; 637 use core::Copy;
638 638
639 #[derive(Copy, Clone)] 639 #[derive(Copy, core::Clone)]
640 struct Foo; 640 struct Foo;
641 641
642 //- /core.rs crate:core 642 //- /core.rs crate:core
@@ -645,7 +645,7 @@ fn expand_derive() {
645 645
646 #[rustc_builtin_macro] 646 #[rustc_builtin_macro]
647 pub macro Clone {} 647 pub macro Clone {}
648 ", 648 "#,
649 ); 649 );
650 assert_eq!(map.modules[map.root].scope.impls().len(), 2); 650 assert_eq!(map.modules[map.root].scope.impls().len(), 2);
651} 651}
diff --git a/crates/hir_def/src/path.rs b/crates/hir_def/src/path.rs
index 00a69a8a6..e2bf85bbc 100644
--- a/crates/hir_def/src/path.rs
+++ b/crates/hir_def/src/path.rs
@@ -9,11 +9,8 @@ use std::{
9 9
10use crate::{body::LowerCtx, type_ref::LifetimeRef}; 10use crate::{body::LowerCtx, type_ref::LifetimeRef};
11use base_db::CrateId; 11use base_db::CrateId;
12use hir_expand::{ 12use hir_expand::{hygiene::Hygiene, name::Name};
13 hygiene::Hygiene, 13use syntax::ast;
14 name::{AsName, Name},
15};
16use syntax::ast::{self};
17 14
18use crate::{ 15use crate::{
19 type_ref::{TypeBound, TypeRef}, 16 type_ref::{TypeBound, TypeRef},
@@ -56,11 +53,6 @@ impl ModPath {
56 ModPath { kind, segments } 53 ModPath { kind, segments }
57 } 54 }
58 55
59 /// Converts an `tt::Ident` into a single-identifier `Path`.
60 pub(crate) fn from_tt_ident(ident: &tt::Ident) -> ModPath {
61 ident.as_name().into()
62 }
63
64 /// Calls `cb` with all paths, represented by this use item. 56 /// Calls `cb` with all paths, represented by this use item.
65 pub(crate) fn expand_use_item( 57 pub(crate) fn expand_use_item(
66 item_src: InFile<ast::Use>, 58 item_src: InFile<ast::Use>,