diff options
author | Aleksey Kladov <[email protected]> | 2020-05-24 14:34:35 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2020-05-24 14:34:35 +0100 |
commit | 130318b8231c2ad5a95fe3681dd32f0040f15255 (patch) | |
tree | 8d67b9cbad2b5f64e4135e626b80646bed851ca6 | |
parent | f26b7928e065f067d7181d3425fe17526dc3bcd1 (diff) | |
parent | 27ed376bc4dfed39295af650effe63007e443b6f (diff) |
Merge pull request #4548 from bnjjj/fix_4464
add support of feature flag for runnables
-rw-r--r-- | Cargo.lock | 3 | ||||
-rw-r--r-- | crates/ra_cfg/src/cfg_expr.rs | 10 | ||||
-rw-r--r-- | crates/ra_hir/src/lib.rs | 1 | ||||
-rw-r--r-- | crates/ra_hir_def/src/attr.rs | 2 | ||||
-rw-r--r-- | crates/ra_hir_def/src/body.rs | 2 | ||||
-rw-r--r-- | crates/ra_ide/src/runnables.rs | 143 | ||||
-rw-r--r-- | crates/rust-analyzer/Cargo.toml | 3 | ||||
-rw-r--r-- | crates/rust-analyzer/src/cargo_target_spec.rs | 8 | ||||
-rw-r--r-- | crates/rust-analyzer/src/main_loop/handlers.rs | 81 |
9 files changed, 234 insertions, 19 deletions
diff --git a/Cargo.lock b/Cargo.lock index a511e0d28..ca6c64166 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -1372,17 +1372,20 @@ dependencies = [ | |||
1372 | "lsp-types", | 1372 | "lsp-types", |
1373 | "parking_lot", | 1373 | "parking_lot", |
1374 | "pico-args", | 1374 | "pico-args", |
1375 | "ra_cfg", | ||
1375 | "ra_db", | 1376 | "ra_db", |
1376 | "ra_flycheck", | 1377 | "ra_flycheck", |
1377 | "ra_hir", | 1378 | "ra_hir", |
1378 | "ra_hir_def", | 1379 | "ra_hir_def", |
1379 | "ra_hir_ty", | 1380 | "ra_hir_ty", |
1380 | "ra_ide", | 1381 | "ra_ide", |
1382 | "ra_mbe", | ||
1381 | "ra_proc_macro_srv", | 1383 | "ra_proc_macro_srv", |
1382 | "ra_prof", | 1384 | "ra_prof", |
1383 | "ra_project_model", | 1385 | "ra_project_model", |
1384 | "ra_syntax", | 1386 | "ra_syntax", |
1385 | "ra_text_edit", | 1387 | "ra_text_edit", |
1388 | "ra_tt", | ||
1386 | "ra_vfs", | 1389 | "ra_vfs", |
1387 | "rand", | 1390 | "rand", |
1388 | "relative-path", | 1391 | "relative-path", |
diff --git a/crates/ra_cfg/src/cfg_expr.rs b/crates/ra_cfg/src/cfg_expr.rs index 39d71851c..85b100c6a 100644 --- a/crates/ra_cfg/src/cfg_expr.rs +++ b/crates/ra_cfg/src/cfg_expr.rs | |||
@@ -88,13 +88,17 @@ fn next_cfg_expr(it: &mut SliceIter<tt::TokenTree>) -> Option<CfgExpr> { | |||
88 | mod tests { | 88 | mod tests { |
89 | use super::*; | 89 | use super::*; |
90 | 90 | ||
91 | use mbe::ast_to_token_tree; | 91 | use mbe::{ast_to_token_tree, TokenMap}; |
92 | use ra_syntax::ast::{self, AstNode}; | 92 | use ra_syntax::ast::{self, AstNode}; |
93 | 93 | ||
94 | fn assert_parse_result(input: &str, expected: CfgExpr) { | 94 | fn get_token_tree_generated(input: &str) -> (tt::Subtree, TokenMap) { |
95 | let source_file = ast::SourceFile::parse(input).ok().unwrap(); | 95 | let source_file = ast::SourceFile::parse(input).ok().unwrap(); |
96 | let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); | 96 | let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); |
97 | let (tt, _) = ast_to_token_tree(&tt).unwrap(); | 97 | ast_to_token_tree(&tt).unwrap() |
98 | } | ||
99 | |||
100 | fn assert_parse_result(input: &str, expected: CfgExpr) { | ||
101 | let (tt, _) = get_token_tree_generated(input); | ||
98 | assert_eq!(parse_cfg(&tt), expected); | 102 | assert_eq!(parse_cfg(&tt), expected); |
99 | } | 103 | } |
100 | 104 | ||
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index c5df4ac24..3364a822f 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs | |||
@@ -62,6 +62,7 @@ pub use crate::{ | |||
62 | 62 | ||
63 | pub use hir_def::{ | 63 | pub use hir_def::{ |
64 | adt::StructKind, | 64 | adt::StructKind, |
65 | attr::Attrs, | ||
65 | body::scope::ExprScopes, | 66 | body::scope::ExprScopes, |
66 | builtin_type::BuiltinType, | 67 | builtin_type::BuiltinType, |
67 | docs::Documentation, | 68 | docs::Documentation, |
diff --git a/crates/ra_hir_def/src/attr.rs b/crates/ra_hir_def/src/attr.rs index 576cd0c65..8b6c0bede 100644 --- a/crates/ra_hir_def/src/attr.rs +++ b/crates/ra_hir_def/src/attr.rs | |||
@@ -81,7 +81,7 @@ impl Attrs { | |||
81 | } | 81 | } |
82 | } | 82 | } |
83 | 83 | ||
84 | fn from_attrs_owner(db: &dyn DefDatabase, owner: InFile<&dyn AttrsOwner>) -> Attrs { | 84 | pub fn from_attrs_owner(db: &dyn DefDatabase, owner: InFile<&dyn AttrsOwner>) -> Attrs { |
85 | let hygiene = Hygiene::new(db.upcast(), owner.file_id); | 85 | let hygiene = Hygiene::new(db.upcast(), owner.file_id); |
86 | Attrs::new(owner.value, &hygiene) | 86 | Attrs::new(owner.value, &hygiene) |
87 | } | 87 | } |
diff --git a/crates/ra_hir_def/src/body.rs b/crates/ra_hir_def/src/body.rs index f5a7305dc..273036cee 100644 --- a/crates/ra_hir_def/src/body.rs +++ b/crates/ra_hir_def/src/body.rs | |||
@@ -29,7 +29,7 @@ use crate::{ | |||
29 | AsMacroCall, DefWithBodyId, HasModule, Lookup, ModuleId, | 29 | AsMacroCall, DefWithBodyId, HasModule, Lookup, ModuleId, |
30 | }; | 30 | }; |
31 | 31 | ||
32 | /// A subset of Exander that only deals with cfg attributes. We only need it to | 32 | /// A subset of Expander that only deals with cfg attributes. We only need it to |
33 | /// avoid cyclic queries in crate def map during enum processing. | 33 | /// avoid cyclic queries in crate def map during enum processing. |
34 | pub(crate) struct CfgExpander { | 34 | pub(crate) struct CfgExpander { |
35 | cfg_options: CfgOptions, | 35 | cfg_options: CfgOptions, |
diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs index 131b8f307..ed98e58e0 100644 --- a/crates/ra_ide/src/runnables.rs +++ b/crates/ra_ide/src/runnables.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use hir::{AsAssocItem, Semantics}; | 3 | use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics}; |
4 | use itertools::Itertools; | 4 | use itertools::Itertools; |
5 | use ra_ide_db::RootDatabase; | 5 | use ra_ide_db::RootDatabase; |
6 | use ra_syntax::{ | 6 | use ra_syntax::{ |
@@ -10,12 +10,14 @@ use ra_syntax::{ | |||
10 | 10 | ||
11 | use crate::FileId; | 11 | use crate::FileId; |
12 | use ast::DocCommentsOwner; | 12 | use ast::DocCommentsOwner; |
13 | use ra_cfg::CfgExpr; | ||
13 | use std::fmt::Display; | 14 | use std::fmt::Display; |
14 | 15 | ||
15 | #[derive(Debug)] | 16 | #[derive(Debug)] |
16 | pub struct Runnable { | 17 | pub struct Runnable { |
17 | pub range: TextRange, | 18 | pub range: TextRange, |
18 | pub kind: RunnableKind, | 19 | pub kind: RunnableKind, |
20 | pub cfg_exprs: Vec<CfgExpr>, | ||
19 | } | 21 | } |
20 | 22 | ||
21 | #[derive(Debug)] | 23 | #[derive(Debug)] |
@@ -45,20 +47,24 @@ pub enum RunnableKind { | |||
45 | pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { | 47 | pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { |
46 | let sema = Semantics::new(db); | 48 | let sema = Semantics::new(db); |
47 | let source_file = sema.parse(file_id); | 49 | let source_file = sema.parse(file_id); |
48 | source_file.syntax().descendants().filter_map(|i| runnable(&sema, i)).collect() | 50 | source_file.syntax().descendants().filter_map(|i| runnable(&sema, i, file_id)).collect() |
49 | } | 51 | } |
50 | 52 | ||
51 | fn runnable(sema: &Semantics<RootDatabase>, item: SyntaxNode) -> Option<Runnable> { | 53 | fn runnable(sema: &Semantics<RootDatabase>, item: SyntaxNode, file_id: FileId) -> Option<Runnable> { |
52 | match_ast! { | 54 | match_ast! { |
53 | match item { | 55 | match item { |
54 | ast::FnDef(it) => runnable_fn(sema, it), | 56 | ast::FnDef(it) => runnable_fn(sema, it, file_id), |
55 | ast::Module(it) => runnable_mod(sema, it), | 57 | ast::Module(it) => runnable_mod(sema, it, file_id), |
56 | _ => None, | 58 | _ => None, |
57 | } | 59 | } |
58 | } | 60 | } |
59 | } | 61 | } |
60 | 62 | ||
61 | fn runnable_fn(sema: &Semantics<RootDatabase>, fn_def: ast::FnDef) -> Option<Runnable> { | 63 | fn runnable_fn( |
64 | sema: &Semantics<RootDatabase>, | ||
65 | fn_def: ast::FnDef, | ||
66 | file_id: FileId, | ||
67 | ) -> Option<Runnable> { | ||
62 | let name_string = fn_def.name()?.text().to_string(); | 68 | let name_string = fn_def.name()?.text().to_string(); |
63 | 69 | ||
64 | let kind = if name_string == "main" { | 70 | let kind = if name_string == "main" { |
@@ -111,7 +117,12 @@ fn runnable_fn(sema: &Semantics<RootDatabase>, fn_def: ast::FnDef) -> Option<Run | |||
111 | return None; | 117 | return None; |
112 | } | 118 | } |
113 | }; | 119 | }; |
114 | Some(Runnable { range: fn_def.syntax().text_range(), kind }) | 120 | |
121 | let attrs = Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &fn_def)); | ||
122 | let cfg_exprs = | ||
123 | attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect(); | ||
124 | |||
125 | Some(Runnable { range: fn_def.syntax().text_range(), kind, cfg_exprs }) | ||
115 | } | 126 | } |
116 | 127 | ||
117 | #[derive(Debug)] | 128 | #[derive(Debug)] |
@@ -147,7 +158,11 @@ fn has_doc_test(fn_def: &ast::FnDef) -> bool { | |||
147 | fn_def.doc_comment_text().map_or(false, |comment| comment.contains("```")) | 158 | fn_def.doc_comment_text().map_or(false, |comment| comment.contains("```")) |
148 | } | 159 | } |
149 | 160 | ||
150 | fn runnable_mod(sema: &Semantics<RootDatabase>, module: ast::Module) -> Option<Runnable> { | 161 | fn runnable_mod( |
162 | sema: &Semantics<RootDatabase>, | ||
163 | module: ast::Module, | ||
164 | file_id: FileId, | ||
165 | ) -> Option<Runnable> { | ||
151 | let has_test_function = module | 166 | let has_test_function = module |
152 | .item_list()? | 167 | .item_list()? |
153 | .items() | 168 | .items() |
@@ -160,11 +175,20 @@ fn runnable_mod(sema: &Semantics<RootDatabase>, module: ast::Module) -> Option<R | |||
160 | return None; | 175 | return None; |
161 | } | 176 | } |
162 | let range = module.syntax().text_range(); | 177 | let range = module.syntax().text_range(); |
163 | let module = sema.to_def(&module)?; | 178 | let module_def = sema.to_def(&module)?; |
179 | |||
180 | let path = module_def | ||
181 | .path_to_root(sema.db) | ||
182 | .into_iter() | ||
183 | .rev() | ||
184 | .filter_map(|it| it.name(sema.db)) | ||
185 | .join("::"); | ||
186 | |||
187 | let attrs = Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &module)); | ||
188 | let cfg_exprs = | ||
189 | attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect(); | ||
164 | 190 | ||
165 | let path = | 191 | Some(Runnable { range, kind: RunnableKind::TestMod { path }, cfg_exprs }) |
166 | module.path_to_root(sema.db).into_iter().rev().filter_map(|it| it.name(sema.db)).join("::"); | ||
167 | Some(Runnable { range, kind: RunnableKind::TestMod { path } }) | ||
168 | } | 192 | } |
169 | 193 | ||
170 | #[cfg(test)] | 194 | #[cfg(test)] |
@@ -196,6 +220,7 @@ mod tests { | |||
196 | Runnable { | 220 | Runnable { |
197 | range: 1..21, | 221 | range: 1..21, |
198 | kind: Bin, | 222 | kind: Bin, |
223 | cfg_exprs: [], | ||
199 | }, | 224 | }, |
200 | Runnable { | 225 | Runnable { |
201 | range: 22..46, | 226 | range: 22..46, |
@@ -207,6 +232,7 @@ mod tests { | |||
207 | ignore: false, | 232 | ignore: false, |
208 | }, | 233 | }, |
209 | }, | 234 | }, |
235 | cfg_exprs: [], | ||
210 | }, | 236 | }, |
211 | Runnable { | 237 | Runnable { |
212 | range: 47..81, | 238 | range: 47..81, |
@@ -218,6 +244,7 @@ mod tests { | |||
218 | ignore: true, | 244 | ignore: true, |
219 | }, | 245 | }, |
220 | }, | 246 | }, |
247 | cfg_exprs: [], | ||
221 | }, | 248 | }, |
222 | ] | 249 | ] |
223 | "### | 250 | "### |
@@ -245,6 +272,7 @@ mod tests { | |||
245 | Runnable { | 272 | Runnable { |
246 | range: 1..21, | 273 | range: 1..21, |
247 | kind: Bin, | 274 | kind: Bin, |
275 | cfg_exprs: [], | ||
248 | }, | 276 | }, |
249 | Runnable { | 277 | Runnable { |
250 | range: 22..64, | 278 | range: 22..64, |
@@ -253,6 +281,7 @@ mod tests { | |||
253 | "foo", | 281 | "foo", |
254 | ), | 282 | ), |
255 | }, | 283 | }, |
284 | cfg_exprs: [], | ||
256 | }, | 285 | }, |
257 | ] | 286 | ] |
258 | "### | 287 | "### |
@@ -283,6 +312,7 @@ mod tests { | |||
283 | Runnable { | 312 | Runnable { |
284 | range: 1..21, | 313 | range: 1..21, |
285 | kind: Bin, | 314 | kind: Bin, |
315 | cfg_exprs: [], | ||
286 | }, | 316 | }, |
287 | Runnable { | 317 | Runnable { |
288 | range: 51..105, | 318 | range: 51..105, |
@@ -291,6 +321,7 @@ mod tests { | |||
291 | "Data::foo", | 321 | "Data::foo", |
292 | ), | 322 | ), |
293 | }, | 323 | }, |
324 | cfg_exprs: [], | ||
294 | }, | 325 | }, |
295 | ] | 326 | ] |
296 | "### | 327 | "### |
@@ -318,6 +349,7 @@ mod tests { | |||
318 | kind: TestMod { | 349 | kind: TestMod { |
319 | path: "test_mod", | 350 | path: "test_mod", |
320 | }, | 351 | }, |
352 | cfg_exprs: [], | ||
321 | }, | 353 | }, |
322 | Runnable { | 354 | Runnable { |
323 | range: 28..57, | 355 | range: 28..57, |
@@ -329,6 +361,7 @@ mod tests { | |||
329 | ignore: false, | 361 | ignore: false, |
330 | }, | 362 | }, |
331 | }, | 363 | }, |
364 | cfg_exprs: [], | ||
332 | }, | 365 | }, |
333 | ] | 366 | ] |
334 | "### | 367 | "### |
@@ -358,6 +391,7 @@ mod tests { | |||
358 | kind: TestMod { | 391 | kind: TestMod { |
359 | path: "foo::test_mod", | 392 | path: "foo::test_mod", |
360 | }, | 393 | }, |
394 | cfg_exprs: [], | ||
361 | }, | 395 | }, |
362 | Runnable { | 396 | Runnable { |
363 | range: 46..79, | 397 | range: 46..79, |
@@ -369,6 +403,7 @@ mod tests { | |||
369 | ignore: false, | 403 | ignore: false, |
370 | }, | 404 | }, |
371 | }, | 405 | }, |
406 | cfg_exprs: [], | ||
372 | }, | 407 | }, |
373 | ] | 408 | ] |
374 | "### | 409 | "### |
@@ -400,6 +435,7 @@ mod tests { | |||
400 | kind: TestMod { | 435 | kind: TestMod { |
401 | path: "foo::bar::test_mod", | 436 | path: "foo::bar::test_mod", |
402 | }, | 437 | }, |
438 | cfg_exprs: [], | ||
403 | }, | 439 | }, |
404 | Runnable { | 440 | Runnable { |
405 | range: 68..105, | 441 | range: 68..105, |
@@ -411,6 +447,89 @@ mod tests { | |||
411 | ignore: false, | 447 | ignore: false, |
412 | }, | 448 | }, |
413 | }, | 449 | }, |
450 | cfg_exprs: [], | ||
451 | }, | ||
452 | ] | ||
453 | "### | ||
454 | ); | ||
455 | } | ||
456 | |||
457 | #[test] | ||
458 | fn test_runnables_with_feature() { | ||
459 | let (analysis, pos) = analysis_and_position( | ||
460 | r#" | ||
461 | //- /lib.rs crate:foo cfg:feature=foo | ||
462 | <|> //empty | ||
463 | #[test] | ||
464 | #[cfg(feature = "foo")] | ||
465 | fn test_foo1() {} | ||
466 | "#, | ||
467 | ); | ||
468 | let runnables = analysis.runnables(pos.file_id).unwrap(); | ||
469 | assert_debug_snapshot!(&runnables, | ||
470 | @r###" | ||
471 | [ | ||
472 | Runnable { | ||
473 | range: 1..58, | ||
474 | kind: Test { | ||
475 | test_id: Name( | ||
476 | "test_foo1", | ||
477 | ), | ||
478 | attr: TestAttr { | ||
479 | ignore: false, | ||
480 | }, | ||
481 | }, | ||
482 | cfg_exprs: [ | ||
483 | KeyValue { | ||
484 | key: "feature", | ||
485 | value: "foo", | ||
486 | }, | ||
487 | ], | ||
488 | }, | ||
489 | ] | ||
490 | "### | ||
491 | ); | ||
492 | } | ||
493 | |||
494 | #[test] | ||
495 | fn test_runnables_with_features() { | ||
496 | let (analysis, pos) = analysis_and_position( | ||
497 | r#" | ||
498 | //- /lib.rs crate:foo cfg:feature=foo,feature=bar | ||
499 | <|> //empty | ||
500 | #[test] | ||
501 | #[cfg(all(feature = "foo", feature = "bar"))] | ||
502 | fn test_foo1() {} | ||
503 | "#, | ||
504 | ); | ||
505 | let runnables = analysis.runnables(pos.file_id).unwrap(); | ||
506 | assert_debug_snapshot!(&runnables, | ||
507 | @r###" | ||
508 | [ | ||
509 | Runnable { | ||
510 | range: 1..80, | ||
511 | kind: Test { | ||
512 | test_id: Name( | ||
513 | "test_foo1", | ||
514 | ), | ||
515 | attr: TestAttr { | ||
516 | ignore: false, | ||
517 | }, | ||
518 | }, | ||
519 | cfg_exprs: [ | ||
520 | All( | ||
521 | [ | ||
522 | KeyValue { | ||
523 | key: "feature", | ||
524 | value: "foo", | ||
525 | }, | ||
526 | KeyValue { | ||
527 | key: "feature", | ||
528 | value: "bar", | ||
529 | }, | ||
530 | ], | ||
531 | ), | ||
532 | ], | ||
414 | }, | 533 | }, |
415 | ] | 534 | ] |
416 | "### | 535 | "### |
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index 9b2d29b1d..65b487db3 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml | |||
@@ -40,6 +40,7 @@ ra_project_model = { path = "../ra_project_model" } | |||
40 | ra_syntax = { path = "../ra_syntax" } | 40 | ra_syntax = { path = "../ra_syntax" } |
41 | ra_text_edit = { path = "../ra_text_edit" } | 41 | ra_text_edit = { path = "../ra_text_edit" } |
42 | ra_vfs = "0.6.0" | 42 | ra_vfs = "0.6.0" |
43 | ra_cfg = { path = "../ra_cfg"} | ||
43 | 44 | ||
44 | # This should only be used in CLI | 45 | # This should only be used in CLI |
45 | ra_db = { path = "../ra_db" } | 46 | ra_db = { path = "../ra_db" } |
@@ -55,6 +56,8 @@ winapi = "0.3.8" | |||
55 | tempfile = "3.1.0" | 56 | tempfile = "3.1.0" |
56 | insta = "0.16.0" | 57 | insta = "0.16.0" |
57 | test_utils = { path = "../test_utils" } | 58 | test_utils = { path = "../test_utils" } |
59 | mbe = { path = "../ra_mbe", package = "ra_mbe" } | ||
60 | tt = { path = "../ra_tt", package = "ra_tt" } | ||
58 | 61 | ||
59 | [features] | 62 | [features] |
60 | jemalloc = [ "ra_prof/jemalloc" ] | 63 | jemalloc = [ "ra_prof/jemalloc" ] |
diff --git a/crates/rust-analyzer/src/cargo_target_spec.rs b/crates/rust-analyzer/src/cargo_target_spec.rs index 5e5a17943..441fb61df 100644 --- a/crates/rust-analyzer/src/cargo_target_spec.rs +++ b/crates/rust-analyzer/src/cargo_target_spec.rs | |||
@@ -4,6 +4,7 @@ use ra_ide::{FileId, RunnableKind, TestId}; | |||
4 | use ra_project_model::{self, ProjectWorkspace, TargetKind}; | 4 | use ra_project_model::{self, ProjectWorkspace, TargetKind}; |
5 | 5 | ||
6 | use crate::{world::WorldSnapshot, Result}; | 6 | use crate::{world::WorldSnapshot, Result}; |
7 | use ra_syntax::SmolStr; | ||
7 | 8 | ||
8 | /// Abstract representation of Cargo target. | 9 | /// Abstract representation of Cargo target. |
9 | /// | 10 | /// |
@@ -20,6 +21,7 @@ impl CargoTargetSpec { | |||
20 | pub(crate) fn runnable_args( | 21 | pub(crate) fn runnable_args( |
21 | spec: Option<CargoTargetSpec>, | 22 | spec: Option<CargoTargetSpec>, |
22 | kind: &RunnableKind, | 23 | kind: &RunnableKind, |
24 | features_needed: &Vec<SmolStr>, | ||
23 | ) -> Result<(Vec<String>, Vec<String>)> { | 25 | ) -> Result<(Vec<String>, Vec<String>)> { |
24 | let mut args = Vec::new(); | 26 | let mut args = Vec::new(); |
25 | let mut extra_args = Vec::new(); | 27 | let mut extra_args = Vec::new(); |
@@ -73,6 +75,12 @@ impl CargoTargetSpec { | |||
73 | } | 75 | } |
74 | } | 76 | } |
75 | } | 77 | } |
78 | |||
79 | features_needed.iter().for_each(|feature| { | ||
80 | args.push("--features".to_string()); | ||
81 | args.push(feature.to_string()); | ||
82 | }); | ||
83 | |||
76 | Ok((args, extra_args)) | 84 | Ok((args, extra_args)) |
77 | } | 85 | } |
78 | 86 | ||
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index ba6857556..2aaff3ea4 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs | |||
@@ -17,13 +17,14 @@ use lsp_types::{ | |||
17 | SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult, | 17 | SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult, |
18 | SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, Url, WorkspaceEdit, | 18 | SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, Url, WorkspaceEdit, |
19 | }; | 19 | }; |
20 | use ra_cfg::CfgExpr; | ||
20 | use ra_ide::{ | 21 | use ra_ide::{ |
21 | FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope, | 22 | FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope, |
22 | TextEdit, | 23 | TextEdit, |
23 | }; | 24 | }; |
24 | use ra_prof::profile; | 25 | use ra_prof::profile; |
25 | use ra_project_model::TargetKind; | 26 | use ra_project_model::TargetKind; |
26 | use ra_syntax::{AstNode, SyntaxKind, TextRange, TextSize}; | 27 | use ra_syntax::{AstNode, SmolStr, SyntaxKind, TextRange, TextSize}; |
27 | use rustc_hash::FxHashMap; | 28 | use rustc_hash::FxHashMap; |
28 | use serde::{Deserialize, Serialize}; | 29 | use serde::{Deserialize, Serialize}; |
29 | use serde_json::to_value; | 30 | use serde_json::to_value; |
@@ -978,7 +979,12 @@ fn to_lsp_runnable( | |||
978 | ) -> Result<lsp_ext::Runnable> { | 979 | ) -> Result<lsp_ext::Runnable> { |
979 | let spec = CargoTargetSpec::for_file(world, file_id)?; | 980 | let spec = CargoTargetSpec::for_file(world, file_id)?; |
980 | let target = spec.as_ref().map(|s| s.target.clone()); | 981 | let target = spec.as_ref().map(|s| s.target.clone()); |
981 | let (args, extra_args) = CargoTargetSpec::runnable_args(spec, &runnable.kind)?; | 982 | let mut features_needed = vec![]; |
983 | for cfg_expr in &runnable.cfg_exprs { | ||
984 | collect_minimal_features_needed(cfg_expr, &mut features_needed); | ||
985 | } | ||
986 | let (args, extra_args) = | ||
987 | CargoTargetSpec::runnable_args(spec, &runnable.kind, &features_needed)?; | ||
982 | let line_index = world.analysis().file_line_index(file_id)?; | 988 | let line_index = world.analysis().file_line_index(file_id)?; |
983 | let label = match &runnable.kind { | 989 | let label = match &runnable.kind { |
984 | RunnableKind::Test { test_id, .. } => format!("test {}", test_id), | 990 | RunnableKind::Test { test_id, .. } => format!("test {}", test_id), |
@@ -1004,6 +1010,26 @@ fn to_lsp_runnable( | |||
1004 | }) | 1010 | }) |
1005 | } | 1011 | } |
1006 | 1012 | ||
1013 | /// Fill minimal features needed | ||
1014 | fn collect_minimal_features_needed(cfg_expr: &CfgExpr, features: &mut Vec<SmolStr>) { | ||
1015 | match cfg_expr { | ||
1016 | CfgExpr::KeyValue { key, value } if key == "feature" => features.push(value.clone()), | ||
1017 | CfgExpr::All(preds) => { | ||
1018 | preds.iter().for_each(|cfg| collect_minimal_features_needed(cfg, features)); | ||
1019 | } | ||
1020 | CfgExpr::Any(preds) => { | ||
1021 | for cfg in preds { | ||
1022 | let len_features = features.len(); | ||
1023 | collect_minimal_features_needed(cfg, features); | ||
1024 | if len_features != features.len() { | ||
1025 | break; | ||
1026 | } | ||
1027 | } | ||
1028 | } | ||
1029 | _ => {} | ||
1030 | } | ||
1031 | } | ||
1032 | |||
1007 | pub fn handle_inlay_hints( | 1033 | pub fn handle_inlay_hints( |
1008 | world: WorldSnapshot, | 1034 | world: WorldSnapshot, |
1009 | params: InlayHintsParams, | 1035 | params: InlayHintsParams, |
@@ -1140,3 +1166,54 @@ pub fn handle_semantic_tokens_range( | |||
1140 | let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); | 1166 | let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); |
1141 | Ok(Some(semantic_tokens.into())) | 1167 | Ok(Some(semantic_tokens.into())) |
1142 | } | 1168 | } |
1169 | |||
1170 | #[cfg(test)] | ||
1171 | mod tests { | ||
1172 | use super::*; | ||
1173 | |||
1174 | use mbe::{ast_to_token_tree, TokenMap}; | ||
1175 | use ra_cfg::parse_cfg; | ||
1176 | use ra_syntax::{ | ||
1177 | ast::{self, AstNode}, | ||
1178 | SmolStr, | ||
1179 | }; | ||
1180 | |||
1181 | fn get_token_tree_generated(input: &str) -> (tt::Subtree, TokenMap) { | ||
1182 | let source_file = ast::SourceFile::parse(input).ok().unwrap(); | ||
1183 | let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); | ||
1184 | ast_to_token_tree(&tt).unwrap() | ||
1185 | } | ||
1186 | |||
1187 | #[test] | ||
1188 | fn test_cfg_expr_minimal_features_needed() { | ||
1189 | let (subtree, _) = get_token_tree_generated(r#"#![cfg(feature = "baz")]"#); | ||
1190 | let cfg_expr = parse_cfg(&subtree); | ||
1191 | let mut min_features = vec![]; | ||
1192 | collect_minimal_features_needed(&cfg_expr, &mut min_features); | ||
1193 | |||
1194 | assert_eq!(min_features, vec![SmolStr::new("baz")]); | ||
1195 | |||
1196 | let (subtree, _) = | ||
1197 | get_token_tree_generated(r#"#![cfg(all(feature = "baz", feature = "foo"))]"#); | ||
1198 | let cfg_expr = parse_cfg(&subtree); | ||
1199 | |||
1200 | let mut min_features = vec![]; | ||
1201 | collect_minimal_features_needed(&cfg_expr, &mut min_features); | ||
1202 | assert_eq!(min_features, vec![SmolStr::new("baz"), SmolStr::new("foo")]); | ||
1203 | |||
1204 | let (subtree, _) = | ||
1205 | get_token_tree_generated(r#"#![cfg(any(feature = "baz", feature = "foo", unix))]"#); | ||
1206 | let cfg_expr = parse_cfg(&subtree); | ||
1207 | |||
1208 | let mut min_features = vec![]; | ||
1209 | collect_minimal_features_needed(&cfg_expr, &mut min_features); | ||
1210 | assert_eq!(min_features, vec![SmolStr::new("baz")]); | ||
1211 | |||
1212 | let (subtree, _) = get_token_tree_generated(r#"#![cfg(foo)]"#); | ||
1213 | let cfg_expr = parse_cfg(&subtree); | ||
1214 | |||
1215 | let mut min_features = vec![]; | ||
1216 | collect_minimal_features_needed(&cfg_expr, &mut min_features); | ||
1217 | assert!(min_features.is_empty()); | ||
1218 | } | ||
1219 | } | ||