2021-02-01 06:20:35 -06:00
|
|
|
mod block;
|
|
|
|
|
|
|
|
use base_db::{fixture::WithFixture, FilePosition, SourceDatabase};
|
|
|
|
use expect_test::Expect;
|
2020-10-23 12:27:04 -05:00
|
|
|
use test_utils::mark;
|
|
|
|
|
2021-02-01 06:20:35 -06:00
|
|
|
use crate::{test_db::TestDB, BlockId, ModuleDefId};
|
2020-10-23 12:27:04 -05:00
|
|
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
fn lower(ra_fixture: &str) -> Arc<Body> {
|
2021-01-21 13:35:36 -06:00
|
|
|
let db = crate::test_db::TestDB::with_files(ra_fixture);
|
2020-10-23 12:27:04 -05:00
|
|
|
|
|
|
|
let krate = db.crate_graph().iter().next().unwrap();
|
|
|
|
let def_map = db.crate_def_map(krate);
|
2021-01-21 13:35:36 -06:00
|
|
|
let mut fn_def = None;
|
|
|
|
'outer: for (_, module) in def_map.modules() {
|
|
|
|
for decl in module.scope.declarations() {
|
|
|
|
match decl {
|
|
|
|
ModuleDefId::FunctionId(it) => {
|
|
|
|
fn_def = Some(it);
|
|
|
|
break 'outer;
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
db.body(fn_def.unwrap().into())
|
2020-10-23 12:27:04 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
fn check_diagnostics(ra_fixture: &str) {
|
|
|
|
let db: TestDB = TestDB::with_files(ra_fixture);
|
|
|
|
db.check_diagnostics();
|
|
|
|
}
|
|
|
|
|
2021-02-01 06:20:35 -06:00
|
|
|
fn block_def_map_at(ra_fixture: &str) -> Arc<DefMap> {
|
|
|
|
let (db, position) = crate::test_db::TestDB::with_position(ra_fixture);
|
|
|
|
|
|
|
|
let krate = db.crate_graph().iter().next().unwrap();
|
|
|
|
let def_map = db.crate_def_map(krate);
|
|
|
|
|
|
|
|
let mut block =
|
|
|
|
block_at_pos(&db, &def_map, position).expect("couldn't find enclosing function or block");
|
|
|
|
loop {
|
2021-02-01 06:32:43 -06:00
|
|
|
let def_map = db.block_def_map(block).unwrap_or_else(|| def_map.clone());
|
2021-02-01 06:20:35 -06:00
|
|
|
let new_block = block_at_pos(&db, &def_map, position);
|
|
|
|
match new_block {
|
|
|
|
Some(new_block) => {
|
|
|
|
assert_ne!(block, new_block);
|
|
|
|
block = new_block;
|
|
|
|
}
|
|
|
|
None => {
|
|
|
|
return def_map;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn block_at_pos(db: &dyn DefDatabase, def_map: &DefMap, position: FilePosition) -> Option<BlockId> {
|
2021-02-01 06:32:43 -06:00
|
|
|
// Find the smallest (innermost) function containing the cursor.
|
2021-02-01 06:20:35 -06:00
|
|
|
let mut size = None;
|
|
|
|
let mut fn_def = None;
|
|
|
|
for (_, module) in def_map.modules() {
|
|
|
|
let file_id = module.definition_source(db).file_id;
|
|
|
|
if file_id != position.file_id.into() {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
let root = db.parse_or_expand(file_id).unwrap();
|
|
|
|
let ast_map = db.ast_id_map(file_id);
|
|
|
|
let item_tree = db.item_tree(file_id);
|
|
|
|
for decl in module.scope.declarations() {
|
|
|
|
if let ModuleDefId::FunctionId(it) = decl {
|
|
|
|
let ast = ast_map.get(item_tree[it.lookup(db).id.value].ast_id).to_node(&root);
|
|
|
|
let range = ast.syntax().text_range();
|
|
|
|
|
|
|
|
if !range.contains(position.offset) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
let new_size = match size {
|
|
|
|
None => range.len(),
|
|
|
|
Some(size) => {
|
|
|
|
if range.len() < size {
|
|
|
|
range.len()
|
|
|
|
} else {
|
|
|
|
size
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
if size != Some(new_size) {
|
|
|
|
size = Some(new_size);
|
|
|
|
fn_def = Some(it);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let (body, source_map) = db.body_with_source_map(fn_def?.into());
|
|
|
|
|
|
|
|
// Now find the smallest encompassing block expression in the function body.
|
|
|
|
let mut size = None;
|
|
|
|
let mut block_id = None;
|
|
|
|
for (expr_id, expr) in body.exprs.iter() {
|
|
|
|
if let Expr::Block { id, .. } = expr {
|
|
|
|
if let Ok(ast) = source_map.expr_syntax(expr_id) {
|
|
|
|
if ast.file_id != position.file_id.into() {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
let root = db.parse_or_expand(ast.file_id).unwrap();
|
|
|
|
let ast = ast.value.to_node(&root);
|
|
|
|
let range = ast.syntax().text_range();
|
|
|
|
|
|
|
|
if !range.contains(position.offset) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
let new_size = match size {
|
|
|
|
None => range.len(),
|
|
|
|
Some(size) => {
|
|
|
|
if range.len() < size {
|
|
|
|
range.len()
|
|
|
|
} else {
|
|
|
|
size
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
if size != Some(new_size) {
|
|
|
|
size = Some(new_size);
|
|
|
|
block_id = Some(*id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Some(block_id.expect("can't find block containing cursor"))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn check_at(ra_fixture: &str, expect: Expect) {
|
|
|
|
let def_map = block_def_map_at(ra_fixture);
|
|
|
|
let actual = def_map.dump();
|
|
|
|
expect.assert_eq(&actual);
|
|
|
|
}
|
|
|
|
|
2020-10-23 12:27:04 -05:00
|
|
|
#[test]
|
|
|
|
fn your_stack_belongs_to_me() {
|
|
|
|
mark::check!(your_stack_belongs_to_me);
|
|
|
|
lower(
|
|
|
|
"
|
|
|
|
macro_rules! n_nuple {
|
|
|
|
($e:tt) => ();
|
|
|
|
($($rest:tt)*) => {{
|
|
|
|
(n_nuple!($($rest)*)None,)
|
|
|
|
}};
|
|
|
|
}
|
|
|
|
fn main() { n_nuple!(1,2,3); }
|
|
|
|
",
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-01-21 13:35:36 -06:00
|
|
|
#[test]
|
|
|
|
fn macro_resolve() {
|
|
|
|
// Regression test for a path resolution bug introduced with inner item handling.
|
|
|
|
lower(
|
|
|
|
r"
|
|
|
|
macro_rules! vec {
|
|
|
|
() => { () };
|
|
|
|
($elem:expr; $n:expr) => { () };
|
|
|
|
($($x:expr),+ $(,)?) => { () };
|
|
|
|
}
|
|
|
|
mod m {
|
|
|
|
fn outer() {
|
|
|
|
let _ = vec![FileSet::default(); self.len()];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
",
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-10-23 12:27:04 -05:00
|
|
|
#[test]
|
|
|
|
fn cfg_diagnostics() {
|
|
|
|
check_diagnostics(
|
|
|
|
r"
|
|
|
|
fn f() {
|
|
|
|
// The three g̶e̶n̶d̶e̶r̶s̶ statements:
|
|
|
|
|
|
|
|
#[cfg(a)] fn f() {} // Item statement
|
|
|
|
//^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled
|
|
|
|
#[cfg(a)] {} // Expression statement
|
|
|
|
//^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled
|
|
|
|
#[cfg(a)] let x = 0; // let statement
|
|
|
|
//^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled
|
|
|
|
|
|
|
|
abc(#[cfg(a)] 0);
|
|
|
|
//^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled
|
|
|
|
let x = Struct {
|
|
|
|
#[cfg(a)] f: 0,
|
|
|
|
//^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled
|
|
|
|
};
|
|
|
|
match () {
|
|
|
|
() => (),
|
|
|
|
#[cfg(a)] () => (),
|
|
|
|
//^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(a)] 0 // Trailing expression of block
|
|
|
|
//^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled
|
|
|
|
}
|
|
|
|
",
|
|
|
|
);
|
|
|
|
}
|
2020-12-02 07:54:25 -06:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn macro_diag_builtin() {
|
|
|
|
check_diagnostics(
|
|
|
|
r#"
|
2020-12-02 10:00:48 -06:00
|
|
|
#[rustc_builtin_macro]
|
|
|
|
macro_rules! env {}
|
|
|
|
|
|
|
|
#[rustc_builtin_macro]
|
|
|
|
macro_rules! include {}
|
|
|
|
|
2020-12-03 08:31:04 -06:00
|
|
|
#[rustc_builtin_macro]
|
|
|
|
macro_rules! compile_error {}
|
|
|
|
|
2020-12-02 10:00:48 -06:00
|
|
|
#[rustc_builtin_macro]
|
|
|
|
macro_rules! format_args {
|
|
|
|
() => {}
|
|
|
|
}
|
|
|
|
|
2020-12-02 07:54:25 -06:00
|
|
|
fn f() {
|
|
|
|
// Test a handful of built-in (eager) macros:
|
|
|
|
|
|
|
|
include!(invalid);
|
2020-12-02 10:00:48 -06:00
|
|
|
//^^^^^^^^^^^^^^^^^ could not convert tokens
|
2020-12-02 07:54:25 -06:00
|
|
|
include!("does not exist");
|
2020-12-02 10:00:48 -06:00
|
|
|
//^^^^^^^^^^^^^^^^^^^^^^^^^^ could not convert tokens
|
2020-12-02 07:54:25 -06:00
|
|
|
|
|
|
|
env!(invalid);
|
2020-12-02 10:00:48 -06:00
|
|
|
//^^^^^^^^^^^^^ could not convert tokens
|
2020-12-02 07:54:25 -06:00
|
|
|
|
2020-12-02 10:03:18 -06:00
|
|
|
env!("OUT_DIR");
|
|
|
|
//^^^^^^^^^^^^^^^ `OUT_DIR` not set, enable "load out dirs from check" to fix
|
|
|
|
|
2020-12-03 08:31:04 -06:00
|
|
|
compile_error!("compile_error works");
|
2020-12-03 12:07:37 -06:00
|
|
|
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ compile_error works
|
2020-12-03 08:31:04 -06:00
|
|
|
|
2020-12-02 07:54:25 -06:00
|
|
|
// Lazy:
|
|
|
|
|
|
|
|
format_args!();
|
2020-12-02 10:00:48 -06:00
|
|
|
//^^^^^^^^^^^^^^ no rule matches input tokens
|
2020-12-02 07:54:25 -06:00
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn macro_rules_diag() {
|
|
|
|
check_diagnostics(
|
|
|
|
r#"
|
|
|
|
macro_rules! m {
|
|
|
|
() => {};
|
|
|
|
}
|
|
|
|
fn f() {
|
|
|
|
m!();
|
|
|
|
|
|
|
|
m!(hi);
|
|
|
|
//^^^^^^ leftover tokens
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
2020-12-08 10:17:30 -06:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn dollar_crate_in_builtin_macro() {
|
|
|
|
check_diagnostics(
|
|
|
|
r#"
|
|
|
|
#[macro_export]
|
|
|
|
#[rustc_builtin_macro]
|
|
|
|
macro_rules! format_args {}
|
|
|
|
|
|
|
|
#[macro_export]
|
|
|
|
macro_rules! arg {
|
|
|
|
() => {}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[macro_export]
|
|
|
|
macro_rules! outer {
|
|
|
|
() => {
|
|
|
|
$crate::format_args!( "", $crate::arg!(1) )
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
fn f() {
|
|
|
|
outer!();
|
|
|
|
//^^^^^^^^ leftover tokens
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
)
|
|
|
|
}
|