9301: internal: Start refactoring ide_completion tests r=Veykril a=Veykril

Our current completion test infra resovles around usually just checking a specific `CompletionKind` which is suboptimal. We only see what we want to see in tests with this causing us to miss a lot of incorrect completions we are doing. Instead we should test for different cursor locations for all kinds(sans the magic kind maybe? not sure yet). This way we will also see potential duplicate completions that merely different in their kind.

Also since most completion submodules complete things in tests of other modules due to the tests overlapping it makes more sense to group these tests differently which implies moving them to a new module. Exceptions for this might be stuff like attribute completion as these cannot currently interfere.

I only wrote a few tests to check for completions in `ItemList` position so far and I already found a few incorrect/irrelevant completions as these haven't been tested properly due to them being hidden by the `CompletionKind` filtering.

I think `CompletionKind` doesn't really seem to be beneficial to me as to I can't think of a occasion where we would want to only check a specific completion kind.


Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
This commit is contained in:
bors[bot] 2021-06-16 19:53:43 +00:00 committed by GitHub
commit d6b8af4482
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 434 additions and 418 deletions

View File

@ -41,9 +41,9 @@ pub struct Completions {
buf: Vec<CompletionItem>,
}
impl Into<Vec<CompletionItem>> for Completions {
fn into(self) -> Vec<CompletionItem> {
self.buf
impl From<Completions> for Vec<CompletionItem> {
fn from(val: Completions) -> Self {
val.buf
}
}
@ -74,35 +74,6 @@ impl Completions {
items.into_iter().for_each(|item| self.add(item.into()))
}
pub(crate) fn add_field(
&mut self,
ctx: &CompletionContext,
receiver: Option<hir::Name>,
field: hir::Field,
ty: &hir::Type,
) {
let item = render_field(RenderContext::new(ctx), receiver, field, ty);
self.add(item);
}
pub(crate) fn add_tuple_field(
&mut self,
ctx: &CompletionContext,
receiver: Option<hir::Name>,
field: usize,
ty: &hir::Type,
) {
let item = render_tuple_field(RenderContext::new(ctx), receiver, field, ty);
self.add(item);
}
pub(crate) fn add_static_lifetime(&mut self, ctx: &CompletionContext) {
let mut item =
CompletionItem::new(CompletionKind::Reference, ctx.source_range(), "'static");
item.kind(CompletionItemKind::SymbolKind(SymbolKind::LifetimeParam));
self.add(item.build());
}
pub(crate) fn add_resolution(
&mut self,
ctx: &CompletionContext,
@ -144,33 +115,6 @@ impl Completions {
self.add_opt(render_method(RenderContext::new(ctx), None, receiver, local_name, func));
}
pub(crate) fn add_variant_pat(
&mut self,
ctx: &CompletionContext,
variant: hir::Variant,
local_name: Option<hir::Name>,
) {
self.add_opt(render_variant_pat(RenderContext::new(ctx), variant, local_name, None));
}
pub(crate) fn add_qualified_variant_pat(
&mut self,
ctx: &CompletionContext,
variant: hir::Variant,
path: hir::ModPath,
) {
self.add_opt(render_variant_pat(RenderContext::new(ctx), variant, None, Some(path)));
}
pub(crate) fn add_struct_pat(
&mut self,
ctx: &CompletionContext,
strukt: hir::Struct,
local_name: Option<hir::Name>,
) {
self.add_opt(render_struct_pat(RenderContext::new(ctx), strukt, local_name));
}
pub(crate) fn add_const(&mut self, ctx: &CompletionContext, constant: hir::Const) {
self.add_opt(render_const(RenderContext::new(ctx), constant));
}
@ -206,10 +150,67 @@ impl Completions {
let item = render_variant(RenderContext::new(ctx), None, local_name, variant, None);
self.add(item);
}
pub(crate) fn add_field(
&mut self,
ctx: &CompletionContext,
receiver: Option<hir::Name>,
field: hir::Field,
ty: &hir::Type,
) {
let item = render_field(RenderContext::new(ctx), receiver, field, ty);
self.add(item);
}
pub(crate) fn add_tuple_field(
&mut self,
ctx: &CompletionContext,
receiver: Option<hir::Name>,
field: usize,
ty: &hir::Type,
) {
let item = render_tuple_field(RenderContext::new(ctx), receiver, field, ty);
self.add(item);
}
pub(crate) fn add_static_lifetime(&mut self, ctx: &CompletionContext) {
let mut item =
CompletionItem::new(CompletionKind::Reference, ctx.source_range(), "'static");
item.kind(CompletionItemKind::SymbolKind(SymbolKind::LifetimeParam));
self.add(item.build());
}
pub(crate) fn add_variant_pat(
&mut self,
ctx: &CompletionContext,
variant: hir::Variant,
local_name: Option<hir::Name>,
) {
self.add_opt(render_variant_pat(RenderContext::new(ctx), variant, local_name, None));
}
pub(crate) fn add_qualified_variant_pat(
&mut self,
ctx: &CompletionContext,
variant: hir::Variant,
path: hir::ModPath,
) {
self.add_opt(render_variant_pat(RenderContext::new(ctx), variant, None, Some(path)));
}
pub(crate) fn add_struct_pat(
&mut self,
ctx: &CompletionContext,
strukt: hir::Struct,
local_name: Option<hir::Name>,
) {
self.add_opt(render_struct_pat(RenderContext::new(ctx), strukt, local_name));
}
}
/// Calls the callback for each variant of the provided enum with the path to the variant.
fn complete_enum_variants(
/// Skips variants that are visible with single segment paths.
fn enum_variants_with_paths(
acc: &mut Completions,
ctx: &CompletionContext,
enum_: hir::Enum,

View File

@ -322,7 +322,7 @@ mod tests {
use expect_test::{expect, Expect};
use crate::{test_utils::completion_list, CompletionKind};
use crate::{tests::filtered_completion_list, CompletionKind};
#[test]
fn attributes_are_sorted() {
@ -341,7 +341,7 @@ mod tests {
}
fn check(ra_fixture: &str, expect: Expect) {
let actual = completion_list(ra_fixture, CompletionKind::Attribute);
let actual = filtered_completion_list(ra_fixture, CompletionKind::Attribute);
expect.assert_eq(&actual);
}

View File

@ -82,7 +82,7 @@ const DEFAULT_DERIVE_COMPLETIONS: &[DeriveDependencies] = &[
mod tests {
use expect_test::{expect, Expect};
use crate::{test_utils::completion_list, CompletionKind};
use crate::{tests::filtered_completion_list, CompletionKind};
fn check(ra_fixture: &str, expect: Expect) {
let builtin_derives = r#"
@ -106,7 +106,7 @@ pub macro PartialOrd {}
pub macro Ord {}
"#;
let actual = completion_list(
let actual = filtered_completion_list(
&format!("{} {}", builtin_derives, ra_fixture),
CompletionKind::Attribute,
);

View File

@ -34,7 +34,7 @@ pub(super) fn complete_lint(
#[cfg(test)]
mod tests {
use crate::test_utils::check_edit;
use crate::tests::check_edit;
#[test]
fn check_empty() {

View File

@ -101,10 +101,10 @@ fn complete_methods(
mod tests {
use expect_test::{expect, Expect};
use crate::{test_utils::completion_list, CompletionKind};
use crate::{tests::filtered_completion_list, CompletionKind};
fn check(ra_fixture: &str, expect: Expect) {
let actual = completion_list(ra_fixture, CompletionKind::Reference);
let actual = filtered_completion_list(ra_fixture, CompletionKind::Reference);
expect.assert_eq(&actual);
}

View File

@ -227,11 +227,11 @@ mod tests {
use crate::{
item::CompletionKind,
test_utils::{check_edit, check_edit_with_config, completion_list, TEST_CONFIG},
tests::{check_edit, check_edit_with_config, filtered_completion_list, TEST_CONFIG},
};
fn check(ra_fixture: &str, expect: Expect) {
let actual = completion_list(ra_fixture, CompletionKind::Magic);
let actual = filtered_completion_list(ra_fixture, CompletionKind::Magic);
expect.assert_eq(&actual);
}

View File

@ -64,10 +64,10 @@ pub(crate) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext)
mod tests {
use expect_test::{expect, Expect};
use crate::{test_utils::completion_list, CompletionKind};
use crate::{tests::filtered_completion_list, CompletionKind};
fn check(ra_fixture: &str, expect: Expect) {
let actual = completion_list(ra_fixture, CompletionKind::Magic);
let actual = filtered_completion_list(ra_fixture, CompletionKind::Magic);
expect.assert_eq(&actual);
}

View File

@ -75,7 +75,9 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
return;
}
if expects_item || ctx.expects_non_trait_assoc_item() || ctx.expect_record_field() {
if !ctx.has_visibility_prev_sibling()
&& (expects_item || ctx.expects_non_trait_assoc_item() || ctx.expect_record_field())
{
add_keyword("pub(crate)", "pub(crate) ");
add_keyword("pub", "pub ");
}
@ -88,11 +90,13 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
}
if expects_item || has_block_expr_parent {
if !ctx.has_visibility_prev_sibling() {
add_keyword("impl", "impl $1 {\n $0\n}");
add_keyword("extern", "extern $0");
}
add_keyword("use", "use $0");
add_keyword("impl", "impl $1 {\n $0\n}");
add_keyword("trait", "trait $1 {\n $0\n}");
add_keyword("static", "static $0");
add_keyword("extern", "extern $0");
add_keyword("mod", "mod $0");
}
@ -186,12 +190,12 @@ mod tests {
use expect_test::{expect, Expect};
use crate::{
test_utils::{check_edit, completion_list},
tests::{check_edit, filtered_completion_list},
CompletionKind,
};
fn check(ra_fixture: &str, expect: Expect) {
let actual = completion_list(ra_fixture, CompletionKind::Keyword);
let actual = filtered_completion_list(ra_fixture, CompletionKind::Keyword);
expect.assert_eq(&actual)
}
@ -230,30 +234,6 @@ mod tests {
);
}
#[test]
fn test_keywords_at_source_file_level() {
check(
r"m$0",
expect![[r#"
kw pub(crate)
kw pub
kw unsafe
kw fn
kw const
kw type
kw use
kw impl
kw trait
kw static
kw extern
kw mod
kw enum
kw struct
kw union
"#]],
);
}
#[test]
fn test_keywords_in_function() {
check(
@ -263,11 +243,11 @@ mod tests {
kw fn
kw const
kw type
kw use
kw impl
kw extern
kw use
kw trait
kw static
kw extern
kw mod
kw match
kw while
@ -291,11 +271,11 @@ mod tests {
kw fn
kw const
kw type
kw use
kw impl
kw extern
kw use
kw trait
kw static
kw extern
kw mod
kw match
kw while
@ -319,11 +299,11 @@ mod tests {
kw fn
kw const
kw type
kw use
kw impl
kw extern
kw use
kw trait
kw static
kw extern
kw mod
kw match
kw while
@ -369,49 +349,6 @@ fn quux() -> i32 {
);
}
#[test]
fn test_keywords_in_trait_def() {
check(
r"trait My { $0 }",
expect![[r#"
kw unsafe
kw fn
kw const
kw type
"#]],
);
}
#[test]
fn test_keywords_in_impl_def() {
check(
r"impl My { $0 }",
expect![[r#"
kw pub(crate)
kw pub
kw unsafe
kw fn
kw const
kw type
"#]],
);
}
#[test]
fn test_keywords_in_impl_def_with_attr() {
check(
r"impl My { #[foo] $0 }",
expect![[r#"
kw pub(crate)
kw pub
kw unsafe
kw fn
kw const
kw type
"#]],
);
}
#[test]
fn test_keywords_in_loop() {
check(
@ -421,11 +358,11 @@ fn quux() -> i32 {
kw fn
kw const
kw type
kw use
kw impl
kw extern
kw use
kw trait
kw static
kw extern
kw mod
kw match
kw while
@ -442,18 +379,6 @@ fn quux() -> i32 {
);
}
#[test]
fn test_keywords_after_unsafe_in_item_list() {
check(
r"unsafe $0",
expect![[r#"
kw fn
kw trait
kw impl
"#]],
);
}
#[test]
fn test_keywords_after_unsafe_in_block_expr() {
check(

View File

@ -50,7 +50,7 @@ mod tests {
use expect_test::{expect, Expect};
use crate::{
test_utils::{check_edit, completion_list_with_config, TEST_CONFIG},
tests::{check_edit, filtered_completion_list_with_config, TEST_CONFIG},
CompletionConfig, CompletionKind,
};
@ -59,7 +59,8 @@ mod tests {
}
fn check_with_config(config: CompletionConfig, ra_fixture: &str, expect: Expect) {
let actual = completion_list_with_config(config, ra_fixture, CompletionKind::Reference);
let actual =
filtered_completion_list_with_config(config, ra_fixture, CompletionKind::Reference);
expect.assert_eq(&actual)
}

View File

@ -141,11 +141,11 @@ fn module_chain_to_containing_module_file(
#[cfg(test)]
mod tests {
use crate::{test_utils::completion_list, CompletionKind};
use crate::{tests::filtered_completion_list, CompletionKind};
use expect_test::{expect, Expect};
fn check(ra_fixture: &str, expect: Expect) {
let actual = completion_list(ra_fixture, CompletionKind::Magic);
let actual = filtered_completion_list(ra_fixture, CompletionKind::Magic);
expect.assert_eq(&actual);
}

View File

@ -13,7 +13,7 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
if let Some(hir::Adt::Enum(e)) =
ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt())
{
super::complete_enum_variants(acc, ctx, e, |acc, ctx, variant, path| {
super::enum_variants_with_paths(acc, ctx, e, |acc, ctx, variant, path| {
acc.add_qualified_variant_pat(ctx, variant, path.clone());
acc.add_qualified_enum_variant(ctx, variant, path);
});
@ -61,17 +61,17 @@ mod tests {
use expect_test::{expect, Expect};
use crate::{
test_utils::{check_edit, completion_list},
tests::{check_edit, filtered_completion_list},
CompletionKind,
};
fn check(ra_fixture: &str, expect: Expect) {
let actual = completion_list(ra_fixture, CompletionKind::Reference);
let actual = filtered_completion_list(ra_fixture, CompletionKind::Reference);
expect.assert_eq(&actual)
}
fn check_snippet(ra_fixture: &str, expect: Expect) {
let actual = completion_list(ra_fixture, CompletionKind::Snippet);
let actual = filtered_completion_list(ra_fixture, CompletionKind::Snippet);
expect.assert_eq(&actual)
}

View File

@ -307,12 +307,12 @@ mod tests {
use expect_test::{expect, Expect};
use crate::{
test_utils::{check_edit, completion_list},
tests::{check_edit, filtered_completion_list},
CompletionKind,
};
fn check(ra_fixture: &str, expect: Expect) {
let actual = completion_list(ra_fixture, CompletionKind::Postfix);
let actual = filtered_completion_list(ra_fixture, CompletionKind::Postfix);
expect.assert_eq(&actual)
}

View File

@ -198,17 +198,17 @@ mod tests {
use expect_test::{expect, Expect};
use crate::{
test_utils::{check_edit, completion_list},
tests::{check_edit, filtered_completion_list},
CompletionKind,
};
fn check(ra_fixture: &str, expect: Expect) {
let actual = completion_list(ra_fixture, CompletionKind::Reference);
let actual = filtered_completion_list(ra_fixture, CompletionKind::Reference);
expect.assert_eq(&actual);
}
fn check_builtin(ra_fixture: &str, expect: Expect) {
let actual = completion_list(ra_fixture, CompletionKind::BuiltinType);
let actual = filtered_completion_list(ra_fixture, CompletionKind::BuiltinType);
expect.assert_eq(&actual);
}
@ -713,24 +713,6 @@ impl MyStruct {
);
}
#[test]
fn completes_in_item_list() {
check(
r#"
struct MyStruct {}
#[macro_export]
macro_rules! foo {}
mod bar {}
crate::$0
"#,
expect![[r#"
md bar
ma foo!() #[macro_export] macro_rules! foo
"#]],
)
}
#[test]
fn test_super_super_completion() {
check(

View File

@ -51,17 +51,17 @@ mod tests {
use ide_db::helpers::FamousDefs;
use crate::{
test_utils::{self, completion_list},
tests::{self, filtered_completion_list},
CompletionKind,
};
fn check(ra_fixture: &str, expect: Expect) {
let actual = completion_list(ra_fixture, CompletionKind::Reference);
let actual = filtered_completion_list(ra_fixture, CompletionKind::Reference);
expect.assert_eq(&actual);
}
fn check_snippet(ra_fixture: &str, expect: Expect) {
let actual = completion_list(
let actual = filtered_completion_list(
&format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE),
CompletionKind::Snippet,
);
@ -69,7 +69,7 @@ mod tests {
}
fn check_edit(what: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
test_utils::check_edit(
tests::check_edit(
what,
&format!(
"//- /main.rs crate:main deps:core{}\n{}",

View File

@ -1,6 +1,7 @@
//! This file provides snippet completions, like `pd` => `eprintln!(...)`.
use ide_db::helpers::SnippetCap;
use syntax::T;
use crate::{
context::PathCompletionContext, item::Builder, CompletionContext, CompletionItem,
@ -35,9 +36,13 @@ pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionConte
}
pub(crate) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionContext) {
if !ctx.expects_item() {
if !ctx.expects_item() || ctx.previous_token_is(T![unsafe]) || ctx.path_qual().is_some() {
return;
}
if ctx.has_visibility_prev_sibling() {
return; // technically we could do some of these snippet completions if we were to put the
// attributes before the vis node.
}
let cap = match ctx.config.snippet_cap {
Some(it) => it,
None => return,
@ -82,10 +87,10 @@ fn ${1:feature}() {
mod tests {
use expect_test::{expect, Expect};
use crate::{test_utils::completion_list, CompletionKind};
use crate::{tests::filtered_completion_list, CompletionKind};
fn check(ra_fixture: &str, expect: Expect) {
let actual = completion_list(ra_fixture, CompletionKind::Snippet);
let actual = filtered_completion_list(ra_fixture, CompletionKind::Snippet);
expect.assert_eq(&actual)
}
@ -105,21 +110,4 @@ mod tests {
check(r#"fn foo(x: i32) { ::foo$0 }"#, expect![[""]]);
check(r#"fn foo(x: i32) { ::$0 }"#, expect![[""]]);
}
#[test]
fn completes_snippets_in_items() {
check(
r#"
#[cfg(test)]
mod tests {
$0
}
"#,
expect![[r#"
sn tmod (Test module)
sn tfn (Test function)
sn macro_rules
"#]],
)
}
}

View File

@ -246,12 +246,12 @@ mod tests {
use expect_test::{expect, Expect};
use crate::{
test_utils::{check_edit, completion_list},
tests::{check_edit, filtered_completion_list},
CompletionKind,
};
fn check(ra_fixture: &str, expect: Expect) {
let actual = completion_list(ra_fixture, CompletionKind::Magic);
let actual = filtered_completion_list(ra_fixture, CompletionKind::Magic);
expect.assert_eq(&actual)
}

View File

@ -40,7 +40,7 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
if let Some(hir::Adt::Enum(e)) =
ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt())
{
super::complete_enum_variants(acc, ctx, e, |acc, ctx, variant, path| {
super::enum_variants_with_paths(acc, ctx, e, |acc, ctx, variant, path| {
acc.add_qualified_enum_variant(ctx, variant, path)
});
}
@ -93,7 +93,7 @@ mod tests {
use expect_test::{expect, Expect};
use crate::{
test_utils::{check_edit, completion_list_with_config, TEST_CONFIG},
tests::{check_edit, filtered_completion_list_with_config, TEST_CONFIG},
CompletionConfig, CompletionKind,
};
@ -102,7 +102,8 @@ mod tests {
}
fn check_with_config(config: CompletionConfig, ra_fixture: &str, expect: Expect) {
let actual = completion_list_with_config(config, ra_fixture, CompletionKind::Reference);
let actual =
filtered_completion_list_with_config(config, ra_fixture, CompletionKind::Reference);
expect.assert_eq(&actual)
}
@ -500,18 +501,6 @@ fn f() {$0}
check(
r#"
#[rustc_builtin_macro]
pub macro Clone {}
struct S;
impl S {
$0
}
"#,
expect![[r#""#]],
);
check(
r#"
#[rustc_builtin_macro]
pub macro bench {}
fn f() {$0}
@ -772,42 +761,6 @@ impl My$0
)
}
#[test]
fn completes_in_assoc_item_list() {
check(
r#"
macro_rules! foo {}
mod bar {}
struct MyStruct {}
impl MyStruct {
$0
}
"#,
expect![[r#"
md bar
ma foo!() macro_rules! foo
"#]],
)
}
#[test]
fn completes_in_item_list() {
check(
r#"
struct MyStruct {}
macro_rules! foo {}
mod bar {}
$0
"#,
expect![[r#"
md bar
ma foo!() macro_rules! foo
"#]],
)
}
#[test]
fn completes_types_and_const_in_arg_list() {
check(

View File

@ -302,18 +302,28 @@ impl<'a> CompletionContext<'a> {
)
}
pub(crate) fn has_visibility_prev_sibling(&self) -> bool {
matches!(self.prev_sibling, Some(ImmediatePrevSibling::Visibility))
}
pub(crate) fn after_if(&self) -> bool {
matches!(self.prev_sibling, Some(ImmediatePrevSibling::IfExpr))
}
pub(crate) fn is_path_disallowed(&self) -> bool {
matches!(
self.completion_location,
Some(ImmediateLocation::Attribute(_))
| Some(ImmediateLocation::ModDeclaration(_))
| Some(ImmediateLocation::RecordPat(_))
| Some(ImmediateLocation::RecordExpr(_))
) || self.attribute_under_caret.is_some()
self.attribute_under_caret.is_some()
|| self.previous_token_is(T![unsafe])
|| matches!(
self.prev_sibling,
Some(ImmediatePrevSibling::Attribute) | Some(ImmediatePrevSibling::Visibility)
)
|| matches!(
self.completion_location,
Some(ImmediateLocation::Attribute(_))
| Some(ImmediateLocation::ModDeclaration(_))
| Some(ImmediateLocation::RecordPat(_))
| Some(ImmediateLocation::RecordExpr(_))
)
}
pub(crate) fn expects_expression(&self) -> bool {
@ -685,7 +695,7 @@ mod tests {
use expect_test::{expect, Expect};
use hir::HirDisplay;
use crate::test_utils::{position, TEST_CONFIG};
use crate::tests::{position, TEST_CONFIG};
use super::CompletionContext;

View File

@ -1,14 +1,14 @@
//! `completions` crate provides utilities for generating completions of user input.
mod completions;
mod config;
mod item;
mod context;
mod item;
mod patterns;
#[cfg(test)]
mod test_utils;
mod render;
mod completions;
#[cfg(test)]
mod tests;
use completions::flyimport::position_for_import;
use ide_db::{
@ -141,6 +141,7 @@ pub fn completions(
let ctx = CompletionContext::new(db, position, config)?;
if ctx.no_completion_required() {
cov_mark::hit!(no_completion_required);
// No work required here.
return None;
}
@ -200,117 +201,3 @@ pub fn resolve_completion_edits(
ImportEdit { import, scope }.to_text_edit(config.insert_use).map(|edit| vec![edit])
}
#[cfg(test)]
mod tests {
use crate::test_utils::{self, TEST_CONFIG};
struct DetailAndDocumentation<'a> {
detail: &'a str,
documentation: &'a str,
}
fn check_detail_and_documentation(ra_fixture: &str, expected: DetailAndDocumentation) {
let (db, position) = test_utils::position(ra_fixture);
let config = TEST_CONFIG;
let completions: Vec<_> = crate::completions(&db, &config, position).unwrap().into();
for item in completions {
if item.detail() == Some(expected.detail) {
let opt = item.documentation();
let doc = opt.as_ref().map(|it| it.as_str());
assert_eq!(doc, Some(expected.documentation));
return;
}
}
panic!("completion detail not found: {}", expected.detail)
}
fn check_no_completion(ra_fixture: &str) {
let (db, position) = test_utils::position(ra_fixture);
let config = TEST_CONFIG;
let completions: Option<Vec<String>> = crate::completions(&db, &config, position)
.and_then(|completions| {
let completions: Vec<_> = completions.into();
if completions.is_empty() {
None
} else {
Some(completions)
}
})
.map(|completions| {
completions.into_iter().map(|completion| format!("{:?}", completion)).collect()
});
// `assert_eq` instead of `assert!(completions.is_none())` to get the list of completions if test will panic.
assert_eq!(completions, None, "Completions were generated, but weren't expected");
}
#[test]
fn test_completion_detail_from_macro_generated_struct_fn_doc_attr() {
check_detail_and_documentation(
r#"
macro_rules! bar {
() => {
struct Bar;
impl Bar {
#[doc = "Do the foo"]
fn foo(&self) {}
}
}
}
bar!();
fn foo() {
let bar = Bar;
bar.fo$0;
}
"#,
DetailAndDocumentation { detail: "fn(&self)", documentation: "Do the foo" },
);
}
#[test]
fn test_completion_detail_from_macro_generated_struct_fn_doc_comment() {
check_detail_and_documentation(
r#"
macro_rules! bar {
() => {
struct Bar;
impl Bar {
/// Do the foo
fn foo(&self) {}
}
}
}
bar!();
fn foo() {
let bar = Bar;
bar.fo$0;
}
"#,
DetailAndDocumentation { detail: "fn(&self)", documentation: "Do the foo" },
);
}
#[test]
fn test_no_completions_required() {
// There must be no hint for 'in' keyword.
check_no_completion(r#"fn foo() { for i i$0 }"#);
// After 'in' keyword hints may be spawned.
check_detail_and_documentation(
r#"
/// Do the foo
fn foo() -> &'static str { "foo" }
fn bar() {
for c in fo$0
}
"#,
DetailAndDocumentation { detail: "fn() -> &str", documentation: "Do the foo" },
);
}
}

View File

@ -11,7 +11,7 @@ use syntax::{
};
#[cfg(test)]
use crate::test_utils::{check_pattern_is_applicable, check_pattern_is_not_applicable};
use crate::tests::{check_pattern_is_applicable, check_pattern_is_not_applicable};
/// Immediate previous node to what we are completing.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
@ -19,6 +19,8 @@ pub(crate) enum ImmediatePrevSibling {
IfExpr,
TraitDefName,
ImplDefType,
Visibility,
Attribute,
}
/// Direct parent "thing" of what we are currently completing.
@ -79,6 +81,17 @@ pub(crate) fn determine_prev_sibling(name_like: &ast::NameLike) -> Option<Immedi
_ => node,
};
let prev_sibling = non_trivia_sibling(node.into(), Direction::Prev)?.into_node()?;
if prev_sibling.kind() == ERROR {
let prev_sibling = prev_sibling.first_child()?;
let res = match_ast! {
match prev_sibling {
// vis followed by random ident will always error the parser
ast::Visibility(_it) => ImmediatePrevSibling::Visibility,
_ => return None,
}
};
return Some(res);
}
let res = match_ast! {
match prev_sibling {
ast::ExprStmt(it) => {
@ -101,6 +114,7 @@ pub(crate) fn determine_prev_sibling(name_like: &ast::NameLike) -> Option<Immedi
} else {
return None
},
ast::Attr(_it) => ImmediatePrevSibling::Attribute,
_ => return None,
}
};
@ -310,7 +324,7 @@ fn previous_non_trivia_token(token: SyntaxToken) -> Option<SyntaxToken> {
mod tests {
use syntax::algo::find_node_at_offset;
use crate::test_utils::position;
use crate::tests::position;
use super::*;
@ -421,4 +435,14 @@ mod tests {
check_prev_sibling(r"fn foo() { if true {} w$0", ImmediatePrevSibling::IfExpr);
check_prev_sibling(r"fn foo() { if true {}; w$0", None);
}
#[test]
fn test_vis_prev_sibling() {
check_prev_sibling(r"pub w$0", ImmediatePrevSibling::Visibility);
}
#[test]
fn test_attr_prev_sibling() {
check_prev_sibling(r"#[attr] w$0", ImmediatePrevSibling::Attribute);
}
}

View File

@ -335,7 +335,7 @@ mod tests {
use crate::{
item::CompletionRelevanceTypeMatch,
test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG},
tests::{check_edit, do_completion, get_all_items, TEST_CONFIG},
CompletionKind, CompletionRelevance,
};

View File

@ -121,7 +121,7 @@ impl<'a> EnumRender<'a> {
#[cfg(test)]
mod tests {
use crate::test_utils::check_edit;
use crate::tests::check_edit;
#[test]
fn inserts_parens_for_tuple_enums() {

View File

@ -191,7 +191,7 @@ impl<'a> FunctionRender<'a> {
#[cfg(test)]
mod tests {
use crate::{
test_utils::{check_edit, check_edit_with_config, TEST_CONFIG},
tests::{check_edit, check_edit_with_config, TEST_CONFIG},
CompletionConfig,
};

View File

@ -133,7 +133,7 @@ fn guess_macro_braces(macro_name: &str, docs: &str) -> (&'static str, &'static s
#[cfg(test)]
mod tests {
use crate::test_utils::check_edit;
use crate::tests::check_edit;
#[test]
fn dont_insert_macro_call_parens_unncessary() {

View File

@ -1,4 +1,4 @@
//! Runs completion for testing purposes.
mod item_list;
use hir::{PrefixKind, Semantics};
use ide_db::{
@ -31,6 +31,14 @@ pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig {
},
};
fn completion_list(code: &str) -> String {
completion_list_with_config(TEST_CONFIG, code)
}
fn completion_list_with_config(config: CompletionConfig, code: &str) -> String {
render_completion_list(get_all_items(config, code))
}
/// Creates analysis from a multi-file fixture, returns positions marked with $0.
pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
let change_fixture = ChangeFixture::parse(ra_fixture);
@ -57,24 +65,27 @@ pub(crate) fn do_completion_with_config(
.collect()
}
pub(crate) fn completion_list(code: &str, kind: CompletionKind) -> String {
completion_list_with_config(TEST_CONFIG, code, kind)
pub(crate) fn filtered_completion_list(code: &str, kind: CompletionKind) -> String {
filtered_completion_list_with_config(TEST_CONFIG, code, kind)
}
pub(crate) fn completion_list_with_config(
pub(crate) fn filtered_completion_list_with_config(
config: CompletionConfig,
code: &str,
kind: CompletionKind,
) -> String {
let kind_completions: Vec<CompletionItem> =
get_all_items(config, code).into_iter().filter(|c| c.completion_kind == kind).collect();
let label_width = kind_completions
.iter()
.map(|it| monospace_width(it.label()))
.max()
.unwrap_or_default()
.min(16);
kind_completions
render_completion_list(kind_completions)
}
fn render_completion_list(completions: Vec<CompletionItem>) -> String {
fn monospace_width(s: &str) -> usize {
s.chars().count()
}
let label_width =
completions.iter().map(|it| monospace_width(it.label())).max().unwrap_or_default().min(16);
completions
.into_iter()
.map(|it| {
let tag = it.kind().unwrap().tag();
@ -93,10 +104,6 @@ pub(crate) fn completion_list_with_config(
.collect()
}
fn monospace_width(s: &str) -> usize {
s.chars().count()
}
pub(crate) fn check_edit(what: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
check_edit_with_config(TEST_CONFIG, what, ra_fixture_before, ra_fixture_after)
}
@ -152,3 +159,18 @@ pub(crate) fn get_all_items(config: CompletionConfig, code: &str) -> Vec<Complet
let (db, position) = position(code);
crate::completions(&db, &config, position).unwrap().into()
}
fn check_no_completion(ra_fixture: &str) {
let (db, position) = position(ra_fixture);
assert!(
crate::completions(&db, &TEST_CONFIG, position).is_none(),
"Completions were generated, but weren't expected"
);
}
#[test]
fn test_no_completions_required() {
cov_mark::check!(no_completion_required);
check_no_completion(r#"fn foo() { for i i$0 }"#);
}

View File

@ -0,0 +1,223 @@
use expect_test::{expect, Expect};
use crate::tests::completion_list;
fn check(ra_fixture: &str, expect: Expect) {
let base = r#"#[rustc_builtin_macro]
pub macro Clone {}
enum Enum { Variant }
struct Struct {}
#[macro_export]
macro_rules! foo {}
mod bar {}
const CONST: () = ();
trait Trait {}
"#;
let actual = completion_list(&format!("{}{}", base, ra_fixture));
expect.assert_eq(&actual)
}
#[test]
fn in_mod_item_list() {
check(
r#"mod tests { $0 }"#,
expect![[r##"
kw pub(crate)
kw pub
kw unsafe
kw fn
kw const
kw type
kw impl
kw extern
kw use
kw trait
kw static
kw mod
kw enum
kw struct
kw union
sn tmod (Test module)
sn tfn (Test function)
sn macro_rules
ma foo!() #[macro_export] macro_rules! foo
"##]],
)
}
#[test]
fn in_source_file_item_list() {
check(
r#"$0"#,
expect![[r##"
kw pub(crate)
kw pub
kw unsafe
kw fn
kw const
kw type
kw impl
kw extern
kw use
kw trait
kw static
kw mod
kw enum
kw struct
kw union
sn tmod (Test module)
sn tfn (Test function)
sn macro_rules
md bar
ma foo!() #[macro_export] macro_rules! foo
ma foo!() #[macro_export] macro_rules! foo
"##]],
)
}
#[test]
fn in_item_list_after_attr() {
check(
r#"#[attr] $0"#,
expect![[r#"
kw pub(crate)
kw pub
kw unsafe
kw fn
kw const
kw type
kw impl
kw extern
kw use
kw trait
kw static
kw mod
kw enum
kw struct
kw union
sn tmod (Test module)
sn tfn (Test function)
sn macro_rules
"#]],
)
}
#[test]
fn in_qualified_path() {
check(
r#"crate::$0"#,
expect![[r##"
kw pub(crate)
kw pub
kw unsafe
kw fn
kw const
kw type
kw impl
kw extern
kw use
kw trait
kw static
kw mod
kw enum
kw struct
kw union
md bar
ma foo!() #[macro_export] macro_rules! foo
"##]],
)
}
#[test]
fn after_unsafe_token() {
check(
r#"unsafe $0"#,
expect![[r#"
kw fn
kw trait
kw impl
"#]],
);
}
#[test]
fn after_visibility() {
check(
r#"pub $0"#,
expect![[r#"
kw unsafe
kw fn
kw const
kw type
kw use
kw trait
kw static
kw mod
kw enum
kw struct
kw union
"#]],
);
}
#[test]
fn after_visibility_unsafe() {
// FIXME this shouldn't show `impl`
check(
r#"pub unsafe $0"#,
expect![[r#"
kw fn
kw trait
kw impl
"#]],
);
}
#[test]
fn in_impl_assoc_item_list() {
check(
r#"impl Struct { $0 }"#,
expect![[r##"
kw pub(crate)
kw pub
kw unsafe
kw fn
kw const
kw type
md bar
ma foo!() #[macro_export] macro_rules! foo
ma foo!() #[macro_export] macro_rules! foo
"##]],
)
}
#[test]
fn in_impl_assoc_item_list_after_attr() {
check(
r#"impl Struct { #[attr] $0 }"#,
expect![[r#"
kw pub(crate)
kw pub
kw unsafe
kw fn
kw const
kw type
"#]],
)
}
#[test]
fn in_trait_assoc_item_list() {
check(
r"trait Foo { $0 }",
expect![[r##"
kw unsafe
kw fn
kw const
kw type
md bar
ma foo!() #[macro_export] macro_rules! foo
ma foo!() #[macro_export] macro_rules! foo
"##]],
);
}