diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index e3a332d30a4..d5a3d9034c1 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -1114,6 +1114,7 @@ pub enum AssocItem {
     Const(Const),
     TypeAlias(TypeAlias),
 }
+#[derive(Debug)]
 pub enum AssocItemContainer {
     Trait(Trait),
     Impl(Impl),
@@ -2136,6 +2137,16 @@ impl ScopeDef {
     }
 }
 
+impl From<ItemInNs> for ScopeDef {
+    fn from(item: ItemInNs) -> Self {
+        match item {
+            ItemInNs::Types(id) => ScopeDef::ModuleDef(id.into()),
+            ItemInNs::Values(id) => ScopeDef::ModuleDef(id.into()),
+            ItemInNs::Macros(id) => ScopeDef::MacroDef(id.into()),
+        }
+    }
+}
+
 pub trait HasVisibility {
     fn visibility(&self, db: &dyn HirDatabase) -> Visibility;
     fn is_visible_from(&self, db: &dyn HirDatabase, module: Module) -> bool {
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index b600178ee15..f83ed65d5a6 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -478,7 +478,6 @@ impl Analysis {
         position: FilePosition,
         full_import_path: &str,
         imported_name: String,
-        import_for_trait_assoc_item: bool,
     ) -> Cancelable<Vec<TextEdit>> {
         Ok(self
             .with_db(|db| {
@@ -488,7 +487,6 @@ impl Analysis {
                     position,
                     full_import_path,
                     imported_name,
-                    import_for_trait_assoc_item,
                 )
             })?
             .unwrap_or_default())
diff --git a/crates/ide_assists/src/handlers/auto_import.rs b/crates/ide_assists/src/handlers/auto_import.rs
index 1422224ac0f..7caee8df04c 100644
--- a/crates/ide_assists/src/handlers/auto_import.rs
+++ b/crates/ide_assists/src/handlers/auto_import.rs
@@ -1,7 +1,7 @@
 use ide_db::helpers::{
     import_assets::{ImportAssets, ImportCandidate},
     insert_use::{insert_use, ImportScope},
-    mod_path_to_ast,
+    item_name, mod_path_to_ast,
 };
 use syntax::{ast, AstNode, SyntaxNode};
 
@@ -92,14 +92,19 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
     let range = ctx.sema.original_range(&syntax_under_caret).range;
     let group = import_group_message(import_assets.import_candidate());
     let scope = ImportScope::find_insert_use_container(&syntax_under_caret, &ctx.sema)?;
-    for (import, _) in proposed_imports {
+    for import in proposed_imports {
+        let name = match item_name(ctx.db(), import.original_item) {
+            Some(name) => name,
+            None => continue,
+        };
         acc.add_group(
             &group,
             AssistId("auto_import", AssistKind::QuickFix),
-            format!("Import `{}`", &import),
+            format!("Import `{}`", name),
             range,
             |builder| {
-                let rewriter = insert_use(&scope, mod_path_to_ast(&import), ctx.config.insert_use);
+                let rewriter =
+                    insert_use(&scope, mod_path_to_ast(&import.import_path), ctx.config.insert_use);
                 builder.rewrite(rewriter);
             },
         );
@@ -125,10 +130,10 @@ fn import_group_message(import_candidate: &ImportCandidate) -> GroupLabel {
     let name = match import_candidate {
         ImportCandidate::Path(candidate) => format!("Import {}", candidate.name.text()),
         ImportCandidate::TraitAssocItem(candidate) => {
-            format!("Import a trait for item {}", candidate.name.text())
+            format!("Import a trait for item {}", candidate.assoc_item_name.text())
         }
         ImportCandidate::TraitMethod(candidate) => {
-            format!("Import a trait for method {}", candidate.name.text())
+            format!("Import a trait for method {}", candidate.assoc_item_name.text())
         }
     };
     GroupLabel(name)
@@ -220,41 +225,6 @@ mod tests {
         );
     }
 
-    #[test]
-    fn auto_imports_are_merged() {
-        check_assist(
-            auto_import,
-            r"
-            use PubMod::PubStruct1;
-
-            struct Test {
-                test: Pub$0Struct2<u8>,
-            }
-
-            pub mod PubMod {
-                pub struct PubStruct1;
-                pub struct PubStruct2<T> {
-                    _t: T,
-                }
-            }
-            ",
-            r"
-            use PubMod::{PubStruct1, PubStruct2};
-
-            struct Test {
-                test: PubStruct2<u8>,
-            }
-
-            pub mod PubMod {
-                pub struct PubStruct1;
-                pub struct PubStruct2<T> {
-                    _t: T,
-                }
-            }
-            ",
-        );
-    }
-
     #[test]
     fn applicable_when_found_multiple_imports() {
         check_assist(
diff --git a/crates/ide_assists/src/handlers/qualify_path.rs b/crates/ide_assists/src/handlers/qualify_path.rs
index d3e34e5403a..272874ae390 100644
--- a/crates/ide_assists/src/handlers/qualify_path.rs
+++ b/crates/ide_assists/src/handlers/qualify_path.rs
@@ -1,7 +1,10 @@
 use std::iter;
 
 use hir::AsAssocItem;
-use ide_db::helpers::{import_assets::ImportCandidate, mod_path_to_ast};
+use ide_db::helpers::{
+    import_assets::{ImportCandidate, LocatedImport},
+    item_name, mod_path_to_ast,
+};
 use ide_db::RootDatabase;
 use syntax::{
     ast,
@@ -71,17 +74,17 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
     };
 
     let group_label = group_label(candidate);
-    for (import, item) in proposed_imports {
+    for import in proposed_imports {
         acc.add_group(
             &group_label,
             AssistId("qualify_path", AssistKind::QuickFix),
-            label(candidate, &import),
+            label(ctx.db(), candidate, &import),
             range,
             |builder| {
                 qualify_candidate.qualify(
                     |replace_with: String| builder.replace(range, replace_with),
-                    import,
-                    item,
+                    &import.import_path,
+                    import.item_to_import,
                 )
             },
         );
@@ -97,8 +100,13 @@ enum QualifyCandidate<'db> {
 }
 
 impl QualifyCandidate<'_> {
-    fn qualify(&self, mut replacer: impl FnMut(String), import: hir::ModPath, item: hir::ItemInNs) {
-        let import = mod_path_to_ast(&import);
+    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);
@@ -183,23 +191,29 @@ fn item_as_trait(db: &RootDatabase, item: hir::ItemInNs) -> Option<hir::Trait> {
 fn group_label(candidate: &ImportCandidate) -> GroupLabel {
     let name = match candidate {
         ImportCandidate::Path(it) => &it.name,
-        ImportCandidate::TraitAssocItem(it) | ImportCandidate::TraitMethod(it) => &it.name,
+        ImportCandidate::TraitAssocItem(it) | ImportCandidate::TraitMethod(it) => {
+            &it.assoc_item_name
+        }
     }
     .text();
     GroupLabel(format!("Qualify {}", name))
 }
 
-fn label(candidate: &ImportCandidate, import: &hir::ModPath) -> String {
+fn label(db: &RootDatabase, candidate: &ImportCandidate, import: &LocatedImport) -> String {
+    let display_path = match item_name(db, import.original_item) {
+        Some(display_path) => display_path.to_string(),
+        None => "{unknown}".to_string(),
+    };
     match candidate {
         ImportCandidate::Path(candidate) => {
             if candidate.qualifier.is_some() {
-                format!("Qualify with `{}`", &import)
+                format!("Qualify with `{}`", display_path)
             } else {
-                format!("Qualify as `{}`", &import)
+                format!("Qualify as `{}`", display_path)
             }
         }
-        ImportCandidate::TraitAssocItem(_) => format!("Qualify `{}`", &import),
-        ImportCandidate::TraitMethod(_) => format!("Qualify with cast as `{}`", &import),
+        ImportCandidate::TraitAssocItem(_) => format!("Qualify `{}`", display_path),
+        ImportCandidate::TraitMethod(_) => format!("Qualify with cast as `{}`", display_path),
     }
 }
 
diff --git a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs
index c69bc5cacd4..88fe2fe9047 100644
--- a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs
+++ b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs
@@ -1,5 +1,6 @@
+use hir::ModuleDef;
 use ide_db::helpers::mod_path_to_ast;
-use ide_db::imports_locator;
+use ide_db::items_locator;
 use itertools::Itertools;
 use syntax::{
     ast::{self, make, AstNode, NameOwner},
@@ -64,22 +65,20 @@ pub(crate) fn replace_derive_with_manual_impl(
     let current_module = ctx.sema.scope(annotated_name.syntax()).module()?;
     let current_crate = current_module.krate();
 
-    let found_traits = imports_locator::find_exact_imports(
-        &ctx.sema,
-        current_crate,
-        trait_token.text().to_string(),
-    )
-    .filter_map(|candidate: either::Either<hir::ModuleDef, hir::MacroDef>| match candidate {
-        either::Either::Left(hir::ModuleDef::Trait(trait_)) => Some(trait_),
-        _ => None,
-    })
-    .flat_map(|trait_| {
-        current_module
-            .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_))
-            .as_ref()
-            .map(mod_path_to_ast)
-            .zip(Some(trait_))
-    });
+    let found_traits =
+        items_locator::with_exact_name(&ctx.sema, current_crate, trait_token.text().to_string())
+            .into_iter()
+            .filter_map(|item| match ModuleDef::from(item.as_module_def_id()?) {
+                ModuleDef::Trait(trait_) => Some(trait_),
+                _ => None,
+            })
+            .flat_map(|trait_| {
+                current_module
+                    .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_))
+                    .as_ref()
+                    .map(mod_path_to_ast)
+                    .zip(Some(trait_))
+            });
 
     let mut no_traits_found = true;
     for (trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) {
diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs
index f34764b6103..391a11c91c9 100644
--- a/crates/ide_completion/src/completions/flyimport.rs
+++ b/crates/ide_completion/src/completions/flyimport.rs
@@ -21,6 +21,46 @@
 //! ```
 //!
 //! Also completes associated items, that require trait imports.
+//! If any unresolved and/or partially-qualified path predeces the input, it will be taken into account.
+//! Currently, only the imports with their import path ending with the whole qialifier will be proposed
+//! (no fuzzy matching for qualifier).
+//!
+//! ```
+//! mod foo {
+//!     pub mod bar {
+//!         pub struct Item;
+//!
+//!         impl Item {
+//!             pub const TEST_ASSOC: usize = 3;
+//!         }
+//!     }
+//! }
+//!
+//! fn main() {
+//!     bar::Item::TEST_A$0
+//! }
+//! ```
+//! ->
+//! ```
+//! use foo::bar;
+//!
+//! mod foo {
+//!     pub mod bar {
+//!         pub struct Item;
+//!
+//!         impl Item {
+//!             pub const TEST_ASSOC: usize = 3;
+//!         }
+//!     }
+//! }
+//!
+//! fn main() {
+//!     bar::Item::TEST_ASSOC
+//! }
+//! ```
+//!
+//! NOTE: currently, if an assoc item comes from a trait that's not currently imported and it also has an unresolved and/or partially-qualified path,
+//! no imports will be proposed.
 //!
 //! .Fuzzy search details
 //!
@@ -48,12 +88,12 @@
 //! Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding
 //! capability enabled.
 
-use hir::{AsAssocItem, ModPath, ScopeDef};
+use hir::ModPath;
 use ide_db::helpers::{
     import_assets::{ImportAssets, ImportCandidate},
     insert_use::ImportScope,
 };
-use rustc_hash::FxHashSet;
+use itertools::Itertools;
 use syntax::{AstNode, SyntaxNode, T};
 
 use crate::{
@@ -92,50 +132,26 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
         &ctx.sema,
     )?;
 
-    let scope_definitions = scope_definitions(ctx);
-    let mut all_mod_paths = import_assets
-        .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind)
-        .into_iter()
-        .map(|(mod_path, item_in_ns)| {
-            let scope_item = match item_in_ns {
-                hir::ItemInNs::Types(id) => ScopeDef::ModuleDef(id.into()),
-                hir::ItemInNs::Values(id) => ScopeDef::ModuleDef(id.into()),
-                hir::ItemInNs::Macros(id) => ScopeDef::MacroDef(id.into()),
-            };
-            (mod_path, scope_item)
-        })
-        .filter(|(_, proposed_def)| !scope_definitions.contains(proposed_def))
-        .collect::<Vec<_>>();
-    all_mod_paths.sort_by_cached_key(|(mod_path, _)| {
-        compute_fuzzy_completion_order_key(mod_path, &user_input_lowercased)
-    });
-
-    acc.add_all(all_mod_paths.into_iter().filter_map(|(import_path, definition)| {
-        let import_for_trait_assoc_item = match definition {
-            ScopeDef::ModuleDef(module_def) => module_def
-                .as_assoc_item(ctx.db)
-                .and_then(|assoc| assoc.containing_trait(ctx.db))
-                .is_some(),
-            _ => false,
-        };
-        let import_edit = ImportEdit {
-            import_path,
-            import_scope: import_scope.clone(),
-            import_for_trait_assoc_item,
-        };
-        render_resolution_with_import(RenderContext::new(ctx), import_edit, &definition)
-    }));
+    acc.add_all(
+        import_assets
+            .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind)
+            .into_iter()
+            .sorted_by_key(|located_import| {
+                compute_fuzzy_completion_order_key(
+                    &located_import.import_path,
+                    &user_input_lowercased,
+                )
+            })
+            .filter_map(|import| {
+                render_resolution_with_import(
+                    RenderContext::new(ctx),
+                    ImportEdit { import, scope: import_scope.clone() },
+                )
+            }),
+    );
     Some(())
 }
 
-fn scope_definitions(ctx: &CompletionContext) -> FxHashSet<ScopeDef> {
-    let mut scope_definitions = FxHashSet::default();
-    ctx.scope.process_all_names(&mut |_, scope_def| {
-        scope_definitions.insert(scope_def);
-    });
-    scope_definitions
-}
-
 pub(crate) fn position_for_import<'a>(
     ctx: &'a CompletionContext,
     import_candidate: Option<&ImportCandidate>,
@@ -160,23 +176,30 @@ fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAs
             current_module,
             ctx.sema.type_of_expr(dot_receiver)?,
             fuzzy_name,
+            dot_receiver.syntax().clone(),
         )
     } else {
         let fuzzy_name_length = fuzzy_name.len();
+        let approximate_node = match current_module.definition_source(ctx.db).value {
+            hir::ModuleSource::SourceFile(s) => s.syntax().clone(),
+            hir::ModuleSource::Module(m) => m.syntax().clone(),
+            hir::ModuleSource::BlockExpr(b) => b.syntax().clone(),
+        };
         let assets_for_path = ImportAssets::for_fuzzy_path(
             current_module,
             ctx.path_qual.clone(),
             fuzzy_name,
             &ctx.sema,
-        );
+            approximate_node,
+        )?;
 
-        if matches!(assets_for_path.as_ref()?.import_candidate(), ImportCandidate::Path(_))
+        if matches!(assets_for_path.import_candidate(), ImportCandidate::Path(_))
             && fuzzy_name_length < 2
         {
             cov_mark::hit!(ignore_short_input_for_path);
             None
         } else {
-            assets_for_path
+            Some(assets_for_path)
         }
     }
 }
@@ -186,11 +209,11 @@ fn compute_fuzzy_completion_order_key(
     user_input_lowercased: &str,
 ) -> usize {
     cov_mark::hit!(certain_fuzzy_order_test);
-    let proposed_import_name = match proposed_mod_path.segments().last() {
+    let import_name = match proposed_mod_path.segments().last() {
         Some(name) => name.to_string().to_lowercase(),
         None => return usize::MAX,
     };
-    match proposed_import_name.match_indices(user_input_lowercased).next() {
+    match import_name.match_indices(user_input_lowercased).next() {
         Some((first_matching_index, _)) => first_matching_index,
         None => usize::MAX,
     }
@@ -773,4 +796,155 @@ fn main() {
 }"#,
         );
     }
+
+    #[test]
+    fn unresolved_qualifier() {
+        let fixture = r#"
+mod foo {
+    pub mod bar {
+        pub mod baz {
+            pub struct Item;
+        }
+    }
+}
+
+fn main() {
+    bar::baz::Ite$0
+}"#;
+
+        check(
+            fixture,
+            expect![[r#"
+        st foo::bar::baz::Item
+        "#]],
+        );
+
+        check_edit(
+            "Item",
+            fixture,
+            r#"
+        use foo::bar;
+
+        mod foo {
+            pub mod bar {
+                pub mod baz {
+                    pub struct Item;
+                }
+            }
+        }
+
+        fn main() {
+            bar::baz::Item
+        }"#,
+        );
+    }
+
+    #[test]
+    fn unresolved_assoc_item_container() {
+        let fixture = r#"
+mod foo {
+    pub struct Item;
+
+    impl Item {
+        pub const TEST_ASSOC: usize = 3;
+    }
+}
+
+fn main() {
+    Item::TEST_A$0
+}"#;
+
+        check(
+            fixture,
+            expect![[r#"
+        ct TEST_ASSOC (foo::Item)
+        "#]],
+        );
+
+        check_edit(
+            "TEST_ASSOC",
+            fixture,
+            r#"
+use foo::Item;
+
+mod foo {
+    pub struct Item;
+
+    impl Item {
+        pub const TEST_ASSOC: usize = 3;
+    }
+}
+
+fn main() {
+    Item::TEST_ASSOC
+}"#,
+        );
+    }
+
+    #[test]
+    fn unresolved_assoc_item_container_with_path() {
+        let fixture = r#"
+mod foo {
+    pub mod bar {
+        pub struct Item;
+
+        impl Item {
+            pub const TEST_ASSOC: usize = 3;
+        }
+    }
+}
+
+fn main() {
+    bar::Item::TEST_A$0
+}"#;
+
+        check(
+            fixture,
+            expect![[r#"
+        ct TEST_ASSOC (foo::bar::Item)
+    "#]],
+        );
+
+        check_edit(
+            "TEST_ASSOC",
+            fixture,
+            r#"
+use foo::bar;
+
+mod foo {
+    pub mod bar {
+        pub struct Item;
+
+        impl Item {
+            pub const TEST_ASSOC: usize = 3;
+        }
+    }
+}
+
+fn main() {
+    bar::Item::TEST_ASSOC
+}"#,
+        );
+    }
+
+    #[test]
+    fn fuzzy_unresolved_path() {
+        check(
+            r#"
+mod foo {
+    pub mod bar {
+        pub struct Item;
+
+        impl Item {
+            pub const TEST_ASSOC: usize = 3;
+        }
+    }
+}
+
+fn main() {
+    bar::Ass$0
+}"#,
+            expect![[]],
+        )
+    }
 }
diff --git a/crates/ide_completion/src/item.rs b/crates/ide_completion/src/item.rs
index 9b2435c4b6c..9b039e3e5f0 100644
--- a/crates/ide_completion/src/item.rs
+++ b/crates/ide_completion/src/item.rs
@@ -2,15 +2,16 @@
 
 use std::fmt;
 
-use hir::{Documentation, ModPath, Mutability};
+use hir::{Documentation, Mutability};
 use ide_db::{
     helpers::{
+        import_assets::LocatedImport,
         insert_use::{self, ImportScope, InsertUseConfig},
         mod_path_to_ast, SnippetCap,
     },
     SymbolKind,
 };
-use stdx::{impl_from, never};
+use stdx::{format_to, impl_from, never};
 use syntax::{algo, TextRange};
 use text_edit::TextEdit;
 
@@ -272,9 +273,8 @@ impl CompletionItem {
 /// An extra import to add after the completion is applied.
 #[derive(Debug, Clone)]
 pub struct ImportEdit {
-    pub import_path: ModPath,
-    pub import_scope: ImportScope,
-    pub import_for_trait_assoc_item: bool,
+    pub import: LocatedImport,
+    pub scope: ImportScope,
 }
 
 impl ImportEdit {
@@ -284,7 +284,7 @@ impl ImportEdit {
         let _p = profile::span("ImportEdit::to_text_edit");
 
         let rewriter =
-            insert_use::insert_use(&self.import_scope, mod_path_to_ast(&self.import_path), cfg);
+            insert_use::insert_use(&self.scope, mod_path_to_ast(&self.import.import_path), cfg);
         let old_ast = rewriter.rewrite_root()?;
         let mut import_insert = TextEdit::builder();
         algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut import_insert);
@@ -322,20 +322,19 @@ impl Builder {
         let mut lookup = self.lookup;
         let mut insert_text = self.insert_text;
 
-        if let Some(import_to_add) = self.import_to_add.as_ref() {
-            if import_to_add.import_for_trait_assoc_item {
-                lookup = lookup.or_else(|| Some(label.clone()));
-                insert_text = insert_text.or_else(|| Some(label.clone()));
-                label = format!("{} ({})", label, import_to_add.import_path);
-            } else {
-                let mut import_path_without_last_segment = import_to_add.import_path.to_owned();
-                let _ = import_path_without_last_segment.pop_segment();
+        if let Some(original_path) = self
+            .import_to_add
+            .as_ref()
+            .and_then(|import_edit| import_edit.import.original_path.as_ref())
+        {
+            lookup = lookup.or_else(|| Some(label.clone()));
+            insert_text = insert_text.or_else(|| Some(label.clone()));
 
-                if !import_path_without_last_segment.segments().is_empty() {
-                    lookup = lookup.or_else(|| Some(label.clone()));
-                    insert_text = insert_text.or_else(|| Some(label.clone()));
-                    label = format!("{}::{}", import_path_without_last_segment, label);
-                }
+            let original_path_label = original_path.to_string();
+            if original_path_label.ends_with(&label) {
+                label = original_path_label;
+            } else {
+                format_to!(label, " ({})", original_path)
             }
         }
 
@@ -439,9 +438,3 @@ impl Builder {
         self
     }
 }
-
-impl<'a> Into<CompletionItem> for Builder {
-    fn into(self) -> CompletionItem {
-        self.build()
-    }
-}
diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs
index b0b809791e3..a0c8c374d0d 100644
--- a/crates/ide_completion/src/lib.rs
+++ b/crates/ide_completion/src/lib.rs
@@ -13,7 +13,9 @@ mod completions;
 
 use completions::flyimport::position_for_import;
 use ide_db::{
-    base_db::FilePosition, helpers::insert_use::ImportScope, imports_locator, RootDatabase,
+    base_db::FilePosition,
+    helpers::{import_assets::LocatedImport, insert_use::ImportScope},
+    items_locator, RootDatabase,
 };
 use text_edit::TextEdit;
 
@@ -139,25 +141,27 @@ pub fn resolve_completion_edits(
     position: FilePosition,
     full_import_path: &str,
     imported_name: String,
-    import_for_trait_assoc_item: bool,
 ) -> Option<Vec<TextEdit>> {
     let ctx = CompletionContext::new(db, position, config)?;
     let position_for_import = position_for_import(&ctx, None)?;
-    let import_scope = ImportScope::find_insert_use_container(position_for_import, &ctx.sema)?;
+    let scope = ImportScope::find_insert_use_container(position_for_import, &ctx.sema)?;
 
     let current_module = ctx.sema.scope(position_for_import).module()?;
     let current_crate = current_module.krate();
 
-    let import_path = imports_locator::find_exact_imports(&ctx.sema, current_crate, imported_name)
-        .filter_map(|candidate| {
-            let item: hir::ItemInNs = candidate.either(Into::into, Into::into);
-            current_module.find_use_path_prefixed(db, item, config.insert_use.prefix_kind)
-        })
-        .find(|mod_path| mod_path.to_string() == full_import_path)?;
+    let (import_path, item_to_import) =
+        items_locator::with_exact_name(&ctx.sema, current_crate, imported_name)
+            .into_iter()
+            .filter_map(|candidate| {
+                current_module
+                    .find_use_path_prefixed(db, candidate, config.insert_use.prefix_kind)
+                    .zip(Some(candidate))
+            })
+            .find(|(mod_path, _)| mod_path.to_string() == full_import_path)?;
+    let import =
+        LocatedImport::new(import_path.clone(), item_to_import, item_to_import, Some(import_path));
 
-    ImportEdit { import_path, import_scope, import_for_trait_assoc_item }
-        .to_text_edit(config.insert_use)
-        .map(|edit| vec![edit])
+    ImportEdit { import, scope }.to_text_edit(config.insert_use).map(|edit| vec![edit])
 }
 
 #[cfg(test)]
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs
index dcfac23c55f..fae5685e227 100644
--- a/crates/ide_completion/src/render.rs
+++ b/crates/ide_completion/src/render.rs
@@ -13,7 +13,10 @@ mod builder_ext;
 use hir::{
     AsAssocItem, Documentation, HasAttrs, HirDisplay, ModuleDef, Mutability, ScopeDef, Type,
 };
-use ide_db::{helpers::SnippetCap, RootDatabase, SymbolKind};
+use ide_db::{
+    helpers::{item_name, SnippetCap},
+    RootDatabase, SymbolKind,
+};
 use syntax::TextRange;
 
 use crate::{
@@ -50,18 +53,20 @@ pub(crate) fn render_resolution<'a>(
 pub(crate) fn render_resolution_with_import<'a>(
     ctx: RenderContext<'a>,
     import_edit: ImportEdit,
-    resolution: &ScopeDef,
 ) -> Option<CompletionItem> {
+    let resolution = ScopeDef::from(import_edit.import.original_item);
     let local_name = match resolution {
         ScopeDef::ModuleDef(ModuleDef::Function(f)) => f.name(ctx.completion.db).to_string(),
         ScopeDef::ModuleDef(ModuleDef::Const(c)) => c.name(ctx.completion.db)?.to_string(),
         ScopeDef::ModuleDef(ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db).to_string(),
-        _ => import_edit.import_path.segments().last()?.to_string(),
+        _ => item_name(ctx.db(), import_edit.import.original_item)?.to_string(),
     };
-    Render::new(ctx).render_resolution(local_name, Some(import_edit), resolution).map(|mut item| {
-        item.completion_kind = CompletionKind::Magic;
-        item
-    })
+    Render::new(ctx).render_resolution(local_name, Some(import_edit), &resolution).map(
+        |mut item| {
+            item.completion_kind = CompletionKind::Magic;
+            item
+        },
+    )
 }
 
 /// Interface for data and methods required for items rendering.
diff --git a/crates/ide_db/src/helpers.rs b/crates/ide_db/src/helpers.rs
index 3ff77400bbf..3c95d3cffa7 100644
--- a/crates/ide_db/src/helpers.rs
+++ b/crates/ide_db/src/helpers.rs
@@ -2,11 +2,19 @@
 pub mod insert_use;
 pub mod import_assets;
 
-use hir::{Crate, Enum, Module, ScopeDef, Semantics, Trait};
+use hir::{Crate, Enum, ItemInNs, MacroDef, Module, ModuleDef, Name, ScopeDef, Semantics, Trait};
 use syntax::ast::{self, make};
 
 use crate::RootDatabase;
 
+pub fn item_name(db: &RootDatabase, item: ItemInNs) -> Option<Name> {
+    match item {
+        ItemInNs::Types(module_def_id) => ModuleDef::from(module_def_id).name(db),
+        ItemInNs::Values(module_def_id) => ModuleDef::from(module_def_id).name(db),
+        ItemInNs::Macros(macro_def_id) => MacroDef::from(macro_def_id).name(db),
+    }
+}
+
 /// Converts the mod path struct into its ast representation.
 pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path {
     let _p = profile::span("mod_path_to_ast");
diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs
index 517abbb4bc0..e03ccd35155 100644
--- a/crates/ide_db/src/helpers/import_assets.rs
+++ b/crates/ide_db/src/helpers/import_assets.rs
@@ -1,19 +1,29 @@
 //! Look up accessible paths for items.
-use either::Either;
-use hir::{AsAssocItem, AssocItem, Crate, MacroDef, Module, ModuleDef, PrefixKind, Semantics};
+use hir::{
+    AsAssocItem, AssocItem, AssocItemContainer, Crate, ItemInNs, MacroDef, ModPath, Module,
+    ModuleDef, PathResolution, PrefixKind, ScopeDef, Semantics, Type,
+};
+use itertools::Itertools;
 use rustc_hash::FxHashSet;
-use syntax::{ast, AstNode};
+use syntax::{ast, utils::path_to_string_stripping_turbo_fish, AstNode, SyntaxNode};
 
 use crate::{
-    imports_locator::{self, AssocItemSearch, DEFAULT_QUERY_SEARCH_LIMIT},
+    items_locator::{self, AssocItemSearch, DEFAULT_QUERY_SEARCH_LIMIT},
     RootDatabase,
 };
 
+use super::item_name;
+
+/// A candidate for import, derived during various IDE activities:
+/// * completion with imports on the fly proposals
+/// * completion edit resolve requests
+/// * assists
+/// * etc.
 #[derive(Debug)]
 pub enum ImportCandidate {
-    // A path, qualified (`std::collections::HashMap`) or not (`HashMap`).
+    /// A path, qualified (`std::collections::HashMap`) or not (`HashMap`).
     Path(PathImportCandidate),
-    /// A trait associated function (with no self parameter) or associated constant.
+    /// A trait associated function (with no self parameter) or an associated constant.
     /// For 'test_mod::TestEnum::test_function', `ty` is the `test_mod::TestEnum` expression type
     /// and `name` is the `test_function`
     TraitAssocItem(TraitImportCandidate),
@@ -23,21 +33,40 @@ pub enum ImportCandidate {
     TraitMethod(TraitImportCandidate),
 }
 
+/// A trait import needed for a given associated item access.
+/// For `some::path::SomeStruct::ASSOC_`, contains the
+/// type of `some::path::SomeStruct` and `ASSOC_` as the item name.
 #[derive(Debug)]
 pub struct TraitImportCandidate {
-    pub receiver_ty: hir::Type,
-    pub name: NameToImport,
+    /// A type of the item that has the associated item accessed at.
+    pub receiver_ty: Type,
+    /// The associated item name that the trait to import should contain.
+    pub assoc_item_name: NameToImport,
 }
 
+/// Path import for a given name, qualified or not.
 #[derive(Debug)]
 pub struct PathImportCandidate {
-    pub qualifier: Option<ast::Path>,
+    /// Optional qualifier before name.
+    pub qualifier: Option<FirstSegmentUnresolved>,
+    /// The name the item (struct, trait, enum, etc.) should have.
     pub name: NameToImport,
 }
 
+/// A qualifier that has a first segment and it's unresolved.
+#[derive(Debug)]
+pub struct FirstSegmentUnresolved {
+    fist_segment: ast::NameRef,
+    full_qualifier: ast::Path,
+}
+
+/// A name that will be used during item lookups.
 #[derive(Debug)]
 pub enum NameToImport {
+    /// Requires items with names that exactly match the given string, case-sensitive.
     Exact(String),
+    /// Requires items with names that case-insensitively contain all letters from the string,
+    /// in the same order, but not necessary adjacent.
     Fuzzy(String),
 }
 
@@ -50,10 +79,12 @@ impl NameToImport {
     }
 }
 
+/// A struct to find imports in the project, given a certain name (or its part) and the context.
 #[derive(Debug)]
 pub struct ImportAssets {
     import_candidate: ImportCandidate,
-    module_with_candidate: hir::Module,
+    candidate_node: SyntaxNode,
+    module_with_candidate: Module,
 }
 
 impl ImportAssets {
@@ -61,9 +92,11 @@ impl ImportAssets {
         method_call: &ast::MethodCallExpr,
         sema: &Semantics<RootDatabase>,
     ) -> Option<Self> {
+        let candidate_node = method_call.syntax().clone();
         Some(Self {
             import_candidate: ImportCandidate::for_method_call(sema, method_call)?,
-            module_with_candidate: sema.scope(method_call.syntax()).module()?,
+            module_with_candidate: sema.scope(&candidate_node).module()?,
+            candidate_node,
         })
     }
 
@@ -71,94 +104,94 @@ impl ImportAssets {
         fully_qualified_path: &ast::Path,
         sema: &Semantics<RootDatabase>,
     ) -> Option<Self> {
-        let syntax_under_caret = fully_qualified_path.syntax();
-        if syntax_under_caret.ancestors().find_map(ast::Use::cast).is_some() {
+        let candidate_node = fully_qualified_path.syntax().clone();
+        if candidate_node.ancestors().find_map(ast::Use::cast).is_some() {
             return None;
         }
         Some(Self {
             import_candidate: ImportCandidate::for_regular_path(sema, fully_qualified_path)?,
-            module_with_candidate: sema.scope(syntax_under_caret).module()?,
+            module_with_candidate: sema.scope(&candidate_node).module()?,
+            candidate_node,
         })
     }
 
     pub fn for_fuzzy_path(
-        module_with_path: Module,
+        module_with_candidate: Module,
         qualifier: Option<ast::Path>,
         fuzzy_name: String,
         sema: &Semantics<RootDatabase>,
+        candidate_node: SyntaxNode,
     ) -> Option<Self> {
-        Some(match qualifier {
-            Some(qualifier) => {
-                let qualifier_resolution = sema.resolve_path(&qualifier)?;
-                match qualifier_resolution {
-                    hir::PathResolution::Def(hir::ModuleDef::Adt(assoc_item_path)) => Self {
-                        import_candidate: ImportCandidate::TraitAssocItem(TraitImportCandidate {
-                            receiver_ty: assoc_item_path.ty(sema.db),
-                            name: NameToImport::Fuzzy(fuzzy_name),
-                        }),
-                        module_with_candidate: module_with_path,
-                    },
-                    _ => Self {
-                        import_candidate: ImportCandidate::Path(PathImportCandidate {
-                            qualifier: Some(qualifier),
-                            name: NameToImport::Fuzzy(fuzzy_name),
-                        }),
-                        module_with_candidate: module_with_path,
-                    },
-                }
-            }
-            None => Self {
-                import_candidate: ImportCandidate::Path(PathImportCandidate {
-                    qualifier: None,
-                    name: NameToImport::Fuzzy(fuzzy_name),
-                }),
-                module_with_candidate: module_with_path,
-            },
+        Some(Self {
+            import_candidate: ImportCandidate::for_fuzzy_path(qualifier, fuzzy_name, sema)?,
+            module_with_candidate,
+            candidate_node,
         })
     }
 
     pub fn for_fuzzy_method_call(
         module_with_method_call: Module,
-        receiver_ty: hir::Type,
+        receiver_ty: Type,
         fuzzy_method_name: String,
+        candidate_node: SyntaxNode,
     ) -> Option<Self> {
         Some(Self {
             import_candidate: ImportCandidate::TraitMethod(TraitImportCandidate {
                 receiver_ty,
-                name: NameToImport::Fuzzy(fuzzy_method_name),
+                assoc_item_name: NameToImport::Fuzzy(fuzzy_method_name),
             }),
             module_with_candidate: module_with_method_call,
+            candidate_node,
         })
     }
 }
 
+/// An import (not necessary the only one) that corresponds a certain given [`PathImportCandidate`].
+/// (the structure is not entirely correct, since there can be situations requiring two imports, see FIXME below for the details)
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct LocatedImport {
+    /// The path to use in the `use` statement for a given candidate to be imported.
+    pub import_path: ModPath,
+    /// An item that will be imported with the import path given.
+    pub item_to_import: ItemInNs,
+    /// The path import candidate, resolved.
+    ///
+    /// Not necessary matches the import:
+    /// For any associated constant from the trait, we try to access as `some::path::SomeStruct::ASSOC_`
+    /// the original item is the associated constant, but the import has to be a trait that
+    /// defines this constant.
+    pub original_item: ItemInNs,
+    /// A path of the original item.
+    pub original_path: Option<ModPath>,
+}
+
+impl LocatedImport {
+    pub fn new(
+        import_path: ModPath,
+        item_to_import: ItemInNs,
+        original_item: ItemInNs,
+        original_path: Option<ModPath>,
+    ) -> Self {
+        Self { import_path, item_to_import, original_item, original_path }
+    }
+}
+
 impl ImportAssets {
     pub fn import_candidate(&self) -> &ImportCandidate {
         &self.import_candidate
     }
 
-    fn name_to_import(&self) -> &NameToImport {
-        match &self.import_candidate {
-            ImportCandidate::Path(candidate) => &candidate.name,
-            ImportCandidate::TraitAssocItem(candidate)
-            | ImportCandidate::TraitMethod(candidate) => &candidate.name,
-        }
-    }
-
     pub fn search_for_imports(
         &self,
         sema: &Semantics<RootDatabase>,
         prefix_kind: PrefixKind,
-    ) -> Vec<(hir::ModPath, hir::ItemInNs)> {
+    ) -> Vec<LocatedImport> {
         let _p = profile::span("import_assets::search_for_imports");
         self.search_for(sema, Some(prefix_kind))
     }
 
     /// This may return non-absolute paths if a part of the returned path is already imported into scope.
-    pub fn search_for_relative_paths(
-        &self,
-        sema: &Semantics<RootDatabase>,
-    ) -> Vec<(hir::ModPath, hir::ItemInNs)> {
+    pub fn search_for_relative_paths(&self, sema: &Semantics<RootDatabase>) -> Vec<LocatedImport> {
         let _p = profile::span("import_assets::search_for_relative_paths");
         self.search_for(sema, None)
     }
@@ -166,29 +199,29 @@ impl ImportAssets {
     fn search_for(
         &self,
         sema: &Semantics<RootDatabase>,
-        prefixed: Option<hir::PrefixKind>,
-    ) -> Vec<(hir::ModPath, hir::ItemInNs)> {
-        let current_crate = self.module_with_candidate.krate();
-
-        let unfiltered_imports = match self.name_to_import() {
-            NameToImport::Exact(exact_name) => {
-                imports_locator::find_exact_imports(sema, current_crate, exact_name.clone())
-            }
+        prefixed: Option<PrefixKind>,
+    ) -> Vec<LocatedImport> {
+        let items_with_candidate_name = match self.name_to_import() {
+            NameToImport::Exact(exact_name) => items_locator::with_exact_name(
+                sema,
+                self.module_with_candidate.krate(),
+                exact_name.clone(),
+            ),
             // FIXME: ideally, we should avoid using `fst` for seacrhing trait imports for assoc items:
             // instead, we need to look up all trait impls for a certain struct and search through them only
             // see https://github.com/rust-analyzer/rust-analyzer/pull/7293#issuecomment-761585032
             // and https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0/topic/Blanket.20trait.20impls.20lookup
             // for the details
             NameToImport::Fuzzy(fuzzy_name) => {
-                let (assoc_item_search, limit) = match self.import_candidate {
-                    ImportCandidate::TraitAssocItem(_) | ImportCandidate::TraitMethod(_) => {
-                        (AssocItemSearch::AssocItemsOnly, None)
-                    }
-                    _ => (AssocItemSearch::Exclude, Some(DEFAULT_QUERY_SEARCH_LIMIT)),
+                let (assoc_item_search, limit) = if self.import_candidate.is_trait_candidate() {
+                    (AssocItemSearch::AssocItemsOnly, None)
+                } else {
+                    (AssocItemSearch::Include, Some(DEFAULT_QUERY_SEARCH_LIMIT))
                 };
-                imports_locator::find_similar_imports(
+
+                items_locator::with_similar_name(
                     sema,
-                    current_crate,
+                    self.module_with_candidate.krate(),
                     fuzzy_name.clone(),
                     assoc_item_search,
                     limit,
@@ -196,63 +229,224 @@ impl ImportAssets {
             }
         };
 
-        let db = sema.db;
-        let mut res =
-            applicable_defs(self.import_candidate(), current_crate, db, unfiltered_imports)
-                .filter_map(|candidate| {
-                    let item: hir::ItemInNs = candidate.clone().either(Into::into, Into::into);
+        let scope_definitions = self.scope_definitions(sema);
+        self.applicable_defs(sema.db, prefixed, items_with_candidate_name)
+            .into_iter()
+            .filter(|import| import.import_path.len() > 1)
+            .filter(|import| !scope_definitions.contains(&ScopeDef::from(import.item_to_import)))
+            .sorted_by_key(|import| import.import_path.clone())
+            .collect()
+    }
 
-                    let item_to_search = match self.import_candidate {
-                        ImportCandidate::TraitAssocItem(_) | ImportCandidate::TraitMethod(_) => {
-                            let canidate_trait = match candidate {
-                                Either::Left(module_def) => {
-                                    module_def.as_assoc_item(db)?.containing_trait(db)
-                                }
-                                _ => None,
-                            }?;
-                            ModuleDef::from(canidate_trait).into()
-                        }
-                        _ => item,
-                    };
+    fn scope_definitions(&self, sema: &Semantics<RootDatabase>) -> FxHashSet<ScopeDef> {
+        let mut scope_definitions = FxHashSet::default();
+        sema.scope(&self.candidate_node).process_all_names(&mut |_, scope_def| {
+            scope_definitions.insert(scope_def);
+        });
+        scope_definitions
+    }
 
-                    if let Some(prefix_kind) = prefixed {
-                        self.module_with_candidate.find_use_path_prefixed(
-                            db,
-                            item_to_search,
-                            prefix_kind,
-                        )
-                    } else {
-                        self.module_with_candidate.find_use_path(db, item_to_search)
-                    }
-                    .map(|path| (path, item))
-                })
-                .filter(|(use_path, _)| use_path.len() > 1)
-                .collect::<Vec<_>>();
-        res.sort_by_cached_key(|(path, _)| path.clone());
-        res
+    fn name_to_import(&self) -> &NameToImport {
+        match &self.import_candidate {
+            ImportCandidate::Path(candidate) => &candidate.name,
+            ImportCandidate::TraitAssocItem(candidate)
+            | ImportCandidate::TraitMethod(candidate) => &candidate.assoc_item_name,
+        }
+    }
+
+    fn applicable_defs(
+        &self,
+        db: &RootDatabase,
+        prefixed: Option<PrefixKind>,
+        items_with_candidate_name: FxHashSet<ItemInNs>,
+    ) -> FxHashSet<LocatedImport> {
+        let _p = profile::span("import_assets::applicable_defs");
+        let current_crate = self.module_with_candidate.krate();
+
+        let mod_path = |item| {
+            get_mod_path(db, item_for_path_search(db, item)?, &self.module_with_candidate, prefixed)
+        };
+
+        match &self.import_candidate {
+            ImportCandidate::Path(path_candidate) => {
+                path_applicable_imports(db, path_candidate, mod_path, items_with_candidate_name)
+            }
+            ImportCandidate::TraitAssocItem(trait_candidate) => trait_applicable_items(
+                db,
+                current_crate,
+                trait_candidate,
+                true,
+                mod_path,
+                items_with_candidate_name,
+            ),
+            ImportCandidate::TraitMethod(trait_candidate) => trait_applicable_items(
+                db,
+                current_crate,
+                trait_candidate,
+                false,
+                mod_path,
+                items_with_candidate_name,
+            ),
+        }
     }
 }
 
-fn applicable_defs<'a>(
-    import_candidate: &ImportCandidate,
-    current_crate: Crate,
+fn path_applicable_imports(
     db: &RootDatabase,
-    unfiltered_imports: Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a>,
-) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a> {
-    let receiver_ty = match import_candidate {
-        ImportCandidate::Path(_) => return unfiltered_imports,
-        ImportCandidate::TraitAssocItem(candidate) | ImportCandidate::TraitMethod(candidate) => {
-            &candidate.receiver_ty
+    path_candidate: &PathImportCandidate,
+    mod_path: impl Fn(ItemInNs) -> Option<ModPath> + Copy,
+    items_with_candidate_name: FxHashSet<ItemInNs>,
+) -> FxHashSet<LocatedImport> {
+    let _p = profile::span("import_assets::path_applicable_imports");
+
+    let (unresolved_first_segment, unresolved_qualifier) = match &path_candidate.qualifier {
+        None => {
+            return items_with_candidate_name
+                .into_iter()
+                .filter_map(|item| {
+                    Some(LocatedImport::new(mod_path(item)?, item, item, mod_path(item)))
+                })
+                .collect();
         }
+        Some(first_segment_unresolved) => (
+            first_segment_unresolved.fist_segment.to_string(),
+            path_to_string_stripping_turbo_fish(&first_segment_unresolved.full_qualifier),
+        ),
     };
 
+    items_with_candidate_name
+        .into_iter()
+        .filter_map(|item| {
+            import_for_item(db, mod_path, &unresolved_first_segment, &unresolved_qualifier, item)
+        })
+        .collect()
+}
+
+fn import_for_item(
+    db: &RootDatabase,
+    mod_path: impl Fn(ItemInNs) -> Option<ModPath>,
+    unresolved_first_segment: &str,
+    unresolved_qualifier: &str,
+    original_item: ItemInNs,
+) -> Option<LocatedImport> {
+    let _p = profile::span("import_assets::import_for_item");
+
+    let original_item_candidate = item_for_path_search(db, original_item)?;
+    let import_path_candidate = mod_path(original_item_candidate)?;
+    let import_path_string = import_path_candidate.to_string();
+
+    let expected_import_end = if item_as_assoc(db, original_item).is_some() {
+        unresolved_qualifier.to_string()
+    } else {
+        format!("{}::{}", unresolved_qualifier, item_name(db, original_item)?)
+    };
+    if !import_path_string.contains(unresolved_first_segment)
+        || !import_path_string.ends_with(&expected_import_end)
+    {
+        return None;
+    }
+
+    let segment_import =
+        find_import_for_segment(db, original_item_candidate, &unresolved_first_segment)?;
+    let trait_item_to_import = item_as_assoc(db, original_item)
+        .and_then(|assoc| assoc.containing_trait(db))
+        .map(|trait_| ItemInNs::from(ModuleDef::from(trait_)));
+    Some(match (segment_import == original_item_candidate, trait_item_to_import) {
+        (true, Some(_)) => {
+            // FIXME we should be able to import both the trait and the segment,
+            // but it's unclear what to do with overlapping edits (merge imports?)
+            // especially in case of lazy completion edit resolutions.
+            return None;
+        }
+        (false, Some(trait_to_import)) => LocatedImport::new(
+            mod_path(trait_to_import)?,
+            trait_to_import,
+            original_item,
+            mod_path(original_item),
+        ),
+        (true, None) => LocatedImport::new(
+            import_path_candidate,
+            original_item_candidate,
+            original_item,
+            mod_path(original_item),
+        ),
+        (false, None) => LocatedImport::new(
+            mod_path(segment_import)?,
+            segment_import,
+            original_item,
+            mod_path(original_item),
+        ),
+    })
+}
+
+fn item_for_path_search(db: &RootDatabase, item: ItemInNs) -> Option<ItemInNs> {
+    Some(match item {
+        ItemInNs::Types(_) | ItemInNs::Values(_) => match item_as_assoc(db, item) {
+            Some(assoc_item) => match assoc_item.container(db) {
+                AssocItemContainer::Trait(trait_) => ItemInNs::from(ModuleDef::from(trait_)),
+                AssocItemContainer::Impl(impl_) => {
+                    ItemInNs::from(ModuleDef::from(impl_.target_ty(db).as_adt()?))
+                }
+            },
+            None => item,
+        },
+        ItemInNs::Macros(_) => item,
+    })
+}
+
+fn find_import_for_segment(
+    db: &RootDatabase,
+    original_item: ItemInNs,
+    unresolved_first_segment: &str,
+) -> Option<ItemInNs> {
+    let segment_is_name = item_name(db, original_item)
+        .map(|name| name.to_string() == unresolved_first_segment)
+        .unwrap_or(false);
+
+    Some(if segment_is_name {
+        original_item
+    } else {
+        let matching_module =
+            module_with_segment_name(db, &unresolved_first_segment, original_item)?;
+        ItemInNs::from(ModuleDef::from(matching_module))
+    })
+}
+
+fn module_with_segment_name(
+    db: &RootDatabase,
+    segment_name: &str,
+    candidate: ItemInNs,
+) -> Option<Module> {
+    let mut current_module = match candidate {
+        ItemInNs::Types(module_def_id) => ModuleDef::from(module_def_id).module(db),
+        ItemInNs::Values(module_def_id) => ModuleDef::from(module_def_id).module(db),
+        ItemInNs::Macros(macro_def_id) => MacroDef::from(macro_def_id).module(db),
+    };
+    while let Some(module) = current_module {
+        if let Some(module_name) = module.name(db) {
+            if module_name.to_string() == segment_name {
+                return Some(module);
+            }
+        }
+        current_module = module.parent(db);
+    }
+    None
+}
+
+fn trait_applicable_items(
+    db: &RootDatabase,
+    current_crate: Crate,
+    trait_candidate: &TraitImportCandidate,
+    trait_assoc_item: bool,
+    mod_path: impl Fn(ItemInNs) -> Option<ModPath>,
+    items_with_candidate_name: FxHashSet<ItemInNs>,
+) -> FxHashSet<LocatedImport> {
+    let _p = profile::span("import_assets::trait_applicable_items");
     let mut required_assoc_items = FxHashSet::default();
 
-    let trait_candidates = unfiltered_imports
-        .filter_map(|input| match input {
-            Either::Left(module_def) => module_def.as_assoc_item(db),
-            _ => None,
-        })
+    let trait_candidates = items_with_candidate_name
+        .into_iter()
+        .filter_map(|input| item_as_assoc(db, input))
         .filter_map(|assoc| {
             let assoc_item_trait = assoc.containing_trait(db)?;
             required_assoc_items.insert(assoc);
@@ -260,11 +454,10 @@ fn applicable_defs<'a>(
         })
         .collect();
 
-    let mut applicable_defs = FxHashSet::default();
+    let mut located_imports = FxHashSet::default();
 
-    match import_candidate {
-        ImportCandidate::Path(_) => unreachable!(),
-        ImportCandidate::TraitAssocItem(_) => receiver_ty.iterate_path_candidates(
+    if trait_assoc_item {
+        trait_candidate.receiver_ty.iterate_path_candidates(
             db,
             current_crate,
             &trait_candidates,
@@ -276,12 +469,21 @@ fn applicable_defs<'a>(
                             return None;
                         }
                     }
-                    applicable_defs.insert(Either::Left(assoc_to_module_def(assoc)));
+
+                    let item = ItemInNs::from(ModuleDef::from(assoc.containing_trait(db)?));
+                    let original_item = assoc_to_item(assoc);
+                    located_imports.insert(LocatedImport::new(
+                        mod_path(item)?,
+                        item,
+                        original_item,
+                        mod_path(original_item),
+                    ));
                 }
                 None::<()>
             },
-        ),
-        ImportCandidate::TraitMethod(_) => receiver_ty.iterate_method_candidates(
+        )
+    } else {
+        trait_candidate.receiver_ty.iterate_method_candidates(
             db,
             current_crate,
             &trait_candidates,
@@ -289,21 +491,41 @@ fn applicable_defs<'a>(
             |_, function| {
                 let assoc = function.as_assoc_item(db)?;
                 if required_assoc_items.contains(&assoc) {
-                    applicable_defs.insert(Either::Left(assoc_to_module_def(assoc)));
+                    let item = ItemInNs::from(ModuleDef::from(assoc.containing_trait(db)?));
+                    let original_item = assoc_to_item(assoc);
+                    located_imports.insert(LocatedImport::new(
+                        mod_path(item)?,
+                        item,
+                        original_item,
+                        mod_path(original_item),
+                    ));
                 }
                 None::<()>
             },
-        ),
+        )
     };
 
-    Box::new(applicable_defs.into_iter())
+    located_imports
 }
 
-fn assoc_to_module_def(assoc: AssocItem) -> ModuleDef {
+fn assoc_to_item(assoc: AssocItem) -> ItemInNs {
     match assoc {
-        AssocItem::Function(f) => f.into(),
-        AssocItem::Const(c) => c.into(),
-        AssocItem::TypeAlias(t) => t.into(),
+        AssocItem::Function(f) => ItemInNs::from(ModuleDef::from(f)),
+        AssocItem::Const(c) => ItemInNs::from(ModuleDef::from(c)),
+        AssocItem::TypeAlias(t) => ItemInNs::from(ModuleDef::from(t)),
+    }
+}
+
+fn get_mod_path(
+    db: &RootDatabase,
+    item_to_search: ItemInNs,
+    module_with_candidate: &Module,
+    prefixed: Option<PrefixKind>,
+) -> Option<ModPath> {
+    if let Some(prefix_kind) = prefixed {
+        module_with_candidate.find_use_path_prefixed(db, item_to_search, prefix_kind)
+    } else {
+        module_with_candidate.find_use_path(db, item_to_search)
     }
 }
 
@@ -316,7 +538,7 @@ impl ImportCandidate {
             Some(_) => None,
             None => Some(Self::TraitMethod(TraitImportCandidate {
                 receiver_ty: sema.type_of_expr(&method_call.receiver()?)?,
-                name: NameToImport::Exact(method_call.name_ref()?.to_string()),
+                assoc_item_name: NameToImport::Exact(method_call.name_ref()?.to_string()),
             })),
         }
     }
@@ -325,41 +547,63 @@ impl ImportCandidate {
         if sema.resolve_path(path).is_some() {
             return None;
         }
+        path_import_candidate(
+            sema,
+            path.qualifier(),
+            NameToImport::Exact(path.segment()?.name_ref()?.to_string()),
+        )
+    }
 
-        let segment = path.segment()?;
-        let candidate = if let Some(qualifier) = path.qualifier() {
-            let qualifier_start = qualifier.syntax().descendants().find_map(ast::NameRef::cast)?;
-            let qualifier_start_path =
-                qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?;
-            if let Some(qualifier_start_resolution) = sema.resolve_path(&qualifier_start_path) {
-                let qualifier_resolution = if qualifier_start_path == qualifier {
-                    qualifier_start_resolution
-                } else {
-                    sema.resolve_path(&qualifier)?
-                };
-                match qualifier_resolution {
-                    hir::PathResolution::Def(hir::ModuleDef::Adt(assoc_item_path)) => {
-                        ImportCandidate::TraitAssocItem(TraitImportCandidate {
-                            receiver_ty: assoc_item_path.ty(sema.db),
-                            name: NameToImport::Exact(segment.name_ref()?.to_string()),
-                        })
-                    }
-                    _ => return None,
-                }
-            } else {
-                ImportCandidate::Path(PathImportCandidate {
-                    qualifier: Some(qualifier),
-                    name: NameToImport::Exact(qualifier_start.to_string()),
-                })
-            }
-        } else {
-            ImportCandidate::Path(PathImportCandidate {
-                qualifier: None,
-                name: NameToImport::Exact(
-                    segment.syntax().descendants().find_map(ast::NameRef::cast)?.to_string(),
-                ),
-            })
-        };
-        Some(candidate)
+    fn for_fuzzy_path(
+        qualifier: Option<ast::Path>,
+        fuzzy_name: String,
+        sema: &Semantics<RootDatabase>,
+    ) -> Option<Self> {
+        path_import_candidate(sema, qualifier, NameToImport::Fuzzy(fuzzy_name))
+    }
+
+    fn is_trait_candidate(&self) -> bool {
+        matches!(self, ImportCandidate::TraitAssocItem(_) | ImportCandidate::TraitMethod(_))
     }
 }
+
+fn path_import_candidate(
+    sema: &Semantics<RootDatabase>,
+    qualifier: Option<ast::Path>,
+    name: NameToImport,
+) -> Option<ImportCandidate> {
+    Some(match qualifier {
+        Some(qualifier) => match sema.resolve_path(&qualifier) {
+            None => {
+                let qualifier_start =
+                    qualifier.syntax().descendants().find_map(ast::NameRef::cast)?;
+                let qualifier_start_path =
+                    qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?;
+                if sema.resolve_path(&qualifier_start_path).is_none() {
+                    ImportCandidate::Path(PathImportCandidate {
+                        qualifier: Some(FirstSegmentUnresolved {
+                            fist_segment: qualifier_start,
+                            full_qualifier: qualifier,
+                        }),
+                        name,
+                    })
+                } else {
+                    return None;
+                }
+            }
+            Some(PathResolution::Def(ModuleDef::Adt(assoc_item_path))) => {
+                ImportCandidate::TraitAssocItem(TraitImportCandidate {
+                    receiver_ty: assoc_item_path.ty(sema.db),
+                    assoc_item_name: name,
+                })
+            }
+            Some(_) => return None,
+        },
+        None => ImportCandidate::Path(PathImportCandidate { qualifier: None, name }),
+    })
+}
+
+fn item_as_assoc(db: &RootDatabase, item: ItemInNs) -> Option<AssocItem> {
+    item.as_module_def_id()
+        .and_then(|module_def_id| ModuleDef::from(module_def_id).as_assoc_item(db))
+}
diff --git a/crates/ide_db/src/imports_locator.rs b/crates/ide_db/src/items_locator.rs
similarity index 61%
rename from crates/ide_db/src/imports_locator.rs
rename to crates/ide_db/src/items_locator.rs
index 502e8281a3c..8a7f0293537 100644
--- a/crates/ide_db/src/imports_locator.rs
+++ b/crates/ide_db/src/items_locator.rs
@@ -1,9 +1,10 @@
 //! This module contains an import search functionality that is provided to the assists module.
 //! Later, this should be moved away to a separate crate that is accessible from the assists module.
 
+use either::Either;
 use hir::{
     import_map::{self, ImportKind},
-    AsAssocItem, Crate, MacroDef, ModuleDef, Semantics,
+    AsAssocItem, Crate, ItemInNs, ModuleDef, Semantics,
 };
 use syntax::{ast, AstNode, SyntaxKind::NAME};
 
@@ -12,47 +13,47 @@ use crate::{
     symbol_index::{self, FileSymbol},
     RootDatabase,
 };
-use either::Either;
 use rustc_hash::FxHashSet;
 
 pub(crate) const DEFAULT_QUERY_SEARCH_LIMIT: usize = 40;
 
-pub fn find_exact_imports<'a>(
-    sema: &Semantics<'a, RootDatabase>,
+pub fn with_exact_name(
+    sema: &Semantics<'_, RootDatabase>,
     krate: Crate,
-    name_to_import: String,
-) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>>> {
+    exact_name: String,
+) -> FxHashSet<ItemInNs> {
     let _p = profile::span("find_exact_imports");
-    Box::new(find_imports(
+    find_items(
         sema,
         krate,
         {
-            let mut local_query = symbol_index::Query::new(name_to_import.clone());
+            let mut local_query = symbol_index::Query::new(exact_name.clone());
             local_query.exact();
             local_query.limit(DEFAULT_QUERY_SEARCH_LIMIT);
             local_query
         },
-        import_map::Query::new(name_to_import)
+        import_map::Query::new(exact_name)
             .limit(DEFAULT_QUERY_SEARCH_LIMIT)
             .name_only()
             .search_mode(import_map::SearchMode::Equals)
             .case_sensitive(),
-    ))
+    )
 }
 
+#[derive(Debug)]
 pub enum AssocItemSearch {
     Include,
     Exclude,
     AssocItemsOnly,
 }
 
-pub fn find_similar_imports<'a>(
-    sema: &Semantics<'a, RootDatabase>,
+pub fn with_similar_name(
+    sema: &Semantics<'_, RootDatabase>,
     krate: Crate,
     fuzzy_search_string: String,
     assoc_item_search: AssocItemSearch,
     limit: Option<usize>,
-) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a> {
+) -> FxHashSet<ItemInNs> {
     let _p = profile::span("find_similar_imports");
 
     let mut external_query = import_map::Query::new(fuzzy_search_string.clone())
@@ -76,37 +77,39 @@ pub fn find_similar_imports<'a>(
         local_query.limit(limit);
     }
 
-    let db = sema.db;
-    Box::new(find_imports(sema, krate, local_query, external_query).filter(
-        move |import_candidate| match assoc_item_search {
+    find_items(sema, krate, local_query, external_query)
+        .into_iter()
+        .filter(move |&item| match assoc_item_search {
             AssocItemSearch::Include => true,
-            AssocItemSearch::Exclude => !is_assoc_item(import_candidate, db),
-            AssocItemSearch::AssocItemsOnly => is_assoc_item(import_candidate, db),
-        },
-    ))
+            AssocItemSearch::Exclude => !is_assoc_item(item, sema.db),
+            AssocItemSearch::AssocItemsOnly => is_assoc_item(item, sema.db),
+        })
+        .collect()
 }
 
-fn is_assoc_item(import_candidate: &Either<ModuleDef, MacroDef>, db: &RootDatabase) -> bool {
-    match import_candidate {
-        Either::Left(ModuleDef::Function(function)) => function.as_assoc_item(db).is_some(),
-        Either::Left(ModuleDef::Const(const_)) => const_.as_assoc_item(db).is_some(),
-        Either::Left(ModuleDef::TypeAlias(type_alias)) => type_alias.as_assoc_item(db).is_some(),
-        _ => false,
-    }
+fn is_assoc_item(item: ItemInNs, db: &RootDatabase) -> bool {
+    item.as_module_def_id()
+        .and_then(|module_def_id| ModuleDef::from(module_def_id).as_assoc_item(db))
+        .is_some()
 }
 
-fn find_imports<'a>(
-    sema: &Semantics<'a, RootDatabase>,
+fn find_items(
+    sema: &Semantics<'_, RootDatabase>,
     krate: Crate,
     local_query: symbol_index::Query,
     external_query: import_map::Query,
-) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> {
+) -> FxHashSet<ItemInNs> {
     let _p = profile::span("find_similar_imports");
     let db = sema.db;
 
     // Query dependencies first.
-    let mut candidates: FxHashSet<_> =
-        krate.query_external_importables(db, external_query).collect();
+    let mut candidates = krate
+        .query_external_importables(db, external_query)
+        .map(|external_importable| match external_importable {
+            Either::Left(module_def) => ItemInNs::from(module_def),
+            Either::Right(macro_def) => ItemInNs::from(macro_def),
+        })
+        .collect::<FxHashSet<_>>();
 
     // Query the local crate using the symbol index.
     let local_results = symbol_index::crate_symbols(db, krate.into(), local_query);
@@ -114,19 +117,19 @@ fn find_imports<'a>(
     candidates.extend(
         local_results
             .into_iter()
-            .filter_map(|import_candidate| get_name_definition(sema, &import_candidate))
+            .filter_map(|local_candidate| get_name_definition(sema, &local_candidate))
             .filter_map(|name_definition_to_import| match name_definition_to_import {
-                Definition::ModuleDef(module_def) => Some(Either::Left(module_def)),
-                Definition::Macro(macro_def) => Some(Either::Right(macro_def)),
+                Definition::ModuleDef(module_def) => Some(ItemInNs::from(module_def)),
+                Definition::Macro(macro_def) => Some(ItemInNs::from(macro_def)),
                 _ => None,
             }),
     );
 
-    candidates.into_iter()
+    candidates
 }
 
-fn get_name_definition<'a>(
-    sema: &Semantics<'a, RootDatabase>,
+fn get_name_definition(
+    sema: &Semantics<'_, RootDatabase>,
     import_candidate: &FileSymbol,
 ) -> Option<Definition> {
     let _p = profile::span("get_name_definition");
diff --git a/crates/ide_db/src/lib.rs b/crates/ide_db/src/lib.rs
index 6eb34b06b7c..88ee4a87d51 100644
--- a/crates/ide_db/src/lib.rs
+++ b/crates/ide_db/src/lib.rs
@@ -8,7 +8,7 @@ pub mod line_index;
 pub mod symbol_index;
 pub mod defs;
 pub mod search;
-pub mod imports_locator;
+pub mod items_locator;
 pub mod source_change;
 pub mod ty_filter;
 pub mod traits;
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 4f6f250d6c6..2c4c339cb73 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -697,7 +697,6 @@ pub(crate) fn handle_completion_resolve(
             FilePosition { file_id, offset },
             &resolve_data.full_import_path,
             resolve_data.imported_name,
-            resolve_data.import_for_trait_assoc_item,
         )?
         .into_iter()
         .flat_map(|edit| edit.into_iter().map(|indel| to_proto::text_edit(&line_index, indel)))
@@ -1525,7 +1524,6 @@ struct CompletionResolveData {
     position: lsp_types::TextDocumentPositionParams,
     full_import_path: String,
     imported_name: String,
-    import_for_trait_assoc_item: bool,
 }
 
 fn fill_resolve_data(
@@ -1534,15 +1532,13 @@ fn fill_resolve_data(
     position: &TextDocumentPositionParams,
 ) -> Option<()> {
     let import_edit = item.import_to_add()?;
-    let full_import_path = import_edit.import_path.to_string();
-    let imported_name = import_edit.import_path.segments().last()?.to_string();
+    let import_path = &import_edit.import.import_path;
 
     *resolve_data = Some(
         to_value(CompletionResolveData {
             position: position.to_owned(),
-            full_import_path,
-            imported_name,
-            import_for_trait_assoc_item: import_edit.import_for_trait_assoc_item,
+            full_import_path: import_path.to_string(),
+            imported_name: import_path.segments().last()?.to_string(),
         })
         .unwrap(),
     );
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml
index c39095def07..33bde099bcc 100644
--- a/crates/syntax/Cargo.toml
+++ b/crates/syntax/Cargo.toml
@@ -25,10 +25,10 @@ serde = { version = "1.0.106", features = ["derive"] }
 stdx = { path = "../stdx", version = "0.0.0" }
 text_edit = { path = "../text_edit", version = "0.0.0" }
 parser = { path = "../parser", version = "0.0.0" }
-test_utils = { path = "../test_utils", version = "0.0.0" }
 profile = { path = "../profile", version = "0.0.0" }
 
 [dev-dependencies]
+test_utils = { path = "../test_utils" }
 walkdir = "2.3.1"
 rayon = "1"
 expect-test = "1.1"
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index b6c5de65884..70ba8adb487 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -91,6 +91,10 @@ pub fn path_from_segments(
     })
 }
 
+pub fn path_from_text(text: &str) -> ast::Path {
+    ast_from_text(&format!("fn main() {{ let test = {}; }}", text))
+}
+
 pub fn glob_use_tree() -> ast::UseTree {
     ast_from_text("use *;")
 }
diff --git a/crates/syntax/src/lib.rs b/crates/syntax/src/lib.rs
index 11294c5b22b..09e212e8c03 100644
--- a/crates/syntax/src/lib.rs
+++ b/crates/syntax/src/lib.rs
@@ -37,6 +37,7 @@ pub mod algo;
 pub mod ast;
 #[doc(hidden)]
 pub mod fuzz;
+pub mod utils;
 
 use std::{marker::PhantomData, sync::Arc};
 
diff --git a/crates/syntax/src/utils.rs b/crates/syntax/src/utils.rs
new file mode 100644
index 00000000000..f4c02518b4c
--- /dev/null
+++ b/crates/syntax/src/utils.rs
@@ -0,0 +1,43 @@
+//! A set of utils methods to reuse on other abstraction levels
+
+use itertools::Itertools;
+
+use crate::{ast, match_ast, AstNode};
+
+pub fn path_to_string_stripping_turbo_fish(path: &ast::Path) -> String {
+    path.syntax()
+        .children()
+        .filter_map(|node| {
+            match_ast! {
+                match node {
+                    ast::PathSegment(it) => {
+                        Some(it.name_ref()?.to_string())
+                    },
+                    ast::Path(it) => {
+                        Some(path_to_string_stripping_turbo_fish(&it))
+                    },
+                    _ => None,
+                }
+            }
+        })
+        .join("::")
+}
+
+#[cfg(test)]
+mod tests {
+    use super::path_to_string_stripping_turbo_fish;
+    use crate::ast::make;
+
+    #[test]
+    fn turbofishes_are_stripped() {
+        assert_eq!("Vec", path_to_string_stripping_turbo_fish(&make::path_from_text("Vec::<i32>")),);
+        assert_eq!(
+            "Vec::new",
+            path_to_string_stripping_turbo_fish(&make::path_from_text("Vec::<i32>::new")),
+        );
+        assert_eq!(
+            "Vec::new",
+            path_to_string_stripping_turbo_fish(&make::path_from_text("Vec::new()")),
+        );
+    }
+}