aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2019-10-05 15:25:59 +0100
committerGitHub <[email protected]>2019-10-05 15:25:59 +0100
commitae6305b90c80eb919cfde985cba66975b6222ed2 (patch)
treede601daf3714c4bda937e7cad05d048b69d16e71 /crates/ra_hir
parentdbf869b4d2dac09df17609edf6e67c1611b71dc5 (diff)
parentc6303d9fee98232ac83a77f943c39d65c9c6b6db (diff)
Merge #1928
1928: Support `#[cfg(..)]` r=matklad a=oxalica This PR implement `#[cfg(..)]` conditional compilation. It read default cfg options from `rustc --print cfg` with also hard-coded `test` and `debug_assertion` enabled. Front-end settings are **not** included in this PR. There is also a known issue that inner control attributes are totally ignored. I think it is **not** a part of `cfg` and create a separated issue for it. #1949 Fixes #1920 Related: #1073 Co-authored-by: uHOOCCOOHu <[email protected]> Co-authored-by: oxalica <[email protected]>
Diffstat (limited to 'crates/ra_hir')
-rw-r--r--crates/ra_hir/Cargo.toml1
-rw-r--r--crates/ra_hir/src/attr.rs80
-rw-r--r--crates/ra_hir/src/impl_block.rs34
-rw-r--r--crates/ra_hir/src/lib.rs1
-rw-r--r--crates/ra_hir/src/mock.rs23
-rw-r--r--crates/ra_hir/src/nameres/collector.rs53
-rw-r--r--crates/ra_hir/src/nameres/raw.rs54
-rw-r--r--crates/ra_hir/src/nameres/tests.rs70
-rw-r--r--crates/ra_hir/src/ty/tests.rs45
9 files changed, 323 insertions, 38 deletions
diff --git a/crates/ra_hir/Cargo.toml b/crates/ra_hir/Cargo.toml
index d9bed4dda..cc117f84d 100644
--- a/crates/ra_hir/Cargo.toml
+++ b/crates/ra_hir/Cargo.toml
@@ -15,6 +15,7 @@ once_cell = "1.0.1"
15 15
16ra_syntax = { path = "../ra_syntax" } 16ra_syntax = { path = "../ra_syntax" }
17ra_arena = { path = "../ra_arena" } 17ra_arena = { path = "../ra_arena" }
18ra_cfg = { path = "../ra_cfg" }
18ra_db = { path = "../ra_db" } 19ra_db = { path = "../ra_db" }
19mbe = { path = "../ra_mbe", package = "ra_mbe" } 20mbe = { path = "../ra_mbe", package = "ra_mbe" }
20tt = { path = "../ra_tt", package = "ra_tt" } 21tt = { path = "../ra_tt", package = "ra_tt" }
diff --git a/crates/ra_hir/src/attr.rs b/crates/ra_hir/src/attr.rs
new file mode 100644
index 000000000..f67e80bfd
--- /dev/null
+++ b/crates/ra_hir/src/attr.rs
@@ -0,0 +1,80 @@
1//! A higher level attributes based on TokenTree, with also some shortcuts.
2
3use std::sync::Arc;
4
5use mbe::ast_to_token_tree;
6use ra_cfg::CfgOptions;
7use ra_syntax::{
8 ast::{self, AstNode, AttrsOwner},
9 SmolStr,
10};
11use tt::Subtree;
12
13use crate::{db::AstDatabase, path::Path, HirFileId, Source};
14
15#[derive(Debug, Clone, PartialEq, Eq)]
16pub(crate) struct Attr {
17 pub(crate) path: Path,
18 pub(crate) input: Option<AttrInput>,
19}
20
21#[derive(Debug, Clone, PartialEq, Eq)]
22pub enum AttrInput {
23 Literal(SmolStr),
24 TokenTree(Subtree),
25}
26
27impl Attr {
28 pub(crate) fn from_src(
29 Source { file_id, ast }: Source<ast::Attr>,
30 db: &impl AstDatabase,
31 ) -> Option<Attr> {
32 let path = Path::from_src(Source { file_id, ast: ast.path()? }, db)?;
33 let input = match ast.input() {
34 None => None,
35 Some(ast::AttrInput::Literal(lit)) => {
36 // FIXME: escape? raw string?
37 let value = lit.syntax().first_token()?.text().trim_matches('"').into();
38 Some(AttrInput::Literal(value))
39 }
40 Some(ast::AttrInput::TokenTree(tt)) => {
41 Some(AttrInput::TokenTree(ast_to_token_tree(&tt)?.0))
42 }
43 };
44
45 Some(Attr { path, input })
46 }
47
48 pub(crate) fn from_attrs_owner(
49 file_id: HirFileId,
50 owner: &dyn AttrsOwner,
51 db: &impl AstDatabase,
52 ) -> Option<Arc<[Attr]>> {
53 let mut attrs = owner.attrs().peekable();
54 if attrs.peek().is_none() {
55 // Avoid heap allocation
56 return None;
57 }
58 Some(attrs.flat_map(|ast| Attr::from_src(Source { file_id, ast }, db)).collect())
59 }
60
61 pub(crate) fn is_simple_atom(&self, name: &str) -> bool {
62 // FIXME: Avoid cloning
63 self.path.as_ident().map_or(false, |s| s.to_string() == name)
64 }
65
66 pub(crate) fn as_cfg(&self) -> Option<&Subtree> {
67 if self.is_simple_atom("cfg") {
68 match &self.input {
69 Some(AttrInput::TokenTree(subtree)) => Some(subtree),
70 _ => None,
71 }
72 } else {
73 None
74 }
75 }
76
77 pub(crate) fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> Option<bool> {
78 cfg_options.is_cfg_enabled(self.as_cfg()?)
79 }
80}
diff --git a/crates/ra_hir/src/impl_block.rs b/crates/ra_hir/src/impl_block.rs
index 8cf74ddc7..55dfc393b 100644
--- a/crates/ra_hir/src/impl_block.rs
+++ b/crates/ra_hir/src/impl_block.rs
@@ -4,12 +4,14 @@ use rustc_hash::FxHashMap;
4use std::sync::Arc; 4use std::sync::Arc;
5 5
6use ra_arena::{impl_arena_id, map::ArenaMap, Arena, RawId}; 6use ra_arena::{impl_arena_id, map::ArenaMap, Arena, RawId};
7use ra_cfg::CfgOptions;
7use ra_syntax::{ 8use ra_syntax::{
8 ast::{self, AstNode}, 9 ast::{self, AstNode},
9 AstPtr, 10 AstPtr,
10}; 11};
11 12
12use crate::{ 13use crate::{
14 attr::Attr,
13 code_model::{Module, ModuleSource}, 15 code_model::{Module, ModuleSource},
14 db::{AstDatabase, DefDatabase, HirDatabase}, 16 db::{AstDatabase, DefDatabase, HirDatabase},
15 generics::HasGenericParams, 17 generics::HasGenericParams,
@@ -176,6 +178,7 @@ pub struct ModuleImplBlocks {
176impl ModuleImplBlocks { 178impl ModuleImplBlocks {
177 fn collect( 179 fn collect(
178 db: &(impl DefDatabase + AstDatabase), 180 db: &(impl DefDatabase + AstDatabase),
181 cfg_options: &CfgOptions,
179 module: Module, 182 module: Module,
180 source_map: &mut ImplSourceMap, 183 source_map: &mut ImplSourceMap,
181 ) -> Self { 184 ) -> Self {
@@ -188,11 +191,11 @@ impl ModuleImplBlocks {
188 let src = m.module.definition_source(db); 191 let src = m.module.definition_source(db);
189 match &src.ast { 192 match &src.ast {
190 ModuleSource::SourceFile(node) => { 193 ModuleSource::SourceFile(node) => {
191 m.collect_from_item_owner(db, source_map, node, src.file_id) 194 m.collect_from_item_owner(db, cfg_options, source_map, node, src.file_id)
192 } 195 }
193 ModuleSource::Module(node) => { 196 ModuleSource::Module(node) => {
194 let item_list = node.item_list().expect("inline module should have item list"); 197 let item_list = node.item_list().expect("inline module should have item list");
195 m.collect_from_item_owner(db, source_map, &item_list, src.file_id) 198 m.collect_from_item_owner(db, cfg_options, source_map, &item_list, src.file_id)
196 } 199 }
197 }; 200 };
198 m 201 m
@@ -201,6 +204,7 @@ impl ModuleImplBlocks {
201 fn collect_from_item_owner( 204 fn collect_from_item_owner(
202 &mut self, 205 &mut self,
203 db: &(impl DefDatabase + AstDatabase), 206 db: &(impl DefDatabase + AstDatabase),
207 cfg_options: &CfgOptions,
204 source_map: &mut ImplSourceMap, 208 source_map: &mut ImplSourceMap,
205 owner: &dyn ast::ModuleItemOwner, 209 owner: &dyn ast::ModuleItemOwner,
206 file_id: HirFileId, 210 file_id: HirFileId,
@@ -208,6 +212,13 @@ impl ModuleImplBlocks {
208 for item in owner.items_with_macros() { 212 for item in owner.items_with_macros() {
209 match item { 213 match item {
210 ast::ItemOrMacro::Item(ast::ModuleItem::ImplBlock(impl_block_ast)) => { 214 ast::ItemOrMacro::Item(ast::ModuleItem::ImplBlock(impl_block_ast)) => {
215 let attrs = Attr::from_attrs_owner(file_id, &impl_block_ast, db);
216 if attrs.map_or(false, |attrs| {
217 attrs.iter().any(|attr| attr.is_cfg_enabled(cfg_options) == Some(false))
218 }) {
219 continue;
220 }
221
211 let impl_block = ImplData::from_ast(db, file_id, self.module, &impl_block_ast); 222 let impl_block = ImplData::from_ast(db, file_id, self.module, &impl_block_ast);
212 let id = self.impls.alloc(impl_block); 223 let id = self.impls.alloc(impl_block);
213 for &impl_item in &self.impls[id].items { 224 for &impl_item in &self.impls[id].items {
@@ -218,6 +229,13 @@ impl ModuleImplBlocks {
218 } 229 }
219 ast::ItemOrMacro::Item(_) => (), 230 ast::ItemOrMacro::Item(_) => (),
220 ast::ItemOrMacro::Macro(macro_call) => { 231 ast::ItemOrMacro::Macro(macro_call) => {
232 let attrs = Attr::from_attrs_owner(file_id, &macro_call, db);
233 if attrs.map_or(false, |attrs| {
234 attrs.iter().any(|attr| attr.is_cfg_enabled(cfg_options) == Some(false))
235 }) {
236 continue;
237 }
238
221 //FIXME: we should really cut down on the boilerplate required to process a macro 239 //FIXME: we should really cut down on the boilerplate required to process a macro
222 let ast_id = db.ast_id_map(file_id).ast_id(&macro_call).with_file_id(file_id); 240 let ast_id = db.ast_id_map(file_id).ast_id(&macro_call).with_file_id(file_id);
223 if let Some(path) = macro_call 241 if let Some(path) = macro_call
@@ -231,7 +249,13 @@ impl ModuleImplBlocks {
231 if let Some(item_list) = 249 if let Some(item_list) =
232 db.parse_or_expand(file_id).and_then(ast::MacroItems::cast) 250 db.parse_or_expand(file_id).and_then(ast::MacroItems::cast)
233 { 251 {
234 self.collect_from_item_owner(db, source_map, &item_list, file_id) 252 self.collect_from_item_owner(
253 db,
254 cfg_options,
255 source_map,
256 &item_list,
257 file_id,
258 )
235 } 259 }
236 } 260 }
237 } 261 }
@@ -246,8 +270,10 @@ pub(crate) fn impls_in_module_with_source_map_query(
246 module: Module, 270 module: Module,
247) -> (Arc<ModuleImplBlocks>, Arc<ImplSourceMap>) { 271) -> (Arc<ModuleImplBlocks>, Arc<ImplSourceMap>) {
248 let mut source_map = ImplSourceMap::default(); 272 let mut source_map = ImplSourceMap::default();
273 let crate_graph = db.crate_graph();
274 let cfg_options = crate_graph.cfg_options(module.krate.crate_id());
249 275
250 let result = ModuleImplBlocks::collect(db, module, &mut source_map); 276 let result = ModuleImplBlocks::collect(db, cfg_options, module, &mut source_map);
251 (Arc::new(result), Arc::new(source_map)) 277 (Arc::new(result), Arc::new(source_map))
252} 278}
253 279
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs
index 00031deba..4340e9d34 100644
--- a/crates/ra_hir/src/lib.rs
+++ b/crates/ra_hir/src/lib.rs
@@ -44,6 +44,7 @@ mod traits;
44mod type_alias; 44mod type_alias;
45mod type_ref; 45mod type_ref;
46mod ty; 46mod ty;
47mod attr;
47mod impl_block; 48mod impl_block;
48mod expr; 49mod expr;
49mod lang_item; 50mod lang_item;
diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs
index 50feb98fb..f750986b8 100644
--- a/crates/ra_hir/src/mock.rs
+++ b/crates/ra_hir/src/mock.rs
@@ -3,6 +3,7 @@
3use std::{panic, sync::Arc}; 3use std::{panic, sync::Arc};
4 4
5use parking_lot::Mutex; 5use parking_lot::Mutex;
6use ra_cfg::CfgOptions;
6use ra_db::{ 7use ra_db::{
7 salsa, CrateGraph, CrateId, Edition, FileId, FilePosition, SourceDatabase, SourceRoot, 8 salsa, CrateGraph, CrateId, Edition, FileId, FilePosition, SourceDatabase, SourceRoot,
8 SourceRootId, 9 SourceRootId,
@@ -74,13 +75,13 @@ impl MockDatabase {
74 pub fn set_crate_graph_from_fixture(&mut self, graph: CrateGraphFixture) { 75 pub fn set_crate_graph_from_fixture(&mut self, graph: CrateGraphFixture) {
75 let mut ids = FxHashMap::default(); 76 let mut ids = FxHashMap::default();
76 let mut crate_graph = CrateGraph::default(); 77 let mut crate_graph = CrateGraph::default();
77 for (crate_name, (crate_root, edition, _)) in graph.0.iter() { 78 for (crate_name, (crate_root, edition, cfg_options, _)) in graph.0.iter() {
78 let crate_root = self.file_id_of(&crate_root); 79 let crate_root = self.file_id_of(&crate_root);
79 let crate_id = crate_graph.add_crate_root(crate_root, *edition); 80 let crate_id = crate_graph.add_crate_root(crate_root, *edition, cfg_options.clone());
80 Arc::make_mut(&mut self.crate_names).insert(crate_id, crate_name.clone()); 81 Arc::make_mut(&mut self.crate_names).insert(crate_id, crate_name.clone());
81 ids.insert(crate_name, crate_id); 82 ids.insert(crate_name, crate_id);
82 } 83 }
83 for (crate_name, (_, _, deps)) in graph.0.iter() { 84 for (crate_name, (_, _, _, deps)) in graph.0.iter() {
84 let from = ids[crate_name]; 85 let from = ids[crate_name];
85 for dep in deps { 86 for dep in deps {
86 let to = ids[dep]; 87 let to = ids[dep];
@@ -184,7 +185,7 @@ impl MockDatabase {
184 185
185 if is_crate_root { 186 if is_crate_root {
186 let mut crate_graph = CrateGraph::default(); 187 let mut crate_graph = CrateGraph::default();
187 crate_graph.add_crate_root(file_id, Edition::Edition2018); 188 crate_graph.add_crate_root(file_id, Edition::Edition2018, CfgOptions::default());
188 self.set_crate_graph(Arc::new(crate_graph)); 189 self.set_crate_graph(Arc::new(crate_graph));
189 } 190 }
190 file_id 191 file_id
@@ -268,19 +269,27 @@ impl MockDatabase {
268} 269}
269 270
270#[derive(Default)] 271#[derive(Default)]
271pub struct CrateGraphFixture(pub Vec<(String, (String, Edition, Vec<String>))>); 272pub struct CrateGraphFixture(pub Vec<(String, (String, Edition, CfgOptions, Vec<String>))>);
272 273
273#[macro_export] 274#[macro_export]
274macro_rules! crate_graph { 275macro_rules! crate_graph {
275 ($($crate_name:literal: ($crate_path:literal, $($edition:literal,)? [$($dep:literal),*]),)*) => {{ 276 ($(
277 $crate_name:literal: (
278 $crate_path:literal,
279 $($edition:literal,)?
280 [$($dep:literal),*]
281 $(,$cfg:expr)?
282 ),
283 )*) => {{
276 let mut res = $crate::mock::CrateGraphFixture::default(); 284 let mut res = $crate::mock::CrateGraphFixture::default();
277 $( 285 $(
278 #[allow(unused_mut, unused_assignments)] 286 #[allow(unused_mut, unused_assignments)]
279 let mut edition = ra_db::Edition::Edition2018; 287 let mut edition = ra_db::Edition::Edition2018;
280 $(edition = ra_db::Edition::from_string($edition);)? 288 $(edition = ra_db::Edition::from_string($edition);)?
289 let cfg_options = { ::ra_cfg::CfgOptions::default() $(; $cfg)? };
281 res.0.push(( 290 res.0.push((
282 $crate_name.to_string(), 291 $crate_name.to_string(),
283 ($crate_path.to_string(), edition, vec![$($dep.to_string()),*]) 292 ($crate_path.to_string(), edition, cfg_options, vec![$($dep.to_string()),*])
284 )); 293 ));
285 )* 294 )*
286 res 295 res
diff --git a/crates/ra_hir/src/nameres/collector.rs b/crates/ra_hir/src/nameres/collector.rs
index a568fdabd..cef2dc9d2 100644
--- a/crates/ra_hir/src/nameres/collector.rs
+++ b/crates/ra_hir/src/nameres/collector.rs
@@ -1,5 +1,6 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use ra_cfg::CfgOptions;
3use ra_db::FileId; 4use ra_db::FileId;
4use ra_syntax::{ast, SmolStr}; 5use ra_syntax::{ast, SmolStr};
5use rustc_hash::FxHashMap; 6use rustc_hash::FxHashMap;
@@ -35,6 +36,9 @@ pub(super) fn collect_defs(db: &impl DefDatabase, mut def_map: CrateDefMap) -> C
35 } 36 }
36 } 37 }
37 38
39 let crate_graph = db.crate_graph();
40 let cfg_options = crate_graph.cfg_options(def_map.krate().crate_id());
41
38 let mut collector = DefCollector { 42 let mut collector = DefCollector {
39 db, 43 db,
40 def_map, 44 def_map,
@@ -42,6 +46,7 @@ pub(super) fn collect_defs(db: &impl DefDatabase, mut def_map: CrateDefMap) -> C
42 unresolved_imports: Vec::new(), 46 unresolved_imports: Vec::new(),
43 unexpanded_macros: Vec::new(), 47 unexpanded_macros: Vec::new(),
44 macro_stack_monitor: MacroStackMonitor::default(), 48 macro_stack_monitor: MacroStackMonitor::default(),
49 cfg_options,
45 }; 50 };
46 collector.collect(); 51 collector.collect();
47 collector.finish() 52 collector.finish()
@@ -76,8 +81,8 @@ impl MacroStackMonitor {
76} 81}
77 82
78/// Walks the tree of module recursively 83/// Walks the tree of module recursively
79struct DefCollector<DB> { 84struct DefCollector<'a, DB> {
80 db: DB, 85 db: &'a DB,
81 def_map: CrateDefMap, 86 def_map: CrateDefMap,
82 glob_imports: FxHashMap<CrateModuleId, Vec<(CrateModuleId, raw::ImportId)>>, 87 glob_imports: FxHashMap<CrateModuleId, Vec<(CrateModuleId, raw::ImportId)>>,
83 unresolved_imports: Vec<(CrateModuleId, raw::ImportId, raw::ImportData)>, 88 unresolved_imports: Vec<(CrateModuleId, raw::ImportId, raw::ImportData)>,
@@ -86,9 +91,11 @@ struct DefCollector<DB> {
86 /// Some macro use `$tt:tt which mean we have to handle the macro perfectly 91 /// Some macro use `$tt:tt which mean we have to handle the macro perfectly
87 /// To prevent stack overflow, we add a deep counter here for prevent that. 92 /// To prevent stack overflow, we add a deep counter here for prevent that.
88 macro_stack_monitor: MacroStackMonitor, 93 macro_stack_monitor: MacroStackMonitor,
94
95 cfg_options: &'a CfgOptions,
89} 96}
90 97
91impl<'a, DB> DefCollector<&'a DB> 98impl<DB> DefCollector<'_, DB>
92where 99where
93 DB: DefDatabase, 100 DB: DefDatabase,
94{ 101{
@@ -506,7 +513,7 @@ struct ModCollector<'a, D> {
506 parent_module: Option<ParentModule<'a>>, 513 parent_module: Option<ParentModule<'a>>,
507} 514}
508 515
509impl<DB> ModCollector<'_, &'_ mut DefCollector<&'_ DB>> 516impl<DB> ModCollector<'_, &'_ mut DefCollector<'_, DB>>
510where 517where
511 DB: DefDatabase, 518 DB: DefDatabase,
512{ 519{
@@ -523,24 +530,27 @@ where
523 // `#[macro_use] extern crate` is hoisted to imports macros before collecting 530 // `#[macro_use] extern crate` is hoisted to imports macros before collecting
524 // any other items. 531 // any other items.
525 for item in items { 532 for item in items {
526 if let raw::RawItem::Import(import_id) = *item { 533 if self.is_cfg_enabled(&item.attrs) {
527 let import = self.raw_items[import_id].clone(); 534 if let raw::RawItemKind::Import(import_id) = item.kind {
528 if import.is_extern_crate && import.is_macro_use { 535 let import = self.raw_items[import_id].clone();
529 self.def_collector.import_macros_from_extern_crate(self.module_id, &import); 536 if import.is_extern_crate && import.is_macro_use {
537 self.def_collector.import_macros_from_extern_crate(self.module_id, &import);
538 }
530 } 539 }
531 } 540 }
532 } 541 }
533 542
534 for item in items { 543 for item in items {
535 match *item { 544 if self.is_cfg_enabled(&item.attrs) {
536 raw::RawItem::Module(m) => self.collect_module(&self.raw_items[m]), 545 match item.kind {
537 raw::RawItem::Import(import_id) => self.def_collector.unresolved_imports.push(( 546 raw::RawItemKind::Module(m) => self.collect_module(&self.raw_items[m]),
538 self.module_id, 547 raw::RawItemKind::Import(import_id) => self
539 import_id, 548 .def_collector
540 self.raw_items[import_id].clone(), 549 .unresolved_imports
541 )), 550 .push((self.module_id, import_id, self.raw_items[import_id].clone())),
542 raw::RawItem::Def(def) => self.define_def(&self.raw_items[def]), 551 raw::RawItemKind::Def(def) => self.define_def(&self.raw_items[def]),
543 raw::RawItem::Macro(mac) => self.collect_macro(&self.raw_items[mac]), 552 raw::RawItemKind::Macro(mac) => self.collect_macro(&self.raw_items[mac]),
553 }
544 } 554 }
545 } 555 }
546 } 556 }
@@ -703,6 +713,14 @@ where
703 self.def_collector.define_legacy_macro(self.module_id, name.clone(), macro_); 713 self.def_collector.define_legacy_macro(self.module_id, name.clone(), macro_);
704 } 714 }
705 } 715 }
716
717 fn is_cfg_enabled(&self, attrs: &raw::Attrs) -> bool {
718 attrs.as_ref().map_or(true, |attrs| {
719 attrs
720 .iter()
721 .all(|attr| attr.is_cfg_enabled(&self.def_collector.cfg_options) != Some(false))
722 })
723 }
706} 724}
707 725
708fn is_macro_rules(path: &Path) -> bool { 726fn is_macro_rules(path: &Path) -> bool {
@@ -730,6 +748,7 @@ mod tests {
730 unresolved_imports: Vec::new(), 748 unresolved_imports: Vec::new(),
731 unexpanded_macros: Vec::new(), 749 unexpanded_macros: Vec::new(),
732 macro_stack_monitor: monitor, 750 macro_stack_monitor: monitor,
751 cfg_options: &CfgOptions::default(),
733 }; 752 };
734 collector.collect(); 753 collector.collect();
735 collector.finish() 754 collector.finish()
diff --git a/crates/ra_hir/src/nameres/raw.rs b/crates/ra_hir/src/nameres/raw.rs
index 606bd1a95..623b343c4 100644
--- a/crates/ra_hir/src/nameres/raw.rs
+++ b/crates/ra_hir/src/nameres/raw.rs
@@ -10,6 +10,7 @@ use ra_syntax::{
10use test_utils::tested_by; 10use test_utils::tested_by;
11 11
12use crate::{ 12use crate::{
13 attr::Attr,
13 db::{AstDatabase, DefDatabase}, 14 db::{AstDatabase, DefDatabase},
14 AsName, AstIdMap, Either, FileAstId, HirFileId, ModuleSource, Name, Path, Source, 15 AsName, AstIdMap, Either, FileAstId, HirFileId, ModuleSource, Name, Path, Source,
15}; 16};
@@ -119,8 +120,17 @@ impl Index<Macro> for RawItems {
119 } 120 }
120} 121}
121 122
123// Avoid heap allocation on items without attributes.
124pub(super) type Attrs = Option<Arc<[Attr]>>;
125
126#[derive(Debug, PartialEq, Eq, Clone)]
127pub(super) struct RawItem {
128 pub(super) attrs: Attrs,
129 pub(super) kind: RawItemKind,
130}
131
122#[derive(Debug, PartialEq, Eq, Clone, Copy)] 132#[derive(Debug, PartialEq, Eq, Clone, Copy)]
123pub(super) enum RawItem { 133pub(super) enum RawItemKind {
124 Module(Module), 134 Module(Module),
125 Import(ImportId), 135 Import(ImportId),
126 Def(Def), 136 Def(Def),
@@ -215,6 +225,7 @@ impl<DB: AstDatabase> RawItemsCollector<&DB> {
215 } 225 }
216 226
217 fn add_item(&mut self, current_module: Option<Module>, item: ast::ModuleItem) { 227 fn add_item(&mut self, current_module: Option<Module>, item: ast::ModuleItem) {
228 let attrs = self.parse_attrs(&item);
218 let (kind, name) = match item { 229 let (kind, name) = match item {
219 ast::ModuleItem::Module(module) => { 230 ast::ModuleItem::Module(module) => {
220 self.add_module(current_module, module); 231 self.add_module(current_module, module);
@@ -263,7 +274,7 @@ impl<DB: AstDatabase> RawItemsCollector<&DB> {
263 if let Some(name) = name { 274 if let Some(name) = name {
264 let name = name.as_name(); 275 let name = name.as_name();
265 let def = self.raw_items.defs.alloc(DefData { name, kind }); 276 let def = self.raw_items.defs.alloc(DefData { name, kind });
266 self.push_item(current_module, RawItem::Def(def)) 277 self.push_item(current_module, attrs, RawItemKind::Def(def));
267 } 278 }
268 } 279 }
269 280
@@ -272,8 +283,10 @@ impl<DB: AstDatabase> RawItemsCollector<&DB> {
272 Some(it) => it.as_name(), 283 Some(it) => it.as_name(),
273 None => return, 284 None => return,
274 }; 285 };
286 let attrs = self.parse_attrs(&module);
275 287
276 let ast_id = self.source_ast_id_map.ast_id(&module); 288 let ast_id = self.source_ast_id_map.ast_id(&module);
289 // FIXME: cfg_attr
277 let is_macro_use = module.has_atom_attr("macro_use"); 290 let is_macro_use = module.has_atom_attr("macro_use");
278 if module.has_semi() { 291 if module.has_semi() {
279 let attr_path = extract_mod_path_attribute(&module); 292 let attr_path = extract_mod_path_attribute(&module);
@@ -283,7 +296,7 @@ impl<DB: AstDatabase> RawItemsCollector<&DB> {
283 attr_path, 296 attr_path,
284 is_macro_use, 297 is_macro_use,
285 }); 298 });
286 self.push_item(current_module, RawItem::Module(item)); 299 self.push_item(current_module, attrs, RawItemKind::Module(item));
287 return; 300 return;
288 } 301 }
289 302
@@ -297,14 +310,16 @@ impl<DB: AstDatabase> RawItemsCollector<&DB> {
297 is_macro_use, 310 is_macro_use,
298 }); 311 });
299 self.process_module(Some(item), item_list); 312 self.process_module(Some(item), item_list);
300 self.push_item(current_module, RawItem::Module(item)); 313 self.push_item(current_module, attrs, RawItemKind::Module(item));
301 return; 314 return;
302 } 315 }
303 tested_by!(name_res_works_for_broken_modules); 316 tested_by!(name_res_works_for_broken_modules);
304 } 317 }
305 318
306 fn add_use_item(&mut self, current_module: Option<Module>, use_item: ast::UseItem) { 319 fn add_use_item(&mut self, current_module: Option<Module>, use_item: ast::UseItem) {
320 // FIXME: cfg_attr
307 let is_prelude = use_item.has_atom_attr("prelude_import"); 321 let is_prelude = use_item.has_atom_attr("prelude_import");
322 let attrs = self.parse_attrs(&use_item);
308 323
309 Path::expand_use_item( 324 Path::expand_use_item(
310 Source { ast: use_item, file_id: self.file_id }, 325 Source { ast: use_item, file_id: self.file_id },
@@ -318,7 +333,12 @@ impl<DB: AstDatabase> RawItemsCollector<&DB> {
318 is_extern_crate: false, 333 is_extern_crate: false,
319 is_macro_use: false, 334 is_macro_use: false,
320 }; 335 };
321 self.push_import(current_module, import_data, Either::A(AstPtr::new(use_tree))); 336 self.push_import(
337 current_module,
338 attrs.clone(),
339 import_data,
340 Either::A(AstPtr::new(use_tree)),
341 );
322 }, 342 },
323 ) 343 )
324 } 344 }
@@ -331,6 +351,8 @@ impl<DB: AstDatabase> RawItemsCollector<&DB> {
331 if let Some(name_ref) = extern_crate.name_ref() { 351 if let Some(name_ref) = extern_crate.name_ref() {
332 let path = Path::from_name_ref(&name_ref); 352 let path = Path::from_name_ref(&name_ref);
333 let alias = extern_crate.alias().and_then(|a| a.name()).map(|it| it.as_name()); 353 let alias = extern_crate.alias().and_then(|a| a.name()).map(|it| it.as_name());
354 let attrs = self.parse_attrs(&extern_crate);
355 // FIXME: cfg_attr
334 let is_macro_use = extern_crate.has_atom_attr("macro_use"); 356 let is_macro_use = extern_crate.has_atom_attr("macro_use");
335 let import_data = ImportData { 357 let import_data = ImportData {
336 path, 358 path,
@@ -340,11 +362,17 @@ impl<DB: AstDatabase> RawItemsCollector<&DB> {
340 is_extern_crate: true, 362 is_extern_crate: true,
341 is_macro_use, 363 is_macro_use,
342 }; 364 };
343 self.push_import(current_module, import_data, Either::B(AstPtr::new(&extern_crate))); 365 self.push_import(
366 current_module,
367 attrs,
368 import_data,
369 Either::B(AstPtr::new(&extern_crate)),
370 );
344 } 371 }
345 } 372 }
346 373
347 fn add_macro(&mut self, current_module: Option<Module>, m: ast::MacroCall) { 374 fn add_macro(&mut self, current_module: Option<Module>, m: ast::MacroCall) {
375 let attrs = self.parse_attrs(&m);
348 let path = match m 376 let path = match m
349 .path() 377 .path()
350 .and_then(|path| Path::from_src(Source { ast: path, file_id: self.file_id }, self.db)) 378 .and_then(|path| Path::from_src(Source { ast: path, file_id: self.file_id }, self.db))
@@ -355,24 +383,26 @@ impl<DB: AstDatabase> RawItemsCollector<&DB> {
355 383
356 let name = m.name().map(|it| it.as_name()); 384 let name = m.name().map(|it| it.as_name());
357 let ast_id = self.source_ast_id_map.ast_id(&m); 385 let ast_id = self.source_ast_id_map.ast_id(&m);
386 // FIXME: cfg_attr
358 let export = m.attrs().filter_map(|x| x.simple_name()).any(|name| name == "macro_export"); 387 let export = m.attrs().filter_map(|x| x.simple_name()).any(|name| name == "macro_export");
359 388
360 let m = self.raw_items.macros.alloc(MacroData { ast_id, path, name, export }); 389 let m = self.raw_items.macros.alloc(MacroData { ast_id, path, name, export });
361 self.push_item(current_module, RawItem::Macro(m)); 390 self.push_item(current_module, attrs, RawItemKind::Macro(m));
362 } 391 }
363 392
364 fn push_import( 393 fn push_import(
365 &mut self, 394 &mut self,
366 current_module: Option<Module>, 395 current_module: Option<Module>,
396 attrs: Attrs,
367 data: ImportData, 397 data: ImportData,
368 source: ImportSourcePtr, 398 source: ImportSourcePtr,
369 ) { 399 ) {
370 let import = self.raw_items.imports.alloc(data); 400 let import = self.raw_items.imports.alloc(data);
371 self.source_map.insert(import, source); 401 self.source_map.insert(import, source);
372 self.push_item(current_module, RawItem::Import(import)) 402 self.push_item(current_module, attrs, RawItemKind::Import(import))
373 } 403 }
374 404
375 fn push_item(&mut self, current_module: Option<Module>, item: RawItem) { 405 fn push_item(&mut self, current_module: Option<Module>, attrs: Attrs, kind: RawItemKind) {
376 match current_module { 406 match current_module {
377 Some(module) => match &mut self.raw_items.modules[module] { 407 Some(module) => match &mut self.raw_items.modules[module] {
378 ModuleData::Definition { items, .. } => items, 408 ModuleData::Definition { items, .. } => items,
@@ -380,7 +410,11 @@ impl<DB: AstDatabase> RawItemsCollector<&DB> {
380 }, 410 },
381 None => &mut self.raw_items.items, 411 None => &mut self.raw_items.items,
382 } 412 }
383 .push(item) 413 .push(RawItem { attrs, kind })
414 }
415
416 fn parse_attrs(&self, item: &impl ast::AttrsOwner) -> Attrs {
417 Attr::from_attrs_owner(self.file_id, item, self.db)
384 } 418 }
385} 419}
386 420
diff --git a/crates/ra_hir/src/nameres/tests.rs b/crates/ra_hir/src/nameres/tests.rs
index bc4b47b70..34dd79574 100644
--- a/crates/ra_hir/src/nameres/tests.rs
+++ b/crates/ra_hir/src/nameres/tests.rs
@@ -7,6 +7,7 @@ mod mod_resolution;
7use std::sync::Arc; 7use std::sync::Arc;
8 8
9use insta::assert_snapshot; 9use insta::assert_snapshot;
10use ra_cfg::CfgOptions;
10use ra_db::SourceDatabase; 11use ra_db::SourceDatabase;
11use test_utils::covers; 12use test_utils::covers;
12 13
@@ -507,3 +508,72 @@ fn values_dont_shadow_extern_crates() {
507 ⋮foo: v 508 ⋮foo: v
508 "###); 509 "###);
509} 510}
511
512#[test]
513fn cfg_not_test() {
514 let map = def_map_with_crate_graph(
515 r#"
516 //- /main.rs
517 use {Foo, Bar, Baz};
518 //- /lib.rs
519 #[prelude_import]
520 pub use self::prelude::*;
521 mod prelude {
522 #[cfg(test)]
523 pub struct Foo;
524 #[cfg(not(test))]
525 pub struct Bar;
526 #[cfg(all(not(any()), feature = "foo", feature = "bar", opt = "42"))]
527 pub struct Baz;
528 }
529 "#,
530 crate_graph! {
531 "main": ("/main.rs", ["std"]),
532 "std": ("/lib.rs", []),
533 },
534 );
535
536 assert_snapshot!(map, @r###"
537 ⋮crate
538 ⋮Bar: t v
539 ⋮Baz: _
540 ⋮Foo: _
541 "###);
542}
543
544#[test]
545fn cfg_test() {
546 let map = def_map_with_crate_graph(
547 r#"
548 //- /main.rs
549 use {Foo, Bar, Baz};
550 //- /lib.rs
551 #[prelude_import]
552 pub use self::prelude::*;
553 mod prelude {
554 #[cfg(test)]
555 pub struct Foo;
556 #[cfg(not(test))]
557 pub struct Bar;
558 #[cfg(all(not(any()), feature = "foo", feature = "bar", opt = "42"))]
559 pub struct Baz;
560 }
561 "#,
562 crate_graph! {
563 "main": ("/main.rs", ["std"]),
564 "std": ("/lib.rs", [], CfgOptions::default()
565 .atom("test".into())
566 .key_value("feature".into(), "foo".into())
567 .key_value("feature".into(), "bar".into())
568 .key_value("opt".into(), "42".into())
569 ),
570 },
571 );
572
573 assert_snapshot!(map, @r###"
574 ⋮crate
575 ⋮Bar: _
576 ⋮Baz: t v
577 ⋮Foo: t v
578 "###);
579}
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs
index 66f63ca24..25dad81eb 100644
--- a/crates/ra_hir/src/ty/tests.rs
+++ b/crates/ra_hir/src/ty/tests.rs
@@ -3,6 +3,7 @@ use std::sync::Arc;
3 3
4use insta::assert_snapshot; 4use insta::assert_snapshot;
5 5
6use ra_cfg::CfgOptions;
6use ra_db::{salsa::Database, FilePosition, SourceDatabase}; 7use ra_db::{salsa::Database, FilePosition, SourceDatabase};
7use ra_syntax::{ 8use ra_syntax::{
8 algo, 9 algo,
@@ -24,6 +25,50 @@ mod never_type;
24mod coercion; 25mod coercion;
25 26
26#[test] 27#[test]
28fn cfg_impl_block() {
29 let (mut db, pos) = MockDatabase::with_position(
30 r#"
31//- /main.rs
32use foo::S as T;
33struct S;
34
35#[cfg(test)]
36impl S {
37 fn foo1(&self) -> i32 { 0 }
38}
39
40#[cfg(not(test))]
41impl S {
42 fn foo2(&self) -> i32 { 0 }
43}
44
45fn test() {
46 let t = (S.foo1(), S.foo2(), T.foo3(), T.foo4());
47 t<|>;
48}
49
50//- /foo.rs
51struct S;
52
53#[cfg(not(test))]
54impl S {
55 fn foo3(&self) -> i32 { 0 }
56}
57
58#[cfg(test)]
59impl S {
60 fn foo4(&self) -> i32 { 0 }
61}
62"#,
63 );
64 db.set_crate_graph_from_fixture(crate_graph! {
65 "main": ("/main.rs", ["foo"], CfgOptions::default().atom("test".into())),
66 "foo": ("/foo.rs", []),
67 });
68 assert_eq!("(i32, {unknown}, i32, {unknown})", type_at_pos(&db, pos));
69}
70
71#[test]
27fn infer_await() { 72fn infer_await() {
28 let (mut db, pos) = MockDatabase::with_position( 73 let (mut db, pos) = MockDatabase::with_position(
29 r#" 74 r#"