diff options
Diffstat (limited to 'src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs')
-rw-r--r-- | src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs | 1297 |
1 files changed, 1297 insertions, 0 deletions
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs new file mode 100644 index 000000000..0c2e9da38 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs @@ -0,0 +1,1297 @@ +use std::iter; + +use hir::AsAssocItem; +use ide_db::RootDatabase; +use ide_db::{ + helpers::mod_path_to_ast, + imports::import_assets::{ImportCandidate, LocatedImport}, +}; +use syntax::{ + ast, + ast::{make, HasArgList}, + AstNode, NodeOrToken, +}; + +use crate::{ + assist_context::{AssistContext, Assists}, + handlers::auto_import::find_importable_node, + AssistId, AssistKind, GroupLabel, +}; + +// Assist: qualify_path +// +// If the name is unresolved, provides all possible qualified paths for it. +// +// ``` +// fn main() { +// let map = HashMap$0::new(); +// } +// # pub mod std { pub mod collections { pub struct HashMap { } } } +// ``` +// -> +// ``` +// fn main() { +// let map = std::collections::HashMap::new(); +// } +// # pub mod std { pub mod collections { pub struct HashMap { } } } +// ``` +pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + let (import_assets, syntax_under_caret) = find_importable_node(ctx)?; + let mut proposed_imports = import_assets.search_for_relative_paths(&ctx.sema); + if proposed_imports.is_empty() { + return None; + } + + let range = match &syntax_under_caret { + NodeOrToken::Node(node) => ctx.sema.original_range(node).range, + NodeOrToken::Token(token) => token.text_range(), + }; + let candidate = import_assets.import_candidate(); + let qualify_candidate = match syntax_under_caret { + NodeOrToken::Node(syntax_under_caret) => match candidate { + ImportCandidate::Path(candidate) if candidate.qualifier.is_some() => { + cov_mark::hit!(qualify_path_qualifier_start); + let path = ast::Path::cast(syntax_under_caret)?; + let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?); + QualifyCandidate::QualifierStart(segment, prev_segment.generic_arg_list()) + } + ImportCandidate::Path(_) => { + cov_mark::hit!(qualify_path_unqualified_name); + let path = ast::Path::cast(syntax_under_caret)?; + let generics = path.segment()?.generic_arg_list(); + QualifyCandidate::UnqualifiedName(generics) + } + ImportCandidate::TraitAssocItem(_) => { + cov_mark::hit!(qualify_path_trait_assoc_item); + let path = ast::Path::cast(syntax_under_caret)?; + let (qualifier, segment) = (path.qualifier()?, path.segment()?); + QualifyCandidate::TraitAssocItem(qualifier, segment) + } + ImportCandidate::TraitMethod(_) => { + cov_mark::hit!(qualify_path_trait_method); + let mcall_expr = ast::MethodCallExpr::cast(syntax_under_caret)?; + QualifyCandidate::TraitMethod(ctx.sema.db, mcall_expr) + } + }, + // derive attribute path + NodeOrToken::Token(_) => QualifyCandidate::UnqualifiedName(None), + }; + + // we aren't interested in different namespaces + proposed_imports.dedup_by(|a, b| a.import_path == b.import_path); + + let group_label = group_label(candidate); + for import in proposed_imports { + acc.add_group( + &group_label, + AssistId("qualify_path", AssistKind::QuickFix), + label(candidate, &import), + range, + |builder| { + qualify_candidate.qualify( + |replace_with: String| builder.replace(range, replace_with), + &import.import_path, + import.item_to_import, + ) + }, + ); + } + Some(()) +} +pub(crate) enum QualifyCandidate<'db> { + QualifierStart(ast::PathSegment, Option<ast::GenericArgList>), + UnqualifiedName(Option<ast::GenericArgList>), + TraitAssocItem(ast::Path, ast::PathSegment), + TraitMethod(&'db RootDatabase, ast::MethodCallExpr), + ImplMethod(&'db RootDatabase, ast::MethodCallExpr, hir::Function), +} + +impl QualifyCandidate<'_> { + pub(crate) fn qualify( + &self, + mut replacer: impl FnMut(String), + import: &hir::ModPath, + item: hir::ItemInNs, + ) { + let import = mod_path_to_ast(import); + match self { + QualifyCandidate::QualifierStart(segment, generics) => { + let generics = generics.as_ref().map_or_else(String::new, ToString::to_string); + replacer(format!("{}{}::{}", import, generics, segment)); + } + QualifyCandidate::UnqualifiedName(generics) => { + let generics = generics.as_ref().map_or_else(String::new, ToString::to_string); + replacer(format!("{}{}", import, generics)); + } + QualifyCandidate::TraitAssocItem(qualifier, segment) => { + replacer(format!("<{} as {}>::{}", qualifier, import, segment)); + } + QualifyCandidate::TraitMethod(db, mcall_expr) => { + Self::qualify_trait_method(db, mcall_expr, replacer, import, item); + } + QualifyCandidate::ImplMethod(db, mcall_expr, hir_fn) => { + Self::qualify_fn_call(db, mcall_expr, replacer, import, hir_fn); + } + } + } + + fn qualify_fn_call( + db: &RootDatabase, + mcall_expr: &ast::MethodCallExpr, + mut replacer: impl FnMut(String), + import: ast::Path, + hir_fn: &hir::Function, + ) -> Option<()> { + let receiver = mcall_expr.receiver()?; + let method_name = mcall_expr.name_ref()?; + let generics = + mcall_expr.generic_arg_list().as_ref().map_or_else(String::new, ToString::to_string); + let arg_list = mcall_expr.arg_list().map(|arg_list| arg_list.args()); + + if let Some(self_access) = hir_fn.self_param(db).map(|sp| sp.access(db)) { + let receiver = match self_access { + hir::Access::Shared => make::expr_ref(receiver, false), + hir::Access::Exclusive => make::expr_ref(receiver, true), + hir::Access::Owned => receiver, + }; + replacer(format!( + "{}::{}{}{}", + import, + method_name, + generics, + match arg_list { + Some(args) => make::arg_list(iter::once(receiver).chain(args)), + None => make::arg_list(iter::once(receiver)), + } + )); + } + Some(()) + } + + fn qualify_trait_method( + db: &RootDatabase, + mcall_expr: &ast::MethodCallExpr, + replacer: impl FnMut(String), + import: ast::Path, + item: hir::ItemInNs, + ) -> Option<()> { + let trait_method_name = mcall_expr.name_ref()?; + let trait_ = item_as_trait(db, item)?; + let method = find_trait_method(db, trait_, &trait_method_name)?; + Self::qualify_fn_call(db, mcall_expr, replacer, import, &method) + } +} + +fn find_trait_method( + db: &RootDatabase, + trait_: hir::Trait, + trait_method_name: &ast::NameRef, +) -> Option<hir::Function> { + if let Some(hir::AssocItem::Function(method)) = + trait_.items(db).into_iter().find(|item: &hir::AssocItem| { + item.name(db) + .map(|name| name.to_string() == trait_method_name.to_string()) + .unwrap_or(false) + }) + { + Some(method) + } else { + None + } +} + +fn item_as_trait(db: &RootDatabase, item: hir::ItemInNs) -> Option<hir::Trait> { + let item_module_def = item.as_module_def()?; + + match item_module_def { + hir::ModuleDef::Trait(trait_) => Some(trait_), + _ => item_module_def.as_assoc_item(db)?.containing_trait(db), + } +} + +fn group_label(candidate: &ImportCandidate) -> GroupLabel { + let name = match candidate { + ImportCandidate::Path(it) => &it.name, + ImportCandidate::TraitAssocItem(it) | ImportCandidate::TraitMethod(it) => { + &it.assoc_item_name + } + } + .text(); + GroupLabel(format!("Qualify {}", name)) +} + +fn label(candidate: &ImportCandidate, import: &LocatedImport) -> String { + match candidate { + ImportCandidate::Path(candidate) if candidate.qualifier.is_none() => { + format!("Qualify as `{}`", import.import_path) + } + _ => format!("Qualify with `{}`", import.import_path), + } +} + +#[cfg(test)] +mod tests { + use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; + + use super::*; + + #[test] + fn applicable_when_found_an_import_partial() { + cov_mark::check!(qualify_path_unqualified_name); + check_assist( + qualify_path, + r#" +mod std { + pub mod fmt { + pub struct Formatter; + } +} + +use std::fmt; + +$0Formatter +"#, + r#" +mod std { + pub mod fmt { + pub struct Formatter; + } +} + +use std::fmt; + +fmt::Formatter +"#, + ); + } + + #[test] + fn applicable_when_found_an_import() { + check_assist( + qualify_path, + r#" +$0PubStruct + +pub mod PubMod { + pub struct PubStruct; +} +"#, + r#" +PubMod::PubStruct + +pub mod PubMod { + pub struct PubStruct; +} +"#, + ); + } + + #[test] + fn applicable_in_macros() { + check_assist( + qualify_path, + r#" +macro_rules! foo { + ($i:ident) => { fn foo(a: $i) {} } +} +foo!(Pub$0Struct); + +pub mod PubMod { + pub struct PubStruct; +} +"#, + r#" +macro_rules! foo { + ($i:ident) => { fn foo(a: $i) {} } +} +foo!(PubMod::PubStruct); + +pub mod PubMod { + pub struct PubStruct; +} +"#, + ); + } + + #[test] + fn applicable_when_found_multiple_imports() { + check_assist( + qualify_path, + r#" +PubSt$0ruct + +pub mod PubMod1 { + pub struct PubStruct; +} +pub mod PubMod2 { + pub struct PubStruct; +} +pub mod PubMod3 { + pub struct PubStruct; +} +"#, + r#" +PubMod3::PubStruct + +pub mod PubMod1 { + pub struct PubStruct; +} +pub mod PubMod2 { + pub struct PubStruct; +} +pub mod PubMod3 { + pub struct PubStruct; +} +"#, + ); + } + + #[test] + fn not_applicable_for_already_imported_types() { + check_assist_not_applicable( + qualify_path, + r#" +use PubMod::PubStruct; + +PubStruct$0 + +pub mod PubMod { + pub struct PubStruct; +} +"#, + ); + } + + #[test] + fn not_applicable_for_types_with_private_paths() { + check_assist_not_applicable( + qualify_path, + r#" +PrivateStruct$0 + +pub mod PubMod { + struct PrivateStruct; +} +"#, + ); + } + + #[test] + fn not_applicable_when_no_imports_found() { + check_assist_not_applicable(qualify_path, r#"PubStruct$0"#); + } + + #[test] + fn qualify_function() { + check_assist( + qualify_path, + r#" +test_function$0 + +pub mod PubMod { + pub fn test_function() {}; +} +"#, + r#" +PubMod::test_function + +pub mod PubMod { + pub fn test_function() {}; +} +"#, + ); + } + + #[test] + fn qualify_macro() { + check_assist( + qualify_path, + r#" +//- /lib.rs crate:crate_with_macro +#[macro_export] +macro_rules! foo { + () => () +} + +//- /main.rs crate:main deps:crate_with_macro +fn main() { + foo$0 +} +"#, + r#" +fn main() { + crate_with_macro::foo +} +"#, + ); + } + + #[test] + fn qualify_path_target() { + check_assist_target( + qualify_path, + r#" +struct AssistInfo { + group_label: Option<$0GroupLabel>, +} + +mod m { pub struct GroupLabel; } +"#, + "GroupLabel", + ) + } + + #[test] + fn not_applicable_when_path_start_is_imported() { + check_assist_not_applicable( + qualify_path, + r#" +pub mod mod1 { + pub mod mod2 { + pub mod mod3 { + pub struct TestStruct; + } + } +} + +use mod1::mod2; +fn main() { + mod2::mod3::TestStruct$0 +} +"#, + ); + } + + #[test] + fn not_applicable_for_imported_function() { + check_assist_not_applicable( + qualify_path, + r#" +pub mod test_mod { + pub fn test_function() {} +} + +use test_mod::test_function; +fn main() { + test_function$0 +} +"#, + ); + } + + #[test] + fn associated_struct_function() { + check_assist( + qualify_path, + r#" +mod test_mod { + pub struct TestStruct {} + impl TestStruct { + pub fn test_function() {} + } +} + +fn main() { + TestStruct::test_function$0 +} +"#, + r#" +mod test_mod { + pub struct TestStruct {} + impl TestStruct { + pub fn test_function() {} + } +} + +fn main() { + test_mod::TestStruct::test_function +} +"#, + ); + } + + #[test] + fn associated_struct_const() { + cov_mark::check!(qualify_path_qualifier_start); + check_assist( + qualify_path, + r#" +mod test_mod { + pub struct TestStruct {} + impl TestStruct { + const TEST_CONST: u8 = 42; + } +} + +fn main() { + TestStruct::TEST_CONST$0 +} +"#, + r#" +mod test_mod { + pub struct TestStruct {} + impl TestStruct { + const TEST_CONST: u8 = 42; + } +} + +fn main() { + test_mod::TestStruct::TEST_CONST +} +"#, + ); + } + + #[test] + fn associated_struct_const_unqualified() { + // FIXME: non-trait assoc items completion is unsupported yet, see FIXME in the import_assets.rs for more details + check_assist_not_applicable( + qualify_path, + r#" +mod test_mod { + pub struct TestStruct {} + impl TestStruct { + const TEST_CONST: u8 = 42; + } +} + +fn main() { + TEST_CONST$0 +} +"#, + ); + } + + #[test] + fn associated_trait_function() { + check_assist( + qualify_path, + r#" +mod test_mod { + pub trait TestTrait { + fn test_function(); + } + pub struct TestStruct {} + impl TestTrait for TestStruct { + fn test_function() {} + } +} + +fn main() { + test_mod::TestStruct::test_function$0 +} +"#, + r#" +mod test_mod { + pub trait TestTrait { + fn test_function(); + } + pub struct TestStruct {} + impl TestTrait for TestStruct { + fn test_function() {} + } +} + +fn main() { + <test_mod::TestStruct as test_mod::TestTrait>::test_function +} +"#, + ); + } + + #[test] + fn not_applicable_for_imported_trait_for_function() { + check_assist_not_applicable( + qualify_path, + r#" +mod test_mod { + pub trait TestTrait { + fn test_function(); + } + pub trait TestTrait2 { + fn test_function(); + } + pub enum TestEnum { + One, + Two, + } + impl TestTrait2 for TestEnum { + fn test_function() {} + } + impl TestTrait for TestEnum { + fn test_function() {} + } +} + +use test_mod::TestTrait2; +fn main() { + test_mod::TestEnum::test_function$0; +} +"#, + ) + } + + #[test] + fn associated_trait_const() { + cov_mark::check!(qualify_path_trait_assoc_item); + check_assist( + qualify_path, + r#" +mod test_mod { + pub trait TestTrait { + const TEST_CONST: u8; + } + pub struct TestStruct {} + impl TestTrait for TestStruct { + const TEST_CONST: u8 = 42; + } +} + +fn main() { + test_mod::TestStruct::TEST_CONST$0 +} +"#, + r#" +mod test_mod { + pub trait TestTrait { + const TEST_CONST: u8; + } + pub struct TestStruct {} + impl TestTrait for TestStruct { + const TEST_CONST: u8 = 42; + } +} + +fn main() { + <test_mod::TestStruct as test_mod::TestTrait>::TEST_CONST +} +"#, + ); + } + + #[test] + fn not_applicable_for_imported_trait_for_const() { + check_assist_not_applicable( + qualify_path, + r#" +mod test_mod { + pub trait TestTrait { + const TEST_CONST: u8; + } + pub trait TestTrait2 { + const TEST_CONST: f64; + } + pub enum TestEnum { + One, + Two, + } + impl TestTrait2 for TestEnum { + const TEST_CONST: f64 = 42.0; + } + impl TestTrait for TestEnum { + const TEST_CONST: u8 = 42; + } +} + +use test_mod::TestTrait2; +fn main() { + test_mod::TestEnum::TEST_CONST$0; +} +"#, + ) + } + + #[test] + fn trait_method() { + cov_mark::check!(qualify_path_trait_method); + check_assist( + qualify_path, + r#" +mod test_mod { + pub trait TestTrait { + fn test_method(&self); + } + pub struct TestStruct {} + impl TestTrait for TestStruct { + fn test_method(&self) {} + } +} + +fn main() { + let test_struct = test_mod::TestStruct {}; + test_struct.test_meth$0od() +} +"#, + r#" +mod test_mod { + pub trait TestTrait { + fn test_method(&self); + } + pub struct TestStruct {} + impl TestTrait for TestStruct { + fn test_method(&self) {} + } +} + +fn main() { + let test_struct = test_mod::TestStruct {}; + test_mod::TestTrait::test_method(&test_struct) +} +"#, + ); + } + + #[test] + fn trait_method_multi_params() { + check_assist( + qualify_path, + r#" +mod test_mod { + pub trait TestTrait { + fn test_method(&self, test: i32); + } + pub struct TestStruct {} + impl TestTrait for TestStruct { + fn test_method(&self, test: i32) {} + } +} + +fn main() { + let test_struct = test_mod::TestStruct {}; + test_struct.test_meth$0od(42) +} +"#, + r#" +mod test_mod { + pub trait TestTrait { + fn test_method(&self, test: i32); + } + pub struct TestStruct {} + impl TestTrait for TestStruct { + fn test_method(&self, test: i32) {} + } +} + +fn main() { + let test_struct = test_mod::TestStruct {}; + test_mod::TestTrait::test_method(&test_struct, 42) +} +"#, + ); + } + + #[test] + fn trait_method_consume() { + check_assist( + qualify_path, + r#" +mod test_mod { + pub trait TestTrait { + fn test_method(self); + } + pub struct TestStruct {} + impl TestTrait for TestStruct { + fn test_method(self) {} + } +} + +fn main() { + let test_struct = test_mod::TestStruct {}; + test_struct.test_meth$0od() +} +"#, + r#" +mod test_mod { + pub trait TestTrait { + fn test_method(self); + } + pub struct TestStruct {} + impl TestTrait for TestStruct { + fn test_method(self) {} + } +} + +fn main() { + let test_struct = test_mod::TestStruct {}; + test_mod::TestTrait::test_method(test_struct) +} +"#, + ); + } + + #[test] + fn trait_method_cross_crate() { + check_assist( + qualify_path, + r#" +//- /main.rs crate:main deps:dep +fn main() { + let test_struct = dep::test_mod::TestStruct {}; + test_struct.test_meth$0od() +} +//- /dep.rs crate:dep +pub mod test_mod { + pub trait TestTrait { + fn test_method(&self); + } + pub struct TestStruct {} + impl TestTrait for TestStruct { + fn test_method(&self) {} + } +} +"#, + r#" +fn main() { + let test_struct = dep::test_mod::TestStruct {}; + dep::test_mod::TestTrait::test_method(&test_struct) +} +"#, + ); + } + + #[test] + fn assoc_fn_cross_crate() { + check_assist( + qualify_path, + r#" +//- /main.rs crate:main deps:dep +fn main() { + dep::test_mod::TestStruct::test_func$0tion +} +//- /dep.rs crate:dep +pub mod test_mod { + pub trait TestTrait { + fn test_function(); + } + pub struct TestStruct {} + impl TestTrait for TestStruct { + fn test_function() {} + } +} +"#, + r#" +fn main() { + <dep::test_mod::TestStruct as dep::test_mod::TestTrait>::test_function +} +"#, + ); + } + + #[test] + fn assoc_const_cross_crate() { + check_assist( + qualify_path, + r#" +//- /main.rs crate:main deps:dep +fn main() { + dep::test_mod::TestStruct::CONST$0 +} +//- /dep.rs crate:dep +pub mod test_mod { + pub trait TestTrait { + const CONST: bool; + } + pub struct TestStruct {} + impl TestTrait for TestStruct { + const CONST: bool = true; + } +} +"#, + r#" +fn main() { + <dep::test_mod::TestStruct as dep::test_mod::TestTrait>::CONST +} +"#, + ); + } + + #[test] + fn assoc_fn_as_method_cross_crate() { + check_assist_not_applicable( + qualify_path, + r#" +//- /main.rs crate:main deps:dep +fn main() { + let test_struct = dep::test_mod::TestStruct {}; + test_struct.test_func$0tion() +} +//- /dep.rs crate:dep +pub mod test_mod { + pub trait TestTrait { + fn test_function(); + } + pub struct TestStruct {} + impl TestTrait for TestStruct { + fn test_function() {} + } +} +"#, + ); + } + + #[test] + fn private_trait_cross_crate() { + check_assist_not_applicable( + qualify_path, + r#" +//- /main.rs crate:main deps:dep +fn main() { + let test_struct = dep::test_mod::TestStruct {}; + test_struct.test_meth$0od() +} +//- /dep.rs crate:dep +pub mod test_mod { + trait TestTrait { + fn test_method(&self); + } + pub struct TestStruct {} + impl TestTrait for TestStruct { + fn test_method(&self) {} + } +} +"#, + ); + } + + #[test] + fn not_applicable_for_imported_trait_for_method() { + check_assist_not_applicable( + qualify_path, + r#" +mod test_mod { + pub trait TestTrait { + fn test_method(&self); + } + pub trait TestTrait2 { + fn test_method(&self); + } + pub enum TestEnum { + One, + Two, + } + impl TestTrait2 for TestEnum { + fn test_method(&self) {} + } + impl TestTrait for TestEnum { + fn test_method(&self) {} + } +} + +use test_mod::TestTrait2; +fn main() { + let one = test_mod::TestEnum::One; + one.test$0_method(); +} +"#, + ) + } + + #[test] + fn dep_import() { + check_assist( + qualify_path, + r" +//- /lib.rs crate:dep +pub struct Struct; + +//- /main.rs crate:main deps:dep +fn main() { + Struct$0 +} +", + r" +fn main() { + dep::Struct +} +", + ); + } + + #[test] + fn whole_segment() { + // Tests that only imports whose last segment matches the identifier get suggested. + check_assist( + qualify_path, + r" +//- /lib.rs crate:dep +pub mod fmt { + pub trait Display {} +} + +pub fn panic_fmt() {} + +//- /main.rs crate:main deps:dep +struct S; + +impl f$0mt::Display for S {} +", + r" +struct S; + +impl dep::fmt::Display for S {} +", + ); + } + + #[test] + fn macro_generated() { + // Tests that macro-generated items are suggested from external crates. + check_assist( + qualify_path, + r" +//- /lib.rs crate:dep +macro_rules! mac { + () => { + pub struct Cheese; + }; +} + +mac!(); + +//- /main.rs crate:main deps:dep +fn main() { + Cheese$0; +} +", + r" +fn main() { + dep::Cheese; +} +", + ); + } + + #[test] + fn casing() { + // Tests that differently cased names don't interfere and we only suggest the matching one. + check_assist( + qualify_path, + r" +//- /lib.rs crate:dep +pub struct FMT; +pub struct fmt; + +//- /main.rs crate:main deps:dep +fn main() { + FMT$0; +} +", + r" +fn main() { + dep::FMT; +} +", + ); + } + + #[test] + fn keep_generic_annotations() { + check_assist( + qualify_path, + r" +//- /lib.rs crate:dep +pub mod generic { pub struct Thing<'a, T>(&'a T); } + +//- /main.rs crate:main deps:dep +fn foo() -> Thin$0g<'static, ()> {} + +fn main() {} +", + r" +fn foo() -> dep::generic::Thing<'static, ()> {} + +fn main() {} +", + ); + } + + #[test] + fn keep_generic_annotations_leading_colon() { + check_assist( + qualify_path, + r#" +//- /lib.rs crate:dep +pub mod generic { pub struct Thing<'a, T>(&'a T); } + +//- /main.rs crate:main deps:dep +fn foo() -> Thin$0g::<'static, ()> {} + +fn main() {} +"#, + r" +fn foo() -> dep::generic::Thing::<'static, ()> {} + +fn main() {} +", + ); + } + + #[test] + fn associated_struct_const_generic() { + check_assist( + qualify_path, + r#" +mod test_mod { + pub struct TestStruct<T> {} + impl<T> TestStruct<T> { + const TEST_CONST: u8 = 42; + } +} + +fn main() { + TestStruct::<()>::TEST_CONST$0 +} +"#, + r#" +mod test_mod { + pub struct TestStruct<T> {} + impl<T> TestStruct<T> { + const TEST_CONST: u8 = 42; + } +} + +fn main() { + test_mod::TestStruct::<()>::TEST_CONST +} +"#, + ); + } + + #[test] + fn associated_trait_const_generic() { + check_assist( + qualify_path, + r#" +mod test_mod { + pub trait TestTrait { + const TEST_CONST: u8; + } + pub struct TestStruct<T> {} + impl<T> TestTrait for TestStruct<T> { + const TEST_CONST: u8 = 42; + } +} + +fn main() { + test_mod::TestStruct::<()>::TEST_CONST$0 +} +"#, + r#" +mod test_mod { + pub trait TestTrait { + const TEST_CONST: u8; + } + pub struct TestStruct<T> {} + impl<T> TestTrait for TestStruct<T> { + const TEST_CONST: u8 = 42; + } +} + +fn main() { + <test_mod::TestStruct::<()> as test_mod::TestTrait>::TEST_CONST +} +"#, + ); + } + + #[test] + fn trait_method_generic() { + check_assist( + qualify_path, + r#" +mod test_mod { + pub trait TestTrait { + fn test_method<T>(&self); + } + pub struct TestStruct {} + impl TestTrait for TestStruct { + fn test_method<T>(&self) {} + } +} + +fn main() { + let test_struct = test_mod::TestStruct {}; + test_struct.test_meth$0od::<()>() +} +"#, + r#" +mod test_mod { + pub trait TestTrait { + fn test_method<T>(&self); + } + pub struct TestStruct {} + impl TestTrait for TestStruct { + fn test_method<T>(&self) {} + } +} + +fn main() { + let test_struct = test_mod::TestStruct {}; + test_mod::TestTrait::test_method::<()>(&test_struct) +} +"#, + ); + } + + #[test] + fn works_in_derives() { + check_assist( + qualify_path, + r#" +//- minicore:derive +mod foo { + #[rustc_builtin_macro] + pub macro Copy {} +} +#[derive(Copy$0)] +struct Foo; +"#, + r#" +mod foo { + #[rustc_builtin_macro] + pub macro Copy {} +} +#[derive(foo::Copy)] +struct Foo; +"#, + ); + } + + #[test] + fn works_in_use_start() { + check_assist( + qualify_path, + r#" +mod bar { + pub mod foo { + pub struct Foo; + } +} +use foo$0::Foo; +"#, + r#" +mod bar { + pub mod foo { + pub struct Foo; + } +} +use bar::foo::Foo; +"#, + ); + } + + #[test] + fn not_applicable_in_non_start_use() { + check_assist_not_applicable( + qualify_path, + r" +mod bar { + pub mod foo { + pub struct Foo; + } +} +use foo::Foo$0; +", + ); + } +} |