mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-02 06:41:48 +00:00
Test and initial refactoring
This commit is contained in:
parent
c48478621f
commit
005bc49d74
4 changed files with 102 additions and 126 deletions
|
@ -220,41 +220,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]
|
#[test]
|
||||||
fn applicable_when_found_multiple_imports() {
|
fn applicable_when_found_multiple_imports() {
|
||||||
check_assist(
|
check_assist(
|
||||||
|
|
|
@ -45,7 +45,7 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
|
||||||
|
|
||||||
let qualify_candidate = match candidate {
|
let qualify_candidate = match candidate {
|
||||||
ImportCandidate::Path(candidate) => {
|
ImportCandidate::Path(candidate) => {
|
||||||
if candidate.qualifier.is_some() {
|
if candidate.unresolved_qualifier.is_some() {
|
||||||
cov_mark::hit!(qualify_path_qualifier_start);
|
cov_mark::hit!(qualify_path_qualifier_start);
|
||||||
let path = ast::Path::cast(syntax_under_caret)?;
|
let path = ast::Path::cast(syntax_under_caret)?;
|
||||||
let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?);
|
let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?);
|
||||||
|
@ -192,7 +192,7 @@ fn group_label(candidate: &ImportCandidate) -> GroupLabel {
|
||||||
fn label(candidate: &ImportCandidate, import: &hir::ModPath) -> String {
|
fn label(candidate: &ImportCandidate, import: &hir::ModPath) -> String {
|
||||||
match candidate {
|
match candidate {
|
||||||
ImportCandidate::Path(candidate) => {
|
ImportCandidate::Path(candidate) => {
|
||||||
if candidate.qualifier.is_some() {
|
if candidate.unresolved_qualifier.is_some() {
|
||||||
format!("Qualify with `{}`", &import)
|
format!("Qualify with `{}`", &import)
|
||||||
} else {
|
} else {
|
||||||
format!("Qualify as `{}`", &import)
|
format!("Qualify as `{}`", &import)
|
||||||
|
|
|
@ -168,15 +168,15 @@ fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAs
|
||||||
ctx.path_qual.clone(),
|
ctx.path_qual.clone(),
|
||||||
fuzzy_name,
|
fuzzy_name,
|
||||||
&ctx.sema,
|
&ctx.sema,
|
||||||
);
|
)?;
|
||||||
|
|
||||||
if matches!(assets_for_path.as_ref()?.import_candidate(), ImportCandidate::Path(_))
|
if matches!(assets_for_path.import_candidate(), ImportCandidate::Path(_))
|
||||||
&& fuzzy_name_length < 2
|
&& fuzzy_name_length < 2
|
||||||
{
|
{
|
||||||
cov_mark::hit!(ignore_short_input_for_path);
|
cov_mark::hit!(ignore_short_input_for_path);
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
assets_for_path
|
Some(assets_for_path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -773,4 +773,35 @@ fn main() {
|
||||||
}"#,
|
}"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unresolved_qualifiers() {
|
||||||
|
check_edit(
|
||||||
|
"Item",
|
||||||
|
r#"
|
||||||
|
mod foo {
|
||||||
|
pub mod bar {
|
||||||
|
pub struct Item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
bar::Ite$0
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
use foo::bar;
|
||||||
|
|
||||||
|
mod foo {
|
||||||
|
pub mod bar {
|
||||||
|
pub struct Item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
bar::Item
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ pub struct TraitImportCandidate {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct PathImportCandidate {
|
pub struct PathImportCandidate {
|
||||||
pub qualifier: Option<ast::Path>,
|
pub unresolved_qualifier: Option<ast::Path>,
|
||||||
pub name: NameToImport,
|
pub name: NameToImport,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,38 +82,14 @@ impl ImportAssets {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn for_fuzzy_path(
|
pub fn for_fuzzy_path(
|
||||||
module_with_path: Module,
|
module_with_candidate: Module,
|
||||||
qualifier: Option<ast::Path>,
|
qualifier: Option<ast::Path>,
|
||||||
fuzzy_name: String,
|
fuzzy_name: String,
|
||||||
sema: &Semantics<RootDatabase>,
|
sema: &Semantics<RootDatabase>,
|
||||||
) -> Option<Self> {
|
) -> Option<Self> {
|
||||||
Some(match qualifier {
|
Some(Self {
|
||||||
Some(qualifier) => {
|
import_candidate: ImportCandidate::for_fuzzy_path(qualifier, fuzzy_name, sema)?,
|
||||||
let qualifier_resolution = sema.resolve_path(&qualifier)?;
|
module_with_candidate,
|
||||||
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,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,8 +145,9 @@ impl ImportAssets {
|
||||||
prefixed: Option<hir::PrefixKind>,
|
prefixed: Option<hir::PrefixKind>,
|
||||||
) -> Vec<(hir::ModPath, hir::ItemInNs)> {
|
) -> Vec<(hir::ModPath, hir::ItemInNs)> {
|
||||||
let current_crate = self.module_with_candidate.krate();
|
let current_crate = self.module_with_candidate.krate();
|
||||||
|
let import_candidate = &self.import_candidate;
|
||||||
|
|
||||||
let unfiltered_imports = match self.name_to_import() {
|
let imports_for_candidate_name = match self.name_to_import() {
|
||||||
NameToImport::Exact(exact_name) => {
|
NameToImport::Exact(exact_name) => {
|
||||||
imports_locator::find_exact_imports(sema, current_crate, exact_name.clone())
|
imports_locator::find_exact_imports(sema, current_crate, exact_name.clone())
|
||||||
}
|
}
|
||||||
|
@ -180,11 +157,10 @@ impl ImportAssets {
|
||||||
// and https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0/topic/Blanket.20trait.20impls.20lookup
|
// and https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0/topic/Blanket.20trait.20impls.20lookup
|
||||||
// for the details
|
// for the details
|
||||||
NameToImport::Fuzzy(fuzzy_name) => {
|
NameToImport::Fuzzy(fuzzy_name) => {
|
||||||
let (assoc_item_search, limit) = match self.import_candidate {
|
let (assoc_item_search, limit) = if import_candidate.is_trait_candidate() {
|
||||||
ImportCandidate::TraitAssocItem(_) | ImportCandidate::TraitMethod(_) => {
|
(AssocItemSearch::AssocItemsOnly, None)
|
||||||
(AssocItemSearch::AssocItemsOnly, None)
|
} else {
|
||||||
}
|
(AssocItemSearch::Exclude, Some(DEFAULT_QUERY_SEARCH_LIMIT))
|
||||||
_ => (AssocItemSearch::Exclude, Some(DEFAULT_QUERY_SEARCH_LIMIT)),
|
|
||||||
};
|
};
|
||||||
imports_locator::find_similar_imports(
|
imports_locator::find_similar_imports(
|
||||||
sema,
|
sema,
|
||||||
|
@ -198,24 +174,22 @@ impl ImportAssets {
|
||||||
|
|
||||||
let db = sema.db;
|
let db = sema.db;
|
||||||
let mut res =
|
let mut res =
|
||||||
applicable_defs(self.import_candidate(), current_crate, db, unfiltered_imports)
|
applicable_defs(import_candidate, current_crate, db, imports_for_candidate_name)
|
||||||
.filter_map(|candidate| {
|
.filter_map(|candidate| {
|
||||||
let item: hir::ItemInNs = candidate.clone().either(Into::into, Into::into);
|
let item: hir::ItemInNs = candidate.clone().either(Into::into, Into::into);
|
||||||
|
|
||||||
let item_to_search = match self.import_candidate {
|
let item_to_search = if import_candidate.is_trait_candidate() {
|
||||||
ImportCandidate::TraitAssocItem(_) | ImportCandidate::TraitMethod(_) => {
|
let canidate_trait = match candidate {
|
||||||
let canidate_trait = match candidate {
|
Either::Left(module_def) => {
|
||||||
Either::Left(module_def) => {
|
module_def.as_assoc_item(db)?.containing_trait(db)
|
||||||
module_def.as_assoc_item(db)?.containing_trait(db)
|
}
|
||||||
}
|
_ => None,
|
||||||
_ => None,
|
}?;
|
||||||
}?;
|
ModuleDef::from(canidate_trait).into()
|
||||||
ModuleDef::from(canidate_trait).into()
|
} else {
|
||||||
}
|
item
|
||||||
_ => item,
|
|
||||||
};
|
};
|
||||||
|
let mod_path = if let Some(prefix_kind) = prefixed {
|
||||||
if let Some(prefix_kind) = prefixed {
|
|
||||||
self.module_with_candidate.find_use_path_prefixed(
|
self.module_with_candidate.find_use_path_prefixed(
|
||||||
db,
|
db,
|
||||||
item_to_search,
|
item_to_search,
|
||||||
|
@ -223,8 +197,9 @@ impl ImportAssets {
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
self.module_with_candidate.find_use_path(db, item_to_search)
|
self.module_with_candidate.find_use_path(db, item_to_search)
|
||||||
}
|
};
|
||||||
.map(|path| (path, item))
|
|
||||||
|
mod_path.zip(Some(item))
|
||||||
})
|
})
|
||||||
.filter(|(use_path, _)| use_path.len() > 1)
|
.filter(|(use_path, _)| use_path.len() > 1)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
@ -239,6 +214,7 @@ fn applicable_defs<'a>(
|
||||||
db: &RootDatabase,
|
db: &RootDatabase,
|
||||||
unfiltered_imports: Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a>,
|
unfiltered_imports: Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a>,
|
||||||
) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a> {
|
) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a> {
|
||||||
|
// TODO kb this needs to consider various path prefixes, etc.
|
||||||
let receiver_ty = match import_candidate {
|
let receiver_ty = match import_candidate {
|
||||||
ImportCandidate::Path(_) => return unfiltered_imports,
|
ImportCandidate::Path(_) => return unfiltered_imports,
|
||||||
ImportCandidate::TraitAssocItem(candidate) | ImportCandidate::TraitMethod(candidate) => {
|
ImportCandidate::TraitAssocItem(candidate) | ImportCandidate::TraitMethod(candidate) => {
|
||||||
|
@ -325,41 +301,45 @@ impl ImportCandidate {
|
||||||
if sema.resolve_path(path).is_some() {
|
if sema.resolve_path(path).is_some() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
path_import_candidate(
|
||||||
|
sema,
|
||||||
|
path.qualifier(),
|
||||||
|
NameToImport::Exact(path.segment()?.name_ref()?.to_string()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
let segment = path.segment()?;
|
fn for_fuzzy_path(
|
||||||
let candidate = if let Some(qualifier) = path.qualifier() {
|
qualifier: Option<ast::Path>,
|
||||||
let qualifier_start = qualifier.syntax().descendants().find_map(ast::NameRef::cast)?;
|
fuzzy_name: String,
|
||||||
let qualifier_start_path =
|
sema: &Semantics<RootDatabase>,
|
||||||
qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?;
|
) -> Option<Self> {
|
||||||
if let Some(qualifier_start_resolution) = sema.resolve_path(&qualifier_start_path) {
|
path_import_candidate(sema, qualifier, NameToImport::Fuzzy(fuzzy_name))
|
||||||
let qualifier_resolution = if qualifier_start_path == qualifier {
|
}
|
||||||
qualifier_start_resolution
|
|
||||||
} else {
|
fn is_trait_candidate(&self) -> bool {
|
||||||
sema.resolve_path(&qualifier)?
|
matches!(self, ImportCandidate::TraitAssocItem(_) | ImportCandidate::TraitMethod(_))
|
||||||
};
|
|
||||||
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 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 => ImportCandidate::Path(PathImportCandidate {
|
||||||
|
unresolved_qualifier: Some(qualifier),
|
||||||
|
name,
|
||||||
|
}),
|
||||||
|
Some(hir::PathResolution::Def(hir::ModuleDef::Adt(assoc_item_path))) => {
|
||||||
|
ImportCandidate::TraitAssocItem(TraitImportCandidate {
|
||||||
|
receiver_ty: assoc_item_path.ty(sema.db),
|
||||||
|
name,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Some(_) => return None,
|
||||||
|
},
|
||||||
|
None => ImportCandidate::Path(PathImportCandidate { unresolved_qualifier: None, name }),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue