rust-analyzer/crates/ra_ide/src/runnables.rs

243 lines
5.9 KiB
Rust
Raw Normal View History

//! FIXME: write short doc here
2019-11-28 09:50:26 +00:00
use hir::InFile;
2019-01-08 19:33:36 +00:00
use itertools::Itertools;
use ra_db::SourceDatabase;
2019-01-08 19:33:36 +00:00
use ra_syntax::{
ast::{self, AstNode, AttrsOwner, ModuleItemOwner, NameOwner},
2019-10-30 20:09:16 +00:00
match_ast, SyntaxNode, TextRange,
2019-01-08 19:33:36 +00:00
};
use crate::{db::RootDatabase, FileId};
#[derive(Debug)]
pub struct Runnable {
pub range: TextRange,
pub kind: RunnableKind,
}
#[derive(Debug)]
pub enum RunnableKind {
Test { name: String },
TestMod { path: String },
2019-01-12 23:40:54 +00:00
Bench { name: String },
2019-01-08 19:33:36 +00:00
Bin,
}
2019-01-15 18:02:42 +00:00
pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
let parse = db.parse(file_id);
parse.tree().syntax().descendants().filter_map(|i| runnable(db, file_id, i)).collect()
2019-01-08 19:33:36 +00:00
}
2019-07-19 09:56:47 +00:00
fn runnable(db: &RootDatabase, file_id: FileId, item: SyntaxNode) -> Option<Runnable> {
2019-10-30 20:09:16 +00:00
match_ast! {
match item {
ast::FnDef(it) => { runnable_fn(it) },
ast::Module(it) => { runnable_mod(db, file_id, it) },
_ => { None },
}
2019-01-08 19:33:36 +00:00
}
}
2019-07-19 09:56:47 +00:00
fn runnable_fn(fn_def: ast::FnDef) -> Option<Runnable> {
let name = fn_def.name()?.text().clone();
2019-01-08 19:33:36 +00:00
let kind = if name == "main" {
RunnableKind::Bin
} else if fn_def.has_atom_attr("test") {
2019-02-08 11:49:43 +00:00
RunnableKind::Test { name: name.to_string() }
2019-01-12 23:40:54 +00:00
} else if fn_def.has_atom_attr("bench") {
2019-02-08 11:49:43 +00:00
RunnableKind::Bench { name: name.to_string() }
2019-01-08 19:33:36 +00:00
} else {
return None;
};
2019-07-20 09:58:27 +00:00
Some(Runnable { range: fn_def.syntax().text_range(), kind })
2019-01-08 19:33:36 +00:00
}
2019-07-19 09:56:47 +00:00
fn runnable_mod(db: &RootDatabase, file_id: FileId, module: ast::Module) -> Option<Runnable> {
2019-01-08 19:33:36 +00:00
let has_test_function = module
.item_list()?
.items()
2019-08-19 11:13:58 +00:00
.filter_map(|it| match it {
ast::ModuleItem::FnDef(it) => Some(it),
2019-01-08 19:33:36 +00:00
_ => None,
})
.any(|f| f.has_atom_attr("test"));
if !has_test_function {
return None;
}
2019-07-20 09:58:27 +00:00
let range = module.syntax().text_range();
2020-01-16 16:33:07 +00:00
let mut sb = hir::SourceBinder::new(db);
let module = sb.to_def(InFile::new(file_id.into(), module))?;
2019-01-08 19:33:36 +00:00
2019-02-08 11:49:43 +00:00
let path = module.path_to_root(db).into_iter().rev().filter_map(|it| it.name(db)).join("::");
Some(Runnable { range, kind: RunnableKind::TestMod { path } })
2019-01-08 19:33:36 +00:00
}
2019-01-14 13:27:08 +00:00
#[cfg(test)]
mod tests {
2019-08-29 13:49:10 +00:00
use insta::assert_debug_snapshot;
2019-01-14 13:27:08 +00:00
use crate::mock_analysis::analysis_and_position;
#[test]
fn test_runnables() {
let (analysis, pos) = analysis_and_position(
r#"
//- /lib.rs
<|> //empty
fn main() {}
#[test]
fn test_foo() {}
#[test]
#[ignore]
fn test_foo() {}
"#,
);
let runnables = analysis.runnables(pos.file_id).unwrap();
2019-08-29 13:49:10 +00:00
assert_debug_snapshot!(&runnables,
2019-11-15 09:56:24 +00:00
@r###"
[
Runnable {
range: [1; 21),
kind: Bin,
},
Runnable {
range: [22; 46),
kind: Test {
name: "test_foo",
},
},
Runnable {
range: [47; 81),
kind: Test {
name: "test_foo",
},
},
]
"###
2019-07-28 11:08:06 +00:00
);
2019-01-14 13:27:08 +00:00
}
#[test]
fn test_runnables_module() {
let (analysis, pos) = analysis_and_position(
r#"
//- /lib.rs
<|> //empty
mod test_mod {
#[test]
fn test_foo1() {}
}
"#,
);
let runnables = analysis.runnables(pos.file_id).unwrap();
2019-08-29 13:49:10 +00:00
assert_debug_snapshot!(&runnables,
2019-11-15 09:56:24 +00:00
@r###"
[
Runnable {
range: [1; 59),
kind: TestMod {
path: "test_mod",
},
},
Runnable {
range: [28; 57),
kind: Test {
name: "test_foo1",
},
},
]
"###
2019-07-28 11:08:06 +00:00
);
2019-01-14 13:27:08 +00:00
}
#[test]
fn test_runnables_one_depth_layer_module() {
let (analysis, pos) = analysis_and_position(
r#"
//- /lib.rs
<|> //empty
mod foo {
mod test_mod {
#[test]
fn test_foo1() {}
}
}
"#,
);
let runnables = analysis.runnables(pos.file_id).unwrap();
2019-08-29 13:49:10 +00:00
assert_debug_snapshot!(&runnables,
2019-11-15 09:56:24 +00:00
@r###"
[
Runnable {
range: [23; 85),
kind: TestMod {
path: "foo::test_mod",
},
},
Runnable {
range: [46; 79),
kind: Test {
name: "test_foo1",
},
},
]
"###
2019-07-28 11:08:06 +00:00
);
2019-01-14 13:27:08 +00:00
}
#[test]
fn test_runnables_multiple_depth_module() {
let (analysis, pos) = analysis_and_position(
r#"
//- /lib.rs
<|> //empty
mod foo {
mod bar {
mod test_mod {
#[test]
fn test_foo1() {}
}
}
}
"#,
);
let runnables = analysis.runnables(pos.file_id).unwrap();
2019-08-29 13:49:10 +00:00
assert_debug_snapshot!(&runnables,
2019-11-15 09:56:24 +00:00
@r###"
[
Runnable {
range: [41; 115),
kind: TestMod {
path: "foo::bar::test_mod",
},
},
Runnable {
range: [68; 105),
kind: Test {
name: "test_foo1",
},
},
]
"###
2019-07-28 11:08:06 +00:00
);
2019-01-14 13:27:08 +00:00
}
#[test]
fn test_runnables_no_test_function_in_module() {
let (analysis, pos) = analysis_and_position(
r#"
//- /lib.rs
<|> //empty
mod test_mod {
fn foo1() {}
}
"#,
);
let runnables = analysis.runnables(pos.file_id).unwrap();
assert!(runnables.is_empty())
}
}