diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index 2099ae48bc..d5f754d665 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -1,6 +1,8 @@ //! See `CompletionContext` structure. mod analysis; +#[cfg(test)] +mod tests; use base_db::SourceDatabaseExt; use hir::{ diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index 8549e955f5..dc50d82f19 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/crates/ide-completion/src/context/analysis.rs @@ -1,3 +1,4 @@ +//! Module responsible for analyzing the code surrounding the cursor for completion. use std::iter; use hir::{Semantics, Type, TypeInfo}; @@ -1033,403 +1034,6 @@ fn has_ref(token: &SyntaxToken) -> bool { token.kind() == T![&] } -#[cfg(test)] -mod tests { - use expect_test::{expect, Expect}; - use hir::HirDisplay; - - use crate::tests::{position, TEST_CONFIG}; - - use super::*; - - fn check_expected_type_and_name(ra_fixture: &str, expect: Expect) { - let (db, pos) = position(ra_fixture); - let config = TEST_CONFIG; - let completion_context = CompletionContext::new(&db, pos, &config).unwrap(); - - let ty = completion_context - .expected_type - .map(|t| t.display_test(&db).to_string()) - .unwrap_or("?".to_owned()); - - let name = completion_context - .expected_name - .map_or_else(|| "?".to_owned(), |name| name.to_string()); - - expect.assert_eq(&format!("ty: {}, name: {}", ty, name)); - } - - #[test] - fn expected_type_let_without_leading_char() { - cov_mark::check!(expected_type_let_without_leading_char); - check_expected_type_and_name( - r#" -fn foo() { - let x: u32 = $0; -} -"#, - expect![[r#"ty: u32, name: x"#]], - ); - } - - #[test] - fn expected_type_let_with_leading_char() { - cov_mark::check!(expected_type_let_with_leading_char); - check_expected_type_and_name( - r#" -fn foo() { - let x: u32 = c$0; -} -"#, - expect![[r#"ty: u32, name: x"#]], - ); - } - - #[test] - fn expected_type_let_pat() { - check_expected_type_and_name( - r#" -fn foo() { - let x$0 = 0u32; -} -"#, - expect![[r#"ty: u32, name: ?"#]], - ); - check_expected_type_and_name( - r#" -fn foo() { - let $0 = 0u32; -} -"#, - expect![[r#"ty: u32, name: ?"#]], - ); - } - - #[test] - fn expected_type_fn_param() { - cov_mark::check!(expected_type_fn_param); - check_expected_type_and_name( - r#" -fn foo() { bar($0); } -fn bar(x: u32) {} -"#, - expect![[r#"ty: u32, name: x"#]], - ); - check_expected_type_and_name( - r#" -fn foo() { bar(c$0); } -fn bar(x: u32) {} -"#, - expect![[r#"ty: u32, name: x"#]], - ); - } - - #[test] - fn expected_type_fn_param_ref() { - cov_mark::check!(expected_type_fn_param_ref); - check_expected_type_and_name( - r#" -fn foo() { bar(&$0); } -fn bar(x: &u32) {} -"#, - expect![[r#"ty: u32, name: x"#]], - ); - check_expected_type_and_name( - r#" -fn foo() { bar(&mut $0); } -fn bar(x: &mut u32) {} -"#, - expect![[r#"ty: u32, name: x"#]], - ); - check_expected_type_and_name( - r#" -fn foo() { bar(& c$0); } -fn bar(x: &u32) {} - "#, - expect![[r#"ty: u32, name: x"#]], - ); - check_expected_type_and_name( - r#" -fn foo() { bar(&mut c$0); } -fn bar(x: &mut u32) {} -"#, - expect![[r#"ty: u32, name: x"#]], - ); - check_expected_type_and_name( - r#" -fn foo() { bar(&c$0); } -fn bar(x: &u32) {} - "#, - expect![[r#"ty: u32, name: x"#]], - ); - } - - #[test] - fn expected_type_struct_field_without_leading_char() { - cov_mark::check!(expected_type_struct_field_without_leading_char); - check_expected_type_and_name( - r#" -struct Foo { a: u32 } -fn foo() { - Foo { a: $0 }; -} -"#, - expect![[r#"ty: u32, name: a"#]], - ) - } - - #[test] - fn expected_type_struct_field_followed_by_comma() { - cov_mark::check!(expected_type_struct_field_followed_by_comma); - check_expected_type_and_name( - r#" -struct Foo { a: u32 } -fn foo() { - Foo { a: $0, }; -} -"#, - expect![[r#"ty: u32, name: a"#]], - ) - } - - #[test] - fn expected_type_generic_struct_field() { - check_expected_type_and_name( - r#" -struct Foo { a: T } -fn foo() -> Foo { - Foo { a: $0 } -} -"#, - expect![[r#"ty: u32, name: a"#]], - ) - } - - #[test] - fn expected_type_struct_field_with_leading_char() { - cov_mark::check!(expected_type_struct_field_with_leading_char); - check_expected_type_and_name( - r#" -struct Foo { a: u32 } -fn foo() { - Foo { a: c$0 }; -} -"#, - expect![[r#"ty: u32, name: a"#]], - ); - } - - #[test] - fn expected_type_match_arm_without_leading_char() { - cov_mark::check!(expected_type_match_arm_without_leading_char); - check_expected_type_and_name( - r#" -enum E { X } -fn foo() { - match E::X { $0 } -} -"#, - expect![[r#"ty: E, name: ?"#]], - ); - } - - #[test] - fn expected_type_match_arm_with_leading_char() { - cov_mark::check!(expected_type_match_arm_with_leading_char); - check_expected_type_and_name( - r#" -enum E { X } -fn foo() { - match E::X { c$0 } -} -"#, - expect![[r#"ty: E, name: ?"#]], - ); - } - - #[test] - fn expected_type_match_arm_body_without_leading_char() { - cov_mark::check!(expected_type_match_arm_body_without_leading_char); - check_expected_type_and_name( - r#" -struct Foo; -enum E { X } -fn foo() -> Foo { - match E::X { E::X => $0 } -} -"#, - expect![[r#"ty: Foo, name: ?"#]], - ); - } - - #[test] - fn expected_type_match_body_arm_with_leading_char() { - cov_mark::check!(expected_type_match_arm_body_with_leading_char); - check_expected_type_and_name( - r#" -struct Foo; -enum E { X } -fn foo() -> Foo { - match E::X { E::X => c$0 } -} -"#, - expect![[r#"ty: Foo, name: ?"#]], - ); - } - - #[test] - fn expected_type_if_let_without_leading_char() { - cov_mark::check!(expected_type_if_let_without_leading_char); - check_expected_type_and_name( - r#" -enum Foo { Bar, Baz, Quux } - -fn foo() { - let f = Foo::Quux; - if let $0 = f { } -} -"#, - expect![[r#"ty: Foo, name: ?"#]], - ) - } - - #[test] - fn expected_type_if_let_with_leading_char() { - cov_mark::check!(expected_type_if_let_with_leading_char); - check_expected_type_and_name( - r#" -enum Foo { Bar, Baz, Quux } - -fn foo() { - let f = Foo::Quux; - if let c$0 = f { } -} -"#, - expect![[r#"ty: Foo, name: ?"#]], - ) - } - - #[test] - fn expected_type_fn_ret_without_leading_char() { - cov_mark::check!(expected_type_fn_ret_without_leading_char); - check_expected_type_and_name( - r#" -fn foo() -> u32 { - $0 -} -"#, - expect![[r#"ty: u32, name: ?"#]], - ) - } - - #[test] - fn expected_type_fn_ret_with_leading_char() { - cov_mark::check!(expected_type_fn_ret_with_leading_char); - check_expected_type_and_name( - r#" -fn foo() -> u32 { - c$0 -} -"#, - expect![[r#"ty: u32, name: ?"#]], - ) - } - - #[test] - fn expected_type_fn_ret_fn_ref_fully_typed() { - check_expected_type_and_name( - r#" -fn foo() -> u32 { - foo$0 -} -"#, - expect![[r#"ty: u32, name: ?"#]], - ) - } - - #[test] - fn expected_type_closure_param_return() { - // FIXME: make this work with `|| $0` - check_expected_type_and_name( - r#" -//- minicore: fn -fn foo() { - bar(|| a$0); -} - -fn bar(f: impl FnOnce() -> u32) {} -"#, - expect![[r#"ty: u32, name: ?"#]], - ); - } - - #[test] - fn expected_type_generic_function() { - check_expected_type_and_name( - r#" -fn foo() { - bar::($0); -} - -fn bar(t: T) {} -"#, - expect![[r#"ty: u32, name: t"#]], - ); - } - - #[test] - fn expected_type_generic_method() { - check_expected_type_and_name( - r#" -fn foo() { - S(1u32).bar($0); -} - -struct S(T); -impl S { - fn bar(self, t: T) {} -} -"#, - expect![[r#"ty: u32, name: t"#]], - ); - } - - #[test] - fn expected_type_functional_update() { - cov_mark::check!(expected_type_struct_func_update); - check_expected_type_and_name( - r#" -struct Foo { field: u32 } -fn foo() { - Foo { - ..$0 - } -} -"#, - expect![[r#"ty: Foo, name: ?"#]], - ); - } - - #[test] - fn expected_type_param_pat() { - check_expected_type_and_name( - r#" -struct Foo { field: u32 } -fn foo(a$0: Foo) {} -"#, - expect![[r#"ty: Foo, name: ?"#]], - ); - check_expected_type_and_name( - r#" -struct Foo { field: u32 } -fn foo($0: Foo) {} -"#, - // FIXME make this work, currently fails due to pattern recovery eating the `:` - expect![[r#"ty: ?, name: ?"#]], - ); - } -} - pub(crate) fn previous_token(element: SyntaxElement) -> Option { element.into_token().and_then(previous_non_trivia_token) } diff --git a/crates/ide-completion/src/context/tests.rs b/crates/ide-completion/src/context/tests.rs new file mode 100644 index 0000000000..ce9357270b --- /dev/null +++ b/crates/ide-completion/src/context/tests.rs @@ -0,0 +1,393 @@ +use expect_test::{expect, Expect}; +use hir::HirDisplay; + +use crate::{ + context::CompletionContext, + tests::{position, TEST_CONFIG}, +}; + +fn check_expected_type_and_name(ra_fixture: &str, expect: Expect) { + let (db, pos) = position(ra_fixture); + let config = TEST_CONFIG; + let completion_context = CompletionContext::new(&db, pos, &config).unwrap(); + + let ty = completion_context + .expected_type + .map(|t| t.display_test(&db).to_string()) + .unwrap_or("?".to_owned()); + + let name = + completion_context.expected_name.map_or_else(|| "?".to_owned(), |name| name.to_string()); + + expect.assert_eq(&format!("ty: {}, name: {}", ty, name)); +} + +#[test] +fn expected_type_let_without_leading_char() { + cov_mark::check!(expected_type_let_without_leading_char); + check_expected_type_and_name( + r#" +fn foo() { + let x: u32 = $0; +} +"#, + expect![[r#"ty: u32, name: x"#]], + ); +} + +#[test] +fn expected_type_let_with_leading_char() { + cov_mark::check!(expected_type_let_with_leading_char); + check_expected_type_and_name( + r#" +fn foo() { + let x: u32 = c$0; +} +"#, + expect![[r#"ty: u32, name: x"#]], + ); +} + +#[test] +fn expected_type_let_pat() { + check_expected_type_and_name( + r#" +fn foo() { + let x$0 = 0u32; +} +"#, + expect![[r#"ty: u32, name: ?"#]], + ); + check_expected_type_and_name( + r#" +fn foo() { + let $0 = 0u32; +} +"#, + expect![[r#"ty: u32, name: ?"#]], + ); +} + +#[test] +fn expected_type_fn_param() { + cov_mark::check!(expected_type_fn_param); + check_expected_type_and_name( + r#" +fn foo() { bar($0); } +fn bar(x: u32) {} +"#, + expect![[r#"ty: u32, name: x"#]], + ); + check_expected_type_and_name( + r#" +fn foo() { bar(c$0); } +fn bar(x: u32) {} +"#, + expect![[r#"ty: u32, name: x"#]], + ); +} + +#[test] +fn expected_type_fn_param_ref() { + cov_mark::check!(expected_type_fn_param_ref); + check_expected_type_and_name( + r#" +fn foo() { bar(&$0); } +fn bar(x: &u32) {} +"#, + expect![[r#"ty: u32, name: x"#]], + ); + check_expected_type_and_name( + r#" +fn foo() { bar(&mut $0); } +fn bar(x: &mut u32) {} +"#, + expect![[r#"ty: u32, name: x"#]], + ); + check_expected_type_and_name( + r#" +fn foo() { bar(& c$0); } +fn bar(x: &u32) {} + "#, + expect![[r#"ty: u32, name: x"#]], + ); + check_expected_type_and_name( + r#" +fn foo() { bar(&mut c$0); } +fn bar(x: &mut u32) {} +"#, + expect![[r#"ty: u32, name: x"#]], + ); + check_expected_type_and_name( + r#" +fn foo() { bar(&c$0); } +fn bar(x: &u32) {} + "#, + expect![[r#"ty: u32, name: x"#]], + ); +} + +#[test] +fn expected_type_struct_field_without_leading_char() { + cov_mark::check!(expected_type_struct_field_without_leading_char); + check_expected_type_and_name( + r#" +struct Foo { a: u32 } +fn foo() { + Foo { a: $0 }; +} +"#, + expect![[r#"ty: u32, name: a"#]], + ) +} + +#[test] +fn expected_type_struct_field_followed_by_comma() { + cov_mark::check!(expected_type_struct_field_followed_by_comma); + check_expected_type_and_name( + r#" +struct Foo { a: u32 } +fn foo() { + Foo { a: $0, }; +} +"#, + expect![[r#"ty: u32, name: a"#]], + ) +} + +#[test] +fn expected_type_generic_struct_field() { + check_expected_type_and_name( + r#" +struct Foo { a: T } +fn foo() -> Foo { + Foo { a: $0 } +} +"#, + expect![[r#"ty: u32, name: a"#]], + ) +} + +#[test] +fn expected_type_struct_field_with_leading_char() { + cov_mark::check!(expected_type_struct_field_with_leading_char); + check_expected_type_and_name( + r#" +struct Foo { a: u32 } +fn foo() { + Foo { a: c$0 }; +} +"#, + expect![[r#"ty: u32, name: a"#]], + ); +} + +#[test] +fn expected_type_match_arm_without_leading_char() { + cov_mark::check!(expected_type_match_arm_without_leading_char); + check_expected_type_and_name( + r#" +enum E { X } +fn foo() { + match E::X { $0 } +} +"#, + expect![[r#"ty: E, name: ?"#]], + ); +} + +#[test] +fn expected_type_match_arm_with_leading_char() { + cov_mark::check!(expected_type_match_arm_with_leading_char); + check_expected_type_and_name( + r#" +enum E { X } +fn foo() { + match E::X { c$0 } +} +"#, + expect![[r#"ty: E, name: ?"#]], + ); +} + +#[test] +fn expected_type_match_arm_body_without_leading_char() { + cov_mark::check!(expected_type_match_arm_body_without_leading_char); + check_expected_type_and_name( + r#" +struct Foo; +enum E { X } +fn foo() -> Foo { + match E::X { E::X => $0 } +} +"#, + expect![[r#"ty: Foo, name: ?"#]], + ); +} + +#[test] +fn expected_type_match_body_arm_with_leading_char() { + cov_mark::check!(expected_type_match_arm_body_with_leading_char); + check_expected_type_and_name( + r#" +struct Foo; +enum E { X } +fn foo() -> Foo { + match E::X { E::X => c$0 } +} +"#, + expect![[r#"ty: Foo, name: ?"#]], + ); +} + +#[test] +fn expected_type_if_let_without_leading_char() { + cov_mark::check!(expected_type_if_let_without_leading_char); + check_expected_type_and_name( + r#" +enum Foo { Bar, Baz, Quux } + +fn foo() { + let f = Foo::Quux; + if let $0 = f { } +} +"#, + expect![[r#"ty: Foo, name: ?"#]], + ) +} + +#[test] +fn expected_type_if_let_with_leading_char() { + cov_mark::check!(expected_type_if_let_with_leading_char); + check_expected_type_and_name( + r#" +enum Foo { Bar, Baz, Quux } + +fn foo() { + let f = Foo::Quux; + if let c$0 = f { } +} +"#, + expect![[r#"ty: Foo, name: ?"#]], + ) +} + +#[test] +fn expected_type_fn_ret_without_leading_char() { + cov_mark::check!(expected_type_fn_ret_without_leading_char); + check_expected_type_and_name( + r#" +fn foo() -> u32 { + $0 +} +"#, + expect![[r#"ty: u32, name: ?"#]], + ) +} + +#[test] +fn expected_type_fn_ret_with_leading_char() { + cov_mark::check!(expected_type_fn_ret_with_leading_char); + check_expected_type_and_name( + r#" +fn foo() -> u32 { + c$0 +} +"#, + expect![[r#"ty: u32, name: ?"#]], + ) +} + +#[test] +fn expected_type_fn_ret_fn_ref_fully_typed() { + check_expected_type_and_name( + r#" +fn foo() -> u32 { + foo$0 +} +"#, + expect![[r#"ty: u32, name: ?"#]], + ) +} + +#[test] +fn expected_type_closure_param_return() { + // FIXME: make this work with `|| $0` + check_expected_type_and_name( + r#" +//- minicore: fn +fn foo() { + bar(|| a$0); +} + +fn bar(f: impl FnOnce() -> u32) {} +"#, + expect![[r#"ty: u32, name: ?"#]], + ); +} + +#[test] +fn expected_type_generic_function() { + check_expected_type_and_name( + r#" +fn foo() { + bar::($0); +} + +fn bar(t: T) {} +"#, + expect![[r#"ty: u32, name: t"#]], + ); +} + +#[test] +fn expected_type_generic_method() { + check_expected_type_and_name( + r#" +fn foo() { + S(1u32).bar($0); +} + +struct S(T); +impl S { + fn bar(self, t: T) {} +} +"#, + expect![[r#"ty: u32, name: t"#]], + ); +} + +#[test] +fn expected_type_functional_update() { + cov_mark::check!(expected_type_struct_func_update); + check_expected_type_and_name( + r#" +struct Foo { field: u32 } +fn foo() { + Foo { + ..$0 + } +} +"#, + expect![[r#"ty: Foo, name: ?"#]], + ); +} + +#[test] +fn expected_type_param_pat() { + check_expected_type_and_name( + r#" +struct Foo { field: u32 } +fn foo(a$0: Foo) {} +"#, + expect![[r#"ty: Foo, name: ?"#]], + ); + check_expected_type_and_name( + r#" +struct Foo { field: u32 } +fn foo($0: Foo) {} +"#, + // FIXME make this work, currently fails due to pattern recovery eating the `:` + expect![[r#"ty: ?, name: ?"#]], + ); +}