11//! FIXME: write short doc here
22
3- use hir:: { AsAssocItem , Semantics } ;
3+ use hir:: { AsAssocItem , Attrs , HirFileId , InFile , Semantics } ;
44use itertools:: Itertools ;
55use ra_ide_db:: RootDatabase ;
66use ra_syntax:: {
@@ -10,12 +10,14 @@ use ra_syntax::{
1010
1111use crate :: FileId ;
1212use ast:: DocCommentsOwner ;
13+ use ra_cfg:: CfgExpr ;
1314use std:: fmt:: Display ;
1415
1516#[ derive( Debug ) ]
1617pub struct Runnable {
1718 pub range : TextRange ,
1819 pub kind : RunnableKind ,
20+ pub cfg_exprs : Vec < CfgExpr > ,
1921}
2022
2123#[ derive( Debug ) ]
@@ -45,20 +47,24 @@ pub enum RunnableKind {
4547pub ( crate ) fn runnables ( db : & RootDatabase , file_id : FileId ) -> Vec < Runnable > {
4648 let sema = Semantics :: new ( db) ;
4749 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 ( )
4951}
5052
51- fn runnable ( sema : & Semantics < RootDatabase > , item : SyntaxNode ) -> Option < Runnable > {
53+ fn runnable ( sema : & Semantics < RootDatabase > , item : SyntaxNode , file_id : FileId ) -> Option < Runnable > {
5254 match_ast ! {
5355 match item {
54- ast:: FnDef ( it) => runnable_fn( sema, it) ,
55- ast:: Module ( it) => runnable_mod( sema, it) ,
56+ ast:: FnDef ( it) => runnable_fn( sema, it, file_id ) ,
57+ ast:: Module ( it) => runnable_mod( sema, it, file_id ) ,
5658 _ => None ,
5759 }
5860 }
5961}
6062
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 > {
6268 let name_string = fn_def. name ( ) ?. text ( ) . to_string ( ) ;
6369
6470 let kind = if name_string == "main" {
@@ -111,7 +117,12 @@ fn runnable_fn(sema: &Semantics<RootDatabase>, fn_def: ast::FnDef) -> Option<Run
111117 return None ;
112118 }
113119 } ;
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 } )
115126}
116127
117128#[ derive( Debug ) ]
@@ -147,7 +158,11 @@ fn has_doc_test(fn_def: &ast::FnDef) -> bool {
147158 fn_def. doc_comment_text ( ) . map_or ( false , |comment| comment. contains ( "```" ) )
148159}
149160
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 > {
151166 let has_test_function = module
152167 . item_list ( ) ?
153168 . items ( )
@@ -160,11 +175,20 @@ fn runnable_mod(sema: &Semantics<RootDatabase>, module: ast::Module) -> Option<R
160175 return None ;
161176 }
162177 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 ( ) ;
164190
165- let path =
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 } } )
191+ Some ( Runnable { range, kind : RunnableKind :: TestMod { path } , cfg_exprs } )
168192}
169193
170194#[ cfg( test) ]
@@ -196,6 +220,7 @@ mod tests {
196220 Runnable {
197221 range: 1..21,
198222 kind: Bin,
223+ cfg_exprs: [],
199224 },
200225 Runnable {
201226 range: 22..46,
@@ -207,6 +232,7 @@ mod tests {
207232 ignore: false,
208233 },
209234 },
235+ cfg_exprs: [],
210236 },
211237 Runnable {
212238 range: 47..81,
@@ -218,6 +244,7 @@ mod tests {
218244 ignore: true,
219245 },
220246 },
247+ cfg_exprs: [],
221248 },
222249 ]
223250 "###
@@ -245,6 +272,7 @@ mod tests {
245272 Runnable {
246273 range: 1..21,
247274 kind: Bin,
275+ cfg_exprs: [],
248276 },
249277 Runnable {
250278 range: 22..64,
@@ -253,6 +281,7 @@ mod tests {
253281 "foo",
254282 ),
255283 },
284+ cfg_exprs: [],
256285 },
257286 ]
258287 "###
@@ -283,6 +312,7 @@ mod tests {
283312 Runnable {
284313 range: 1..21,
285314 kind: Bin,
315+ cfg_exprs: [],
286316 },
287317 Runnable {
288318 range: 51..105,
@@ -291,6 +321,7 @@ mod tests {
291321 "Data::foo",
292322 ),
293323 },
324+ cfg_exprs: [],
294325 },
295326 ]
296327 "###
@@ -318,6 +349,7 @@ mod tests {
318349 kind: TestMod {
319350 path: "test_mod",
320351 },
352+ cfg_exprs: [],
321353 },
322354 Runnable {
323355 range: 28..57,
@@ -329,6 +361,7 @@ mod tests {
329361 ignore: false,
330362 },
331363 },
364+ cfg_exprs: [],
332365 },
333366 ]
334367 "###
@@ -358,6 +391,7 @@ mod tests {
358391 kind: TestMod {
359392 path: "foo::test_mod",
360393 },
394+ cfg_exprs: [],
361395 },
362396 Runnable {
363397 range: 46..79,
@@ -369,6 +403,7 @@ mod tests {
369403 ignore: false,
370404 },
371405 },
406+ cfg_exprs: [],
372407 },
373408 ]
374409 "###
@@ -400,6 +435,7 @@ mod tests {
400435 kind: TestMod {
401436 path: "foo::bar::test_mod",
402437 },
438+ cfg_exprs: [],
403439 },
404440 Runnable {
405441 range: 68..105,
@@ -411,6 +447,89 @@ mod tests {
411447 ignore: false,
412448 },
413449 },
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+ ],
414533 },
415534 ]
416535 "###
0 commit comments