diff options
29 files changed, 671 insertions, 66 deletions
diff --git a/Cargo.lock b/Cargo.lock index 988f7ec0b..736f1994e 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -923,6 +923,16 @@ dependencies = [ | |||
923 | ] | 923 | ] |
924 | 924 | ||
925 | [[package]] | 925 | [[package]] |
926 | name = "ra_cfg" | ||
927 | version = "0.1.0" | ||
928 | dependencies = [ | ||
929 | "ra_mbe 0.1.0", | ||
930 | "ra_syntax 0.1.0", | ||
931 | "ra_tt 0.1.0", | ||
932 | "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||
933 | ] | ||
934 | |||
935 | [[package]] | ||
926 | name = "ra_cli" | 936 | name = "ra_cli" |
927 | version = "0.1.0" | 937 | version = "0.1.0" |
928 | dependencies = [ | 938 | dependencies = [ |
@@ -941,6 +951,7 @@ dependencies = [ | |||
941 | name = "ra_db" | 951 | name = "ra_db" |
942 | version = "0.1.0" | 952 | version = "0.1.0" |
943 | dependencies = [ | 953 | dependencies = [ |
954 | "ra_cfg 0.1.0", | ||
944 | "ra_prof 0.1.0", | 955 | "ra_prof 0.1.0", |
945 | "ra_syntax 0.1.0", | 956 | "ra_syntax 0.1.0", |
946 | "relative-path 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | 957 | "relative-path 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", |
@@ -971,6 +982,7 @@ dependencies = [ | |||
971 | "once_cell 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", | 982 | "once_cell 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", |
972 | "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", | 983 | "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", |
973 | "ra_arena 0.1.0", | 984 | "ra_arena 0.1.0", |
985 | "ra_cfg 0.1.0", | ||
974 | "ra_db 0.1.0", | 986 | "ra_db 0.1.0", |
975 | "ra_mbe 0.1.0", | 987 | "ra_mbe 0.1.0", |
976 | "ra_prof 0.1.0", | 988 | "ra_prof 0.1.0", |
@@ -993,6 +1005,7 @@ dependencies = [ | |||
993 | "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", | 1005 | "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", |
994 | "proptest 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)", | 1006 | "proptest 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)", |
995 | "ra_assists 0.1.0", | 1007 | "ra_assists 0.1.0", |
1008 | "ra_cfg 0.1.0", | ||
996 | "ra_db 0.1.0", | 1009 | "ra_db 0.1.0", |
997 | "ra_fmt 0.1.0", | 1010 | "ra_fmt 0.1.0", |
998 | "ra_hir 0.1.0", | 1011 | "ra_hir 0.1.0", |
@@ -1019,6 +1032,7 @@ dependencies = [ | |||
1019 | "lsp-server 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", | 1032 | "lsp-server 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", |
1020 | "lsp-types 0.61.0 (registry+https://github.com/rust-lang/crates.io-index)", | 1033 | "lsp-types 0.61.0 (registry+https://github.com/rust-lang/crates.io-index)", |
1021 | "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", | 1034 | "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", |
1035 | "ra_cfg 0.1.0", | ||
1022 | "ra_db 0.1.0", | 1036 | "ra_db 0.1.0", |
1023 | "ra_ide_api 0.1.0", | 1037 | "ra_ide_api 0.1.0", |
1024 | "ra_prof 0.1.0", | 1038 | "ra_prof 0.1.0", |
@@ -1075,6 +1089,7 @@ dependencies = [ | |||
1075 | "cargo_metadata 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", | 1089 | "cargo_metadata 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", |
1076 | "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", | 1090 | "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", |
1077 | "ra_arena 0.1.0", | 1091 | "ra_arena 0.1.0", |
1092 | "ra_cfg 0.1.0", | ||
1078 | "ra_db 0.1.0", | 1093 | "ra_db 0.1.0", |
1079 | "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", | 1094 | "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", |
1080 | "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", | 1095 | "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", |
diff --git a/crates/ra_batch/src/lib.rs b/crates/ra_batch/src/lib.rs index 939f72037..a5fc2a23e 100644 --- a/crates/ra_batch/src/lib.rs +++ b/crates/ra_batch/src/lib.rs | |||
@@ -7,7 +7,7 @@ use rustc_hash::FxHashMap; | |||
7 | use crossbeam_channel::{unbounded, Receiver}; | 7 | use crossbeam_channel::{unbounded, Receiver}; |
8 | use ra_db::{CrateGraph, FileId, SourceRootId}; | 8 | use ra_db::{CrateGraph, FileId, SourceRootId}; |
9 | use ra_ide_api::{AnalysisChange, AnalysisHost, FeatureFlags}; | 9 | use ra_ide_api::{AnalysisChange, AnalysisHost, FeatureFlags}; |
10 | use ra_project_model::{PackageRoot, ProjectWorkspace}; | 10 | use ra_project_model::{get_rustc_cfg_options, PackageRoot, ProjectWorkspace}; |
11 | use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask, Watch}; | 11 | use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask, Watch}; |
12 | use ra_vfs_glob::RustPackageFilterBuilder; | 12 | use ra_vfs_glob::RustPackageFilterBuilder; |
13 | 13 | ||
@@ -41,11 +41,17 @@ pub fn load_cargo(root: &Path) -> Result<(AnalysisHost, FxHashMap<SourceRootId, | |||
41 | sender, | 41 | sender, |
42 | Watch(false), | 42 | Watch(false), |
43 | ); | 43 | ); |
44 | let (crate_graph, _crate_names) = ws.to_crate_graph(&mut |path: &Path| { | 44 | |
45 | let vfs_file = vfs.load(path); | 45 | // FIXME: cfg options? |
46 | log::debug!("vfs file {:?} -> {:?}", path, vfs_file); | 46 | let default_cfg_options = |
47 | vfs_file.map(vfs_file_to_id) | 47 | get_rustc_cfg_options().atom("test".into()).atom("debug_assertion".into()); |
48 | }); | 48 | |
49 | let (crate_graph, _crate_names) = | ||
50 | ws.to_crate_graph(&default_cfg_options, &mut |path: &Path| { | ||
51 | let vfs_file = vfs.load(path); | ||
52 | log::debug!("vfs file {:?} -> {:?}", path, vfs_file); | ||
53 | vfs_file.map(vfs_file_to_id) | ||
54 | }); | ||
49 | log::debug!("crate graph: {:?}", crate_graph); | 55 | log::debug!("crate graph: {:?}", crate_graph); |
50 | 56 | ||
51 | let source_roots = roots | 57 | let source_roots = roots |
diff --git a/crates/ra_cfg/Cargo.toml b/crates/ra_cfg/Cargo.toml new file mode 100644 index 000000000..b28affc3a --- /dev/null +++ b/crates/ra_cfg/Cargo.toml | |||
@@ -0,0 +1,14 @@ | |||
1 | [package] | ||
2 | edition = "2018" | ||
3 | name = "ra_cfg" | ||
4 | version = "0.1.0" | ||
5 | authors = ["rust-analyzer developers"] | ||
6 | |||
7 | [dependencies] | ||
8 | rustc-hash = "1.0.1" | ||
9 | |||
10 | ra_syntax = { path = "../ra_syntax" } | ||
11 | tt = { path = "../ra_tt", package = "ra_tt" } | ||
12 | |||
13 | [dev-dependencies] | ||
14 | mbe = { path = "../ra_mbe", package = "ra_mbe" } | ||
diff --git a/crates/ra_cfg/src/cfg_expr.rs b/crates/ra_cfg/src/cfg_expr.rs new file mode 100644 index 000000000..39d71851c --- /dev/null +++ b/crates/ra_cfg/src/cfg_expr.rs | |||
@@ -0,0 +1,132 @@ | |||
1 | //! The condition expression used in `#[cfg(..)]` attributes. | ||
2 | //! | ||
3 | //! See: https://doc.rust-lang.org/reference/conditional-compilation.html#conditional-compilation | ||
4 | |||
5 | use std::slice::Iter as SliceIter; | ||
6 | |||
7 | use ra_syntax::SmolStr; | ||
8 | use tt::{Leaf, Subtree, TokenTree}; | ||
9 | |||
10 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
11 | pub enum CfgExpr { | ||
12 | Invalid, | ||
13 | Atom(SmolStr), | ||
14 | KeyValue { key: SmolStr, value: SmolStr }, | ||
15 | All(Vec<CfgExpr>), | ||
16 | Any(Vec<CfgExpr>), | ||
17 | Not(Box<CfgExpr>), | ||
18 | } | ||
19 | |||
20 | impl CfgExpr { | ||
21 | /// Fold the cfg by querying all basic `Atom` and `KeyValue` predicates. | ||
22 | pub fn fold(&self, query: &dyn Fn(&SmolStr, Option<&SmolStr>) -> bool) -> Option<bool> { | ||
23 | match self { | ||
24 | CfgExpr::Invalid => None, | ||
25 | CfgExpr::Atom(name) => Some(query(name, None)), | ||
26 | CfgExpr::KeyValue { key, value } => Some(query(key, Some(value))), | ||
27 | CfgExpr::All(preds) => { | ||
28 | preds.iter().try_fold(true, |s, pred| Some(s && pred.fold(query)?)) | ||
29 | } | ||
30 | CfgExpr::Any(preds) => { | ||
31 | preds.iter().try_fold(false, |s, pred| Some(s || pred.fold(query)?)) | ||
32 | } | ||
33 | CfgExpr::Not(pred) => pred.fold(query).map(|s| !s), | ||
34 | } | ||
35 | } | ||
36 | } | ||
37 | |||
38 | pub fn parse_cfg(tt: &Subtree) -> CfgExpr { | ||
39 | next_cfg_expr(&mut tt.token_trees.iter()).unwrap_or(CfgExpr::Invalid) | ||
40 | } | ||
41 | |||
42 | fn next_cfg_expr(it: &mut SliceIter<tt::TokenTree>) -> Option<CfgExpr> { | ||
43 | let name = match it.next() { | ||
44 | None => return None, | ||
45 | Some(TokenTree::Leaf(Leaf::Ident(ident))) => ident.text.clone(), | ||
46 | Some(_) => return Some(CfgExpr::Invalid), | ||
47 | }; | ||
48 | |||
49 | // Peek | ||
50 | let ret = match it.as_slice().first() { | ||
51 | Some(TokenTree::Leaf(Leaf::Punct(punct))) if punct.char == '=' => { | ||
52 | match it.as_slice().get(1) { | ||
53 | Some(TokenTree::Leaf(Leaf::Literal(literal))) => { | ||
54 | it.next(); | ||
55 | it.next(); | ||
56 | // FIXME: escape? raw string? | ||
57 | let value = | ||
58 | SmolStr::new(literal.text.trim_start_matches('"').trim_end_matches('"')); | ||
59 | CfgExpr::KeyValue { key: name, value } | ||
60 | } | ||
61 | _ => return Some(CfgExpr::Invalid), | ||
62 | } | ||
63 | } | ||
64 | Some(TokenTree::Subtree(subtree)) => { | ||
65 | it.next(); | ||
66 | let mut sub_it = subtree.token_trees.iter(); | ||
67 | let mut subs = std::iter::from_fn(|| next_cfg_expr(&mut sub_it)).collect(); | ||
68 | match name.as_str() { | ||
69 | "all" => CfgExpr::All(subs), | ||
70 | "any" => CfgExpr::Any(subs), | ||
71 | "not" => CfgExpr::Not(Box::new(subs.pop().unwrap_or(CfgExpr::Invalid))), | ||
72 | _ => CfgExpr::Invalid, | ||
73 | } | ||
74 | } | ||
75 | _ => CfgExpr::Atom(name), | ||
76 | }; | ||
77 | |||
78 | // Eat comma separator | ||
79 | if let Some(TokenTree::Leaf(Leaf::Punct(punct))) = it.as_slice().first() { | ||
80 | if punct.char == ',' { | ||
81 | it.next(); | ||
82 | } | ||
83 | } | ||
84 | Some(ret) | ||
85 | } | ||
86 | |||
87 | #[cfg(test)] | ||
88 | mod tests { | ||
89 | use super::*; | ||
90 | |||
91 | use mbe::ast_to_token_tree; | ||
92 | use ra_syntax::ast::{self, AstNode}; | ||
93 | |||
94 | fn assert_parse_result(input: &str, expected: CfgExpr) { | ||
95 | let source_file = ast::SourceFile::parse(input).ok().unwrap(); | ||
96 | let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); | ||
97 | let (tt, _) = ast_to_token_tree(&tt).unwrap(); | ||
98 | assert_eq!(parse_cfg(&tt), expected); | ||
99 | } | ||
100 | |||
101 | #[test] | ||
102 | fn test_cfg_expr_parser() { | ||
103 | assert_parse_result("#![cfg(foo)]", CfgExpr::Atom("foo".into())); | ||
104 | assert_parse_result("#![cfg(foo,)]", CfgExpr::Atom("foo".into())); | ||
105 | assert_parse_result( | ||
106 | "#![cfg(not(foo))]", | ||
107 | CfgExpr::Not(Box::new(CfgExpr::Atom("foo".into()))), | ||
108 | ); | ||
109 | assert_parse_result("#![cfg(foo(bar))]", CfgExpr::Invalid); | ||
110 | |||
111 | // Only take the first | ||
112 | assert_parse_result(r#"#![cfg(foo, bar = "baz")]"#, CfgExpr::Atom("foo".into())); | ||
113 | |||
114 | assert_parse_result( | ||
115 | r#"#![cfg(all(foo, bar = "baz"))]"#, | ||
116 | CfgExpr::All(vec![ | ||
117 | CfgExpr::Atom("foo".into()), | ||
118 | CfgExpr::KeyValue { key: "bar".into(), value: "baz".into() }, | ||
119 | ]), | ||
120 | ); | ||
121 | |||
122 | assert_parse_result( | ||
123 | r#"#![cfg(any(not(), all(), , bar = "baz",))]"#, | ||
124 | CfgExpr::Any(vec![ | ||
125 | CfgExpr::Not(Box::new(CfgExpr::Invalid)), | ||
126 | CfgExpr::All(vec![]), | ||
127 | CfgExpr::Invalid, | ||
128 | CfgExpr::KeyValue { key: "bar".into(), value: "baz".into() }, | ||
129 | ]), | ||
130 | ); | ||
131 | } | ||
132 | } | ||
diff --git a/crates/ra_cfg/src/lib.rs b/crates/ra_cfg/src/lib.rs new file mode 100644 index 000000000..e1c92fbba --- /dev/null +++ b/crates/ra_cfg/src/lib.rs | |||
@@ -0,0 +1,61 @@ | |||
1 | //! ra_cfg defines conditional compiling options, `cfg` attibute parser and evaluator | ||
2 | use std::iter::IntoIterator; | ||
3 | |||
4 | use ra_syntax::SmolStr; | ||
5 | use rustc_hash::FxHashSet; | ||
6 | |||
7 | mod cfg_expr; | ||
8 | |||
9 | pub use cfg_expr::{parse_cfg, CfgExpr}; | ||
10 | |||
11 | /// Configuration options used for conditional compilition on items with `cfg` attributes. | ||
12 | /// We have two kind of options in different namespaces: atomic options like `unix`, and | ||
13 | /// key-value options like `target_arch="x86"`. | ||
14 | /// | ||
15 | /// Note that for key-value options, one key can have multiple values (but not none). | ||
16 | /// `feature` is an example. We have both `feature="foo"` and `feature="bar"` if features | ||
17 | /// `foo` and `bar` are both enabled. And here, we store key-value options as a set of tuple | ||
18 | /// of key and value in `key_values`. | ||
19 | /// | ||
20 | /// See: https://doc.rust-lang.org/reference/conditional-compilation.html#set-configuration-options | ||
21 | #[derive(Debug, Clone, PartialEq, Eq, Default)] | ||
22 | pub struct CfgOptions { | ||
23 | atoms: FxHashSet<SmolStr>, | ||
24 | key_values: FxHashSet<(SmolStr, SmolStr)>, | ||
25 | } | ||
26 | |||
27 | impl CfgOptions { | ||
28 | pub fn check(&self, cfg: &CfgExpr) -> Option<bool> { | ||
29 | cfg.fold(&|key, value| match value { | ||
30 | None => self.atoms.contains(key), | ||
31 | Some(value) => self.key_values.contains(&(key.clone(), value.clone())), | ||
32 | }) | ||
33 | } | ||
34 | |||
35 | pub fn is_cfg_enabled(&self, attr: &tt::Subtree) -> Option<bool> { | ||
36 | self.check(&parse_cfg(attr)) | ||
37 | } | ||
38 | |||
39 | pub fn atom(mut self, name: SmolStr) -> CfgOptions { | ||
40 | self.atoms.insert(name); | ||
41 | self | ||
42 | } | ||
43 | |||
44 | pub fn key_value(mut self, key: SmolStr, value: SmolStr) -> CfgOptions { | ||
45 | self.key_values.insert((key, value)); | ||
46 | self | ||
47 | } | ||
48 | |||
49 | /// Shortcut to set features | ||
50 | pub fn features(mut self, iter: impl IntoIterator<Item = SmolStr>) -> CfgOptions { | ||
51 | for feat in iter { | ||
52 | self = self.key_value("feature".into(), feat); | ||
53 | } | ||
54 | self | ||
55 | } | ||
56 | |||
57 | pub fn remove_atom(mut self, name: &SmolStr) -> CfgOptions { | ||
58 | self.atoms.remove(name); | ||
59 | self | ||
60 | } | ||
61 | } | ||
diff --git a/crates/ra_db/Cargo.toml b/crates/ra_db/Cargo.toml index 2fac07bc5..c141f1a88 100644 --- a/crates/ra_db/Cargo.toml +++ b/crates/ra_db/Cargo.toml | |||
@@ -10,4 +10,5 @@ relative-path = "0.4.0" | |||
10 | rustc-hash = "1.0" | 10 | rustc-hash = "1.0" |
11 | 11 | ||
12 | ra_syntax = { path = "../ra_syntax" } | 12 | ra_syntax = { path = "../ra_syntax" } |
13 | ra_cfg = { path = "../ra_cfg" } | ||
13 | ra_prof = { path = "../ra_prof" } | 14 | ra_prof = { path = "../ra_prof" } |
diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs index 52f892891..23148096c 100644 --- a/crates/ra_db/src/input.rs +++ b/crates/ra_db/src/input.rs | |||
@@ -9,6 +9,7 @@ | |||
9 | use relative_path::{RelativePath, RelativePathBuf}; | 9 | use relative_path::{RelativePath, RelativePathBuf}; |
10 | use rustc_hash::FxHashMap; | 10 | use rustc_hash::FxHashMap; |
11 | 11 | ||
12 | use ra_cfg::CfgOptions; | ||
12 | use ra_syntax::SmolStr; | 13 | use ra_syntax::SmolStr; |
13 | use rustc_hash::FxHashSet; | 14 | use rustc_hash::FxHashSet; |
14 | 15 | ||
@@ -109,11 +110,12 @@ struct CrateData { | |||
109 | file_id: FileId, | 110 | file_id: FileId, |
110 | edition: Edition, | 111 | edition: Edition, |
111 | dependencies: Vec<Dependency>, | 112 | dependencies: Vec<Dependency>, |
113 | cfg_options: CfgOptions, | ||
112 | } | 114 | } |
113 | 115 | ||
114 | impl CrateData { | 116 | impl CrateData { |
115 | fn new(file_id: FileId, edition: Edition) -> CrateData { | 117 | fn new(file_id: FileId, edition: Edition, cfg_options: CfgOptions) -> CrateData { |
116 | CrateData { file_id, edition, dependencies: Vec::new() } | 118 | CrateData { file_id, edition, dependencies: Vec::new(), cfg_options } |
117 | } | 119 | } |
118 | 120 | ||
119 | fn add_dep(&mut self, name: SmolStr, crate_id: CrateId) { | 121 | fn add_dep(&mut self, name: SmolStr, crate_id: CrateId) { |
@@ -134,13 +136,22 @@ impl Dependency { | |||
134 | } | 136 | } |
135 | 137 | ||
136 | impl CrateGraph { | 138 | impl CrateGraph { |
137 | pub fn add_crate_root(&mut self, file_id: FileId, edition: Edition) -> CrateId { | 139 | pub fn add_crate_root( |
140 | &mut self, | ||
141 | file_id: FileId, | ||
142 | edition: Edition, | ||
143 | cfg_options: CfgOptions, | ||
144 | ) -> CrateId { | ||
138 | let crate_id = CrateId(self.arena.len() as u32); | 145 | let crate_id = CrateId(self.arena.len() as u32); |
139 | let prev = self.arena.insert(crate_id, CrateData::new(file_id, edition)); | 146 | let prev = self.arena.insert(crate_id, CrateData::new(file_id, edition, cfg_options)); |
140 | assert!(prev.is_none()); | 147 | assert!(prev.is_none()); |
141 | crate_id | 148 | crate_id |
142 | } | 149 | } |
143 | 150 | ||
151 | pub fn cfg_options(&self, crate_id: CrateId) -> &CfgOptions { | ||
152 | &self.arena[&crate_id].cfg_options | ||
153 | } | ||
154 | |||
144 | pub fn add_dep( | 155 | pub fn add_dep( |
145 | &mut self, | 156 | &mut self, |
146 | from: CrateId, | 157 | from: CrateId, |
@@ -221,14 +232,14 @@ impl CrateGraph { | |||
221 | 232 | ||
222 | #[cfg(test)] | 233 | #[cfg(test)] |
223 | mod tests { | 234 | mod tests { |
224 | use super::{CrateGraph, Edition::Edition2018, FileId, SmolStr}; | 235 | use super::{CfgOptions, CrateGraph, Edition::Edition2018, FileId, SmolStr}; |
225 | 236 | ||
226 | #[test] | 237 | #[test] |
227 | fn it_should_panic_because_of_cycle_dependencies() { | 238 | fn it_should_panic_because_of_cycle_dependencies() { |
228 | let mut graph = CrateGraph::default(); | 239 | let mut graph = CrateGraph::default(); |
229 | let crate1 = graph.add_crate_root(FileId(1u32), Edition2018); | 240 | let crate1 = graph.add_crate_root(FileId(1u32), Edition2018, CfgOptions::default()); |
230 | let crate2 = graph.add_crate_root(FileId(2u32), Edition2018); | 241 | let crate2 = graph.add_crate_root(FileId(2u32), Edition2018, CfgOptions::default()); |
231 | let crate3 = graph.add_crate_root(FileId(3u32), Edition2018); | 242 | let crate3 = graph.add_crate_root(FileId(3u32), Edition2018, CfgOptions::default()); |
232 | assert!(graph.add_dep(crate1, SmolStr::new("crate2"), crate2).is_ok()); | 243 | assert!(graph.add_dep(crate1, SmolStr::new("crate2"), crate2).is_ok()); |
233 | assert!(graph.add_dep(crate2, SmolStr::new("crate3"), crate3).is_ok()); | 244 | assert!(graph.add_dep(crate2, SmolStr::new("crate3"), crate3).is_ok()); |
234 | assert!(graph.add_dep(crate3, SmolStr::new("crate1"), crate1).is_err()); | 245 | assert!(graph.add_dep(crate3, SmolStr::new("crate1"), crate1).is_err()); |
@@ -237,9 +248,9 @@ mod tests { | |||
237 | #[test] | 248 | #[test] |
238 | fn it_works() { | 249 | fn it_works() { |
239 | let mut graph = CrateGraph::default(); | 250 | let mut graph = CrateGraph::default(); |
240 | let crate1 = graph.add_crate_root(FileId(1u32), Edition2018); | 251 | let crate1 = graph.add_crate_root(FileId(1u32), Edition2018, CfgOptions::default()); |
241 | let crate2 = graph.add_crate_root(FileId(2u32), Edition2018); | 252 | let crate2 = graph.add_crate_root(FileId(2u32), Edition2018, CfgOptions::default()); |
242 | let crate3 = graph.add_crate_root(FileId(3u32), Edition2018); | 253 | let crate3 = graph.add_crate_root(FileId(3u32), Edition2018, CfgOptions::default()); |
243 | assert!(graph.add_dep(crate1, SmolStr::new("crate2"), crate2).is_ok()); | 254 | assert!(graph.add_dep(crate1, SmolStr::new("crate2"), crate2).is_ok()); |
244 | assert!(graph.add_dep(crate2, SmolStr::new("crate3"), crate3).is_ok()); | 255 | assert!(graph.add_dep(crate2, SmolStr::new("crate3"), crate3).is_ok()); |
245 | } | 256 | } |
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 | ||
16 | ra_syntax = { path = "../ra_syntax" } | 16 | ra_syntax = { path = "../ra_syntax" } |
17 | ra_arena = { path = "../ra_arena" } | 17 | ra_arena = { path = "../ra_arena" } |
18 | ra_cfg = { path = "../ra_cfg" } | ||
18 | ra_db = { path = "../ra_db" } | 19 | ra_db = { path = "../ra_db" } |
19 | mbe = { path = "../ra_mbe", package = "ra_mbe" } | 20 | mbe = { path = "../ra_mbe", package = "ra_mbe" } |
20 | tt = { path = "../ra_tt", package = "ra_tt" } | 21 | tt = { 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 | |||
3 | use std::sync::Arc; | ||
4 | |||
5 | use mbe::ast_to_token_tree; | ||
6 | use ra_cfg::CfgOptions; | ||
7 | use ra_syntax::{ | ||
8 | ast::{self, AstNode, AttrsOwner}, | ||
9 | SmolStr, | ||
10 | }; | ||
11 | use tt::Subtree; | ||
12 | |||
13 | use crate::{db::AstDatabase, path::Path, HirFileId, Source}; | ||
14 | |||
15 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
16 | pub(crate) struct Attr { | ||
17 | pub(crate) path: Path, | ||
18 | pub(crate) input: Option<AttrInput>, | ||
19 | } | ||
20 | |||
21 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
22 | pub enum AttrInput { | ||
23 | Literal(SmolStr), | ||
24 | TokenTree(Subtree), | ||
25 | } | ||
26 | |||
27 | impl 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; | |||
4 | use std::sync::Arc; | 4 | use std::sync::Arc; |
5 | 5 | ||
6 | use ra_arena::{impl_arena_id, map::ArenaMap, Arena, RawId}; | 6 | use ra_arena::{impl_arena_id, map::ArenaMap, Arena, RawId}; |
7 | use ra_cfg::CfgOptions; | ||
7 | use ra_syntax::{ | 8 | use ra_syntax::{ |
8 | ast::{self, AstNode}, | 9 | ast::{self, AstNode}, |
9 | AstPtr, | 10 | AstPtr, |
10 | }; | 11 | }; |
11 | 12 | ||
12 | use crate::{ | 13 | use 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 { | |||
176 | impl ModuleImplBlocks { | 178 | impl 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, ¯o_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(¯o_call).with_file_id(file_id); | 240 | let ast_id = db.ast_id_map(file_id).ast_id(¯o_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; | |||
44 | mod type_alias; | 44 | mod type_alias; |
45 | mod type_ref; | 45 | mod type_ref; |
46 | mod ty; | 46 | mod ty; |
47 | mod attr; | ||
47 | mod impl_block; | 48 | mod impl_block; |
48 | mod expr; | 49 | mod expr; |
49 | mod lang_item; | 50 | mod 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 @@ | |||
3 | use std::{panic, sync::Arc}; | 3 | use std::{panic, sync::Arc}; |
4 | 4 | ||
5 | use parking_lot::Mutex; | 5 | use parking_lot::Mutex; |
6 | use ra_cfg::CfgOptions; | ||
6 | use ra_db::{ | 7 | use 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)] |
271 | pub struct CrateGraphFixture(pub Vec<(String, (String, Edition, Vec<String>))>); | 272 | pub struct CrateGraphFixture(pub Vec<(String, (String, Edition, CfgOptions, Vec<String>))>); |
272 | 273 | ||
273 | #[macro_export] | 274 | #[macro_export] |
274 | macro_rules! crate_graph { | 275 | macro_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 | ||
3 | use ra_cfg::CfgOptions; | ||
3 | use ra_db::FileId; | 4 | use ra_db::FileId; |
4 | use ra_syntax::{ast, SmolStr}; | 5 | use ra_syntax::{ast, SmolStr}; |
5 | use rustc_hash::FxHashMap; | 6 | use 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 |
79 | struct DefCollector<DB> { | 84 | struct 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 | ||
91 | impl<'a, DB> DefCollector<&'a DB> | 98 | impl<DB> DefCollector<'_, DB> |
92 | where | 99 | where |
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 | ||
509 | impl<DB> ModCollector<'_, &'_ mut DefCollector<&'_ DB>> | 516 | impl<DB> ModCollector<'_, &'_ mut DefCollector<'_, DB>> |
510 | where | 517 | where |
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 | ||
708 | fn is_macro_rules(path: &Path) -> bool { | 726 | fn 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::{ | |||
10 | use test_utils::tested_by; | 10 | use test_utils::tested_by; |
11 | 11 | ||
12 | use crate::{ | 12 | use 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. | ||
124 | pub(super) type Attrs = Option<Arc<[Attr]>>; | ||
125 | |||
126 | #[derive(Debug, PartialEq, Eq, Clone)] | ||
127 | pub(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)] |
123 | pub(super) enum RawItem { | 133 | pub(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; | |||
7 | use std::sync::Arc; | 7 | use std::sync::Arc; |
8 | 8 | ||
9 | use insta::assert_snapshot; | 9 | use insta::assert_snapshot; |
10 | use ra_cfg::CfgOptions; | ||
10 | use ra_db::SourceDatabase; | 11 | use ra_db::SourceDatabase; |
11 | use test_utils::covers; | 12 | use 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] | ||
513 | fn 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] | ||
545 | fn 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 | ||
4 | use insta::assert_snapshot; | 4 | use insta::assert_snapshot; |
5 | 5 | ||
6 | use ra_cfg::CfgOptions; | ||
6 | use ra_db::{salsa::Database, FilePosition, SourceDatabase}; | 7 | use ra_db::{salsa::Database, FilePosition, SourceDatabase}; |
7 | use ra_syntax::{ | 8 | use ra_syntax::{ |
8 | algo, | 9 | algo, |
@@ -24,6 +25,50 @@ mod never_type; | |||
24 | mod coercion; | 25 | mod coercion; |
25 | 26 | ||
26 | #[test] | 27 | #[test] |
28 | fn cfg_impl_block() { | ||
29 | let (mut db, pos) = MockDatabase::with_position( | ||
30 | r#" | ||
31 | //- /main.rs | ||
32 | use foo::S as T; | ||
33 | struct S; | ||
34 | |||
35 | #[cfg(test)] | ||
36 | impl S { | ||
37 | fn foo1(&self) -> i32 { 0 } | ||
38 | } | ||
39 | |||
40 | #[cfg(not(test))] | ||
41 | impl S { | ||
42 | fn foo2(&self) -> i32 { 0 } | ||
43 | } | ||
44 | |||
45 | fn test() { | ||
46 | let t = (S.foo1(), S.foo2(), T.foo3(), T.foo4()); | ||
47 | t<|>; | ||
48 | } | ||
49 | |||
50 | //- /foo.rs | ||
51 | struct S; | ||
52 | |||
53 | #[cfg(not(test))] | ||
54 | impl S { | ||
55 | fn foo3(&self) -> i32 { 0 } | ||
56 | } | ||
57 | |||
58 | #[cfg(test)] | ||
59 | impl 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] | ||
27 | fn infer_await() { | 72 | fn infer_await() { |
28 | let (mut db, pos) = MockDatabase::with_position( | 73 | let (mut db, pos) = MockDatabase::with_position( |
29 | r#" | 74 | r#" |
diff --git a/crates/ra_ide_api/Cargo.toml b/crates/ra_ide_api/Cargo.toml index 6bbf9d5dd..f919a2d61 100644 --- a/crates/ra_ide_api/Cargo.toml +++ b/crates/ra_ide_api/Cargo.toml | |||
@@ -23,6 +23,7 @@ rand = { version = "0.7.0", features = ["small_rng"] } | |||
23 | ra_syntax = { path = "../ra_syntax" } | 23 | ra_syntax = { path = "../ra_syntax" } |
24 | ra_text_edit = { path = "../ra_text_edit" } | 24 | ra_text_edit = { path = "../ra_text_edit" } |
25 | ra_db = { path = "../ra_db" } | 25 | ra_db = { path = "../ra_db" } |
26 | ra_cfg = { path = "../ra_cfg" } | ||
26 | ra_fmt = { path = "../ra_fmt" } | 27 | ra_fmt = { path = "../ra_fmt" } |
27 | ra_prof = { path = "../ra_prof" } | 28 | ra_prof = { path = "../ra_prof" } |
28 | hir = { path = "../ra_hir", package = "ra_hir" } | 29 | hir = { path = "../ra_hir", package = "ra_hir" } |
diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs index 44d1ec77b..24f1b91f6 100644 --- a/crates/ra_ide_api/src/lib.rs +++ b/crates/ra_ide_api/src/lib.rs | |||
@@ -49,6 +49,7 @@ mod test_utils; | |||
49 | 49 | ||
50 | use std::sync::Arc; | 50 | use std::sync::Arc; |
51 | 51 | ||
52 | use ra_cfg::CfgOptions; | ||
52 | use ra_db::{ | 53 | use ra_db::{ |
53 | salsa::{self, ParallelDatabase}, | 54 | salsa::{self, ParallelDatabase}, |
54 | CheckCanceled, SourceDatabase, | 55 | CheckCanceled, SourceDatabase, |
@@ -322,7 +323,10 @@ impl Analysis { | |||
322 | change.add_root(source_root, true); | 323 | change.add_root(source_root, true); |
323 | let mut crate_graph = CrateGraph::default(); | 324 | let mut crate_graph = CrateGraph::default(); |
324 | let file_id = FileId(0); | 325 | let file_id = FileId(0); |
325 | crate_graph.add_crate_root(file_id, Edition::Edition2018); | 326 | // FIXME: cfg options |
327 | // Default to enable test for single file. | ||
328 | let cfg_options = CfgOptions::default().atom("test".into()); | ||
329 | crate_graph.add_crate_root(file_id, Edition::Edition2018, cfg_options); | ||
326 | change.add_file(source_root, file_id, "main.rs".into(), Arc::new(text)); | 330 | change.add_file(source_root, file_id, "main.rs".into(), Arc::new(text)); |
327 | change.set_crate_graph(crate_graph); | 331 | change.set_crate_graph(crate_graph); |
328 | host.apply_change(change); | 332 | host.apply_change(change); |
diff --git a/crates/ra_ide_api/src/mock_analysis.rs b/crates/ra_ide_api/src/mock_analysis.rs index 16870c7ae..80b71894c 100644 --- a/crates/ra_ide_api/src/mock_analysis.rs +++ b/crates/ra_ide_api/src/mock_analysis.rs | |||
@@ -2,6 +2,7 @@ | |||
2 | 2 | ||
3 | use std::sync::Arc; | 3 | use std::sync::Arc; |
4 | 4 | ||
5 | use ra_cfg::CfgOptions; | ||
5 | use relative_path::RelativePathBuf; | 6 | use relative_path::RelativePathBuf; |
6 | use test_utils::{extract_offset, extract_range, parse_fixture, CURSOR_MARKER}; | 7 | use test_utils::{extract_offset, extract_range, parse_fixture, CURSOR_MARKER}; |
7 | 8 | ||
@@ -93,10 +94,11 @@ impl MockAnalysis { | |||
93 | assert!(path.starts_with('/')); | 94 | assert!(path.starts_with('/')); |
94 | let path = RelativePathBuf::from_path(&path[1..]).unwrap(); | 95 | let path = RelativePathBuf::from_path(&path[1..]).unwrap(); |
95 | let file_id = FileId(i as u32 + 1); | 96 | let file_id = FileId(i as u32 + 1); |
97 | let cfg_options = CfgOptions::default(); | ||
96 | if path == "/lib.rs" || path == "/main.rs" { | 98 | if path == "/lib.rs" || path == "/main.rs" { |
97 | root_crate = Some(crate_graph.add_crate_root(file_id, Edition2018)); | 99 | root_crate = Some(crate_graph.add_crate_root(file_id, Edition2018, cfg_options)); |
98 | } else if path.ends_with("/lib.rs") { | 100 | } else if path.ends_with("/lib.rs") { |
99 | let other_crate = crate_graph.add_crate_root(file_id, Edition2018); | 101 | let other_crate = crate_graph.add_crate_root(file_id, Edition2018, cfg_options); |
100 | let crate_name = path.parent().unwrap().file_name().unwrap(); | 102 | let crate_name = path.parent().unwrap().file_name().unwrap(); |
101 | if let Some(root_crate) = root_crate { | 103 | if let Some(root_crate) = root_crate { |
102 | crate_graph.add_dep(root_crate, crate_name.into(), other_crate).unwrap(); | 104 | crate_graph.add_dep(root_crate, crate_name.into(), other_crate).unwrap(); |
diff --git a/crates/ra_ide_api/src/parent_module.rs b/crates/ra_ide_api/src/parent_module.rs index c85f1d0d0..566509849 100644 --- a/crates/ra_ide_api/src/parent_module.rs +++ b/crates/ra_ide_api/src/parent_module.rs | |||
@@ -41,6 +41,7 @@ mod tests { | |||
41 | AnalysisChange, CrateGraph, | 41 | AnalysisChange, CrateGraph, |
42 | Edition::Edition2018, | 42 | Edition::Edition2018, |
43 | }; | 43 | }; |
44 | use ra_cfg::CfgOptions; | ||
44 | 45 | ||
45 | #[test] | 46 | #[test] |
46 | fn test_resolve_parent_module() { | 47 | fn test_resolve_parent_module() { |
@@ -88,7 +89,7 @@ mod tests { | |||
88 | assert!(host.analysis().crate_for(mod_file).unwrap().is_empty()); | 89 | assert!(host.analysis().crate_for(mod_file).unwrap().is_empty()); |
89 | 90 | ||
90 | let mut crate_graph = CrateGraph::default(); | 91 | let mut crate_graph = CrateGraph::default(); |
91 | let crate_id = crate_graph.add_crate_root(root_file, Edition2018); | 92 | let crate_id = crate_graph.add_crate_root(root_file, Edition2018, CfgOptions::default()); |
92 | let mut change = AnalysisChange::new(); | 93 | let mut change = AnalysisChange::new(); |
93 | change.set_crate_graph(crate_graph); | 94 | change.set_crate_graph(crate_graph); |
94 | host.apply_change(change); | 95 | host.apply_change(change); |
diff --git a/crates/ra_lsp_server/Cargo.toml b/crates/ra_lsp_server/Cargo.toml index 677d81835..aedc55a95 100644 --- a/crates/ra_lsp_server/Cargo.toml +++ b/crates/ra_lsp_server/Cargo.toml | |||
@@ -19,6 +19,7 @@ jod-thread = "0.1.0" | |||
19 | ra_vfs = "0.4.0" | 19 | ra_vfs = "0.4.0" |
20 | ra_syntax = { path = "../ra_syntax" } | 20 | ra_syntax = { path = "../ra_syntax" } |
21 | ra_db = { path = "../ra_db" } | 21 | ra_db = { path = "../ra_db" } |
22 | ra_cfg = { path = "../ra_cfg" } | ||
22 | ra_text_edit = { path = "../ra_text_edit" } | 23 | ra_text_edit = { path = "../ra_text_edit" } |
23 | ra_ide_api = { path = "../ra_ide_api" } | 24 | ra_ide_api = { path = "../ra_ide_api" } |
24 | lsp-server = "0.2.0" | 25 | lsp-server = "0.2.0" |
diff --git a/crates/ra_lsp_server/src/world.rs b/crates/ra_lsp_server/src/world.rs index b55046ec9..27da751ab 100644 --- a/crates/ra_lsp_server/src/world.rs +++ b/crates/ra_lsp_server/src/world.rs | |||
@@ -13,7 +13,7 @@ use ra_ide_api::{ | |||
13 | Analysis, AnalysisChange, AnalysisHost, CrateGraph, FeatureFlags, FileId, LibraryData, | 13 | Analysis, AnalysisChange, AnalysisHost, CrateGraph, FeatureFlags, FileId, LibraryData, |
14 | SourceRootId, | 14 | SourceRootId, |
15 | }; | 15 | }; |
16 | use ra_project_model::ProjectWorkspace; | 16 | use ra_project_model::{get_rustc_cfg_options, ProjectWorkspace}; |
17 | use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask, Watch}; | 17 | use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask, Watch}; |
18 | use ra_vfs_glob::{Glob, RustPackageFilterBuilder}; | 18 | use ra_vfs_glob::{Glob, RustPackageFilterBuilder}; |
19 | use relative_path::RelativePathBuf; | 19 | use relative_path::RelativePathBuf; |
@@ -97,6 +97,10 @@ impl WorldState { | |||
97 | change.set_debug_root_path(SourceRootId(r.0), vfs_root_path.display().to_string()); | 97 | change.set_debug_root_path(SourceRootId(r.0), vfs_root_path.display().to_string()); |
98 | } | 98 | } |
99 | 99 | ||
100 | // FIXME: Read default cfgs from config | ||
101 | let default_cfg_options = | ||
102 | get_rustc_cfg_options().atom("test".into()).atom("debug_assertion".into()); | ||
103 | |||
100 | // Create crate graph from all the workspaces | 104 | // Create crate graph from all the workspaces |
101 | let mut crate_graph = CrateGraph::default(); | 105 | let mut crate_graph = CrateGraph::default(); |
102 | let mut load = |path: &std::path::Path| { | 106 | let mut load = |path: &std::path::Path| { |
@@ -104,7 +108,7 @@ impl WorldState { | |||
104 | vfs_file.map(|f| FileId(f.0)) | 108 | vfs_file.map(|f| FileId(f.0)) |
105 | }; | 109 | }; |
106 | for ws in workspaces.iter() { | 110 | for ws in workspaces.iter() { |
107 | let (graph, crate_names) = ws.to_crate_graph(&mut load); | 111 | let (graph, crate_names) = ws.to_crate_graph(&default_cfg_options, &mut load); |
108 | let shift = crate_graph.extend(graph); | 112 | let shift = crate_graph.extend(graph); |
109 | for (crate_id, name) in crate_names { | 113 | for (crate_id, name) in crate_names { |
110 | change.set_debug_crate_name(crate_id.shift(shift), name) | 114 | change.set_debug_crate_name(crate_id.shift(shift), name) |
diff --git a/crates/ra_lsp_server/tests/heavy_tests/main.rs b/crates/ra_lsp_server/tests/heavy_tests/main.rs index 152681062..2ba82ab05 100644 --- a/crates/ra_lsp_server/tests/heavy_tests/main.rs +++ b/crates/ra_lsp_server/tests/heavy_tests/main.rs | |||
@@ -286,7 +286,13 @@ fn test_missing_module_code_action_in_json_project() { | |||
286 | 286 | ||
287 | let project = json!({ | 287 | let project = json!({ |
288 | "roots": [path], | 288 | "roots": [path], |
289 | "crates": [ { "root_module": path.join("src/lib.rs"), "deps": [], "edition": "2015" } ] | 289 | "crates": [ { |
290 | "root_module": path.join("src/lib.rs"), | ||
291 | "deps": [], | ||
292 | "edition": "2015", | ||
293 | "atom_cfgs": [], | ||
294 | "key_value_cfgs": {} | ||
295 | } ] | ||
290 | }); | 296 | }); |
291 | 297 | ||
292 | let code = format!( | 298 | let code = format!( |
diff --git a/crates/ra_project_model/Cargo.toml b/crates/ra_project_model/Cargo.toml index ae6b91aa6..a65100031 100644 --- a/crates/ra_project_model/Cargo.toml +++ b/crates/ra_project_model/Cargo.toml | |||
@@ -12,6 +12,7 @@ cargo_metadata = "0.8.2" | |||
12 | 12 | ||
13 | ra_arena = { path = "../ra_arena" } | 13 | ra_arena = { path = "../ra_arena" } |
14 | ra_db = { path = "../ra_db" } | 14 | ra_db = { path = "../ra_db" } |
15 | ra_cfg = { path = "../ra_cfg" } | ||
15 | 16 | ||
16 | serde = { version = "1.0.89", features = ["derive"] } | 17 | serde = { version = "1.0.89", features = ["derive"] } |
17 | serde_json = "1.0.39" | 18 | serde_json = "1.0.39" |
diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs index 805eaa178..28dadea9d 100644 --- a/crates/ra_project_model/src/cargo_workspace.rs +++ b/crates/ra_project_model/src/cargo_workspace.rs | |||
@@ -39,6 +39,7 @@ struct PackageData { | |||
39 | is_member: bool, | 39 | is_member: bool, |
40 | dependencies: Vec<PackageDependency>, | 40 | dependencies: Vec<PackageDependency>, |
41 | edition: Edition, | 41 | edition: Edition, |
42 | features: Vec<String>, | ||
42 | } | 43 | } |
43 | 44 | ||
44 | #[derive(Debug, Clone)] | 45 | #[derive(Debug, Clone)] |
@@ -91,6 +92,9 @@ impl Package { | |||
91 | pub fn edition(self, ws: &CargoWorkspace) -> Edition { | 92 | pub fn edition(self, ws: &CargoWorkspace) -> Edition { |
92 | ws.packages[self].edition | 93 | ws.packages[self].edition |
93 | } | 94 | } |
95 | pub fn features(self, ws: &CargoWorkspace) -> &[String] { | ||
96 | &ws.packages[self].features | ||
97 | } | ||
94 | pub fn targets<'a>(self, ws: &'a CargoWorkspace) -> impl Iterator<Item = Target> + 'a { | 98 | pub fn targets<'a>(self, ws: &'a CargoWorkspace) -> impl Iterator<Item = Target> + 'a { |
95 | ws.packages[self].targets.iter().cloned() | 99 | ws.packages[self].targets.iter().cloned() |
96 | } | 100 | } |
@@ -144,6 +148,7 @@ impl CargoWorkspace { | |||
144 | is_member, | 148 | is_member, |
145 | edition: Edition::from_string(&meta_pkg.edition), | 149 | edition: Edition::from_string(&meta_pkg.edition), |
146 | dependencies: Vec::new(), | 150 | dependencies: Vec::new(), |
151 | features: Vec::new(), | ||
147 | }); | 152 | }); |
148 | let pkg_data = &mut packages[pkg]; | 153 | let pkg_data = &mut packages[pkg]; |
149 | pkg_by_id.insert(meta_pkg.id.clone(), pkg); | 154 | pkg_by_id.insert(meta_pkg.id.clone(), pkg); |
@@ -164,6 +169,7 @@ impl CargoWorkspace { | |||
164 | let dep = PackageDependency { name: dep_node.name, pkg: pkg_by_id[&dep_node.pkg] }; | 169 | let dep = PackageDependency { name: dep_node.name, pkg: pkg_by_id[&dep_node.pkg] }; |
165 | packages[source].dependencies.push(dep); | 170 | packages[source].dependencies.push(dep); |
166 | } | 171 | } |
172 | packages[source].features.extend(node.features); | ||
167 | } | 173 | } |
168 | 174 | ||
169 | Ok(CargoWorkspace { packages, targets, workspace_root: meta.workspace_root }) | 175 | Ok(CargoWorkspace { packages, targets, workspace_root: meta.workspace_root }) |
diff --git a/crates/ra_project_model/src/json_project.rs b/crates/ra_project_model/src/json_project.rs index 54ddca2cb..1bacb1d09 100644 --- a/crates/ra_project_model/src/json_project.rs +++ b/crates/ra_project_model/src/json_project.rs | |||
@@ -2,6 +2,7 @@ | |||
2 | 2 | ||
3 | use std::path::PathBuf; | 3 | use std::path::PathBuf; |
4 | 4 | ||
5 | use rustc_hash::{FxHashMap, FxHashSet}; | ||
5 | use serde::Deserialize; | 6 | use serde::Deserialize; |
6 | 7 | ||
7 | /// A root points to the directory which contains Rust crates. rust-analyzer watches all files in | 8 | /// A root points to the directory which contains Rust crates. rust-analyzer watches all files in |
@@ -19,6 +20,8 @@ pub struct Crate { | |||
19 | pub(crate) root_module: PathBuf, | 20 | pub(crate) root_module: PathBuf, |
20 | pub(crate) edition: Edition, | 21 | pub(crate) edition: Edition, |
21 | pub(crate) deps: Vec<Dep>, | 22 | pub(crate) deps: Vec<Dep>, |
23 | pub(crate) atom_cfgs: FxHashSet<String>, | ||
24 | pub(crate) key_value_cfgs: FxHashMap<String, String>, | ||
22 | } | 25 | } |
23 | 26 | ||
24 | #[derive(Clone, Copy, Debug, Deserialize)] | 27 | #[derive(Clone, Copy, Debug, Deserialize)] |
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index 5d3078598..640a5ebd3 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs | |||
@@ -9,8 +9,10 @@ use std::{ | |||
9 | fs::File, | 9 | fs::File, |
10 | io::BufReader, | 10 | io::BufReader, |
11 | path::{Path, PathBuf}, | 11 | path::{Path, PathBuf}, |
12 | process::Command, | ||
12 | }; | 13 | }; |
13 | 14 | ||
15 | use ra_cfg::CfgOptions; | ||
14 | use ra_db::{CrateGraph, CrateId, Edition, FileId}; | 16 | use ra_db::{CrateGraph, CrateId, Edition, FileId}; |
15 | use rustc_hash::FxHashMap; | 17 | use rustc_hash::FxHashMap; |
16 | use serde_json::from_reader; | 18 | use serde_json::from_reader; |
@@ -117,6 +119,7 @@ impl ProjectWorkspace { | |||
117 | 119 | ||
118 | pub fn to_crate_graph( | 120 | pub fn to_crate_graph( |
119 | &self, | 121 | &self, |
122 | default_cfg_options: &CfgOptions, | ||
120 | load: &mut dyn FnMut(&Path) -> Option<FileId>, | 123 | load: &mut dyn FnMut(&Path) -> Option<FileId>, |
121 | ) -> (CrateGraph, FxHashMap<CrateId, String>) { | 124 | ) -> (CrateGraph, FxHashMap<CrateId, String>) { |
122 | let mut crate_graph = CrateGraph::default(); | 125 | let mut crate_graph = CrateGraph::default(); |
@@ -131,7 +134,17 @@ impl ProjectWorkspace { | |||
131 | json_project::Edition::Edition2015 => Edition::Edition2015, | 134 | json_project::Edition::Edition2015 => Edition::Edition2015, |
132 | json_project::Edition::Edition2018 => Edition::Edition2018, | 135 | json_project::Edition::Edition2018 => Edition::Edition2018, |
133 | }; | 136 | }; |
134 | crates.insert(crate_id, crate_graph.add_crate_root(file_id, edition)); | 137 | let mut cfg_options = default_cfg_options.clone(); |
138 | for name in &krate.atom_cfgs { | ||
139 | cfg_options = cfg_options.atom(name.into()); | ||
140 | } | ||
141 | for (key, value) in &krate.key_value_cfgs { | ||
142 | cfg_options = cfg_options.key_value(key.into(), value.into()); | ||
143 | } | ||
144 | crates.insert( | ||
145 | crate_id, | ||
146 | crate_graph.add_crate_root(file_id, edition, cfg_options), | ||
147 | ); | ||
135 | } | 148 | } |
136 | } | 149 | } |
137 | 150 | ||
@@ -157,7 +170,10 @@ impl ProjectWorkspace { | |||
157 | let mut sysroot_crates = FxHashMap::default(); | 170 | let mut sysroot_crates = FxHashMap::default(); |
158 | for krate in sysroot.crates() { | 171 | for krate in sysroot.crates() { |
159 | if let Some(file_id) = load(krate.root(&sysroot)) { | 172 | if let Some(file_id) = load(krate.root(&sysroot)) { |
160 | let crate_id = crate_graph.add_crate_root(file_id, Edition::Edition2018); | 173 | // Crates from sysroot have `cfg(test)` disabled |
174 | let cfg_options = default_cfg_options.clone().remove_atom(&"test".into()); | ||
175 | let crate_id = | ||
176 | crate_graph.add_crate_root(file_id, Edition::Edition2018, cfg_options); | ||
161 | sysroot_crates.insert(krate, crate_id); | 177 | sysroot_crates.insert(krate, crate_id); |
162 | names.insert(crate_id, krate.name(&sysroot).to_string()); | 178 | names.insert(crate_id, krate.name(&sysroot).to_string()); |
163 | } | 179 | } |
@@ -186,7 +202,11 @@ impl ProjectWorkspace { | |||
186 | let root = tgt.root(&cargo); | 202 | let root = tgt.root(&cargo); |
187 | if let Some(file_id) = load(root) { | 203 | if let Some(file_id) = load(root) { |
188 | let edition = pkg.edition(&cargo); | 204 | let edition = pkg.edition(&cargo); |
189 | let crate_id = crate_graph.add_crate_root(file_id, edition); | 205 | let cfg_options = default_cfg_options |
206 | .clone() | ||
207 | .features(pkg.features(&cargo).iter().map(Into::into)); | ||
208 | let crate_id = | ||
209 | crate_graph.add_crate_root(file_id, edition, cfg_options); | ||
190 | names.insert(crate_id, pkg.name(&cargo).to_string()); | 210 | names.insert(crate_id, pkg.name(&cargo).to_string()); |
191 | if tgt.kind(&cargo) == TargetKind::Lib { | 211 | if tgt.kind(&cargo) == TargetKind::Lib { |
192 | lib_tgt = Some(crate_id); | 212 | lib_tgt = Some(crate_id); |
@@ -286,3 +306,32 @@ fn find_cargo_toml(path: &Path) -> Result<PathBuf> { | |||
286 | } | 306 | } |
287 | Err(format!("can't find Cargo.toml at {}", path.display()))? | 307 | Err(format!("can't find Cargo.toml at {}", path.display()))? |
288 | } | 308 | } |
309 | |||
310 | pub fn get_rustc_cfg_options() -> CfgOptions { | ||
311 | let mut cfg_options = CfgOptions::default(); | ||
312 | |||
313 | match (|| -> Result<_> { | ||
314 | // `cfg(test)` and `cfg(debug_assertion)` are handled outside, so we suppress them here. | ||
315 | let output = Command::new("rustc").args(&["--print", "cfg", "-O"]).output()?; | ||
316 | if !output.status.success() { | ||
317 | Err("failed to get rustc cfgs")?; | ||
318 | } | ||
319 | Ok(String::from_utf8(output.stdout)?) | ||
320 | })() { | ||
321 | Ok(rustc_cfgs) => { | ||
322 | for line in rustc_cfgs.lines() { | ||
323 | match line.find('=') { | ||
324 | None => cfg_options = cfg_options.atom(line.into()), | ||
325 | Some(pos) => { | ||
326 | let key = &line[..pos]; | ||
327 | let value = line[pos + 1..].trim_matches('"'); | ||
328 | cfg_options = cfg_options.key_value(key.into(), value.into()); | ||
329 | } | ||
330 | } | ||
331 | } | ||
332 | } | ||
333 | Err(e) => log::error!("failed to get rustc cfgs: {}", e), | ||
334 | } | ||
335 | |||
336 | cfg_options | ||
337 | } | ||
diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs index 3bb5571ee..34b22c3e2 100644 --- a/crates/ra_syntax/src/ast/generated.rs +++ b/crates/ra_syntax/src/ast/generated.rs | |||
@@ -1962,6 +1962,7 @@ impl AstNode for ModuleItem { | |||
1962 | } | 1962 | } |
1963 | } | 1963 | } |
1964 | } | 1964 | } |
1965 | impl ast::AttrsOwner for ModuleItem {} | ||
1965 | impl ModuleItem {} | 1966 | impl ModuleItem {} |
1966 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 1967 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
1967 | pub struct Name { | 1968 | pub struct Name { |
diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron index 17477a696..25e6f64ce 100644 --- a/crates/ra_syntax/src/grammar.ron +++ b/crates/ra_syntax/src/grammar.ron | |||
@@ -397,7 +397,8 @@ Grammar( | |||
397 | ), | 397 | ), |
398 | "ModuleItem": ( | 398 | "ModuleItem": ( |
399 | enum: ["StructDef", "EnumDef", "FnDef", "TraitDef", "TypeAliasDef", "ImplBlock", | 399 | enum: ["StructDef", "EnumDef", "FnDef", "TraitDef", "TypeAliasDef", "ImplBlock", |
400 | "UseItem", "ExternCrateItem", "ConstDef", "StaticDef", "Module" ] | 400 | "UseItem", "ExternCrateItem", "ConstDef", "StaticDef", "Module" ], |
401 | traits: ["AttrsOwner"] | ||
401 | ), | 402 | ), |
402 | "ImplItem": ( | 403 | "ImplItem": ( |
403 | enum: ["FnDef", "TypeAliasDef", "ConstDef"] | 404 | enum: ["FnDef", "TypeAliasDef", "ConstDef"] |