3685: Auto import macros r=SomeoneToIgnore a=SomeoneToIgnore

If I got it right, assists test infra does not support multiple crates snippets (2720e2374b/crates/ra_hir_def/src/nameres/tests.rs (L491)) hence no tests added for the macro import.

Co-authored-by: Kirill Bulatov <mail4score@gmail.com>
This commit is contained in:
bors[bot] 2020-03-24 08:43:58 +00:00 committed by GitHub
commit d5f77f3b6d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 100 additions and 37 deletions

2
Cargo.lock generated
View file

@ -884,6 +884,7 @@ version = "0.1.0"
name = "ra_assists" name = "ra_assists"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"either",
"format-buf", "format-buf",
"itertools 0.9.0", "itertools 0.9.0",
"join_to_string", "join_to_string",
@ -1045,6 +1046,7 @@ dependencies = [
name = "ra_ide_db" name = "ra_ide_db"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"either",
"fst", "fst",
"log", "log",
"once_cell", "once_cell",

View file

@ -12,6 +12,7 @@ format-buf = "1.0.0"
join_to_string = "0.1.3" join_to_string = "0.1.3"
rustc-hash = "1.1.0" rustc-hash = "1.1.0"
itertools = "0.9.0" itertools = "0.9.0"
either = "1.5.3"
ra_syntax = { path = "../ra_syntax" } ra_syntax = { path = "../ra_syntax" }
ra_text_edit = { path = "../ra_text_edit" } ra_text_edit = { path = "../ra_text_edit" }

View file

@ -17,6 +17,7 @@ use crate::{
utils::insert_use_statement, utils::insert_use_statement,
AssistId, AssistId,
}; };
use either::Either;
// Assist: auto_import // Assist: auto_import
// //
@ -58,6 +59,7 @@ pub(crate) fn auto_import(ctx: AssistCtx) -> Option<Assist> {
group.finish() group.finish()
} }
#[derive(Debug)]
struct AutoImportAssets { struct AutoImportAssets {
import_candidate: ImportCandidate, import_candidate: ImportCandidate,
module_with_name_to_import: Module, module_with_name_to_import: Module,
@ -127,14 +129,14 @@ impl AutoImportAssets {
ImportsLocator::new(db) ImportsLocator::new(db)
.find_imports(&self.get_search_query()) .find_imports(&self.get_search_query())
.into_iter() .into_iter()
.filter_map(|module_def| match &self.import_candidate { .filter_map(|candidate| match &self.import_candidate {
ImportCandidate::TraitAssocItem(assoc_item_type, _) => { ImportCandidate::TraitAssocItem(assoc_item_type, _) => {
let located_assoc_item = match module_def { let located_assoc_item = match candidate {
ModuleDef::Function(located_function) => located_function Either::Left(ModuleDef::Function(located_function)) => located_function
.as_assoc_item(db) .as_assoc_item(db)
.map(|assoc| assoc.container(db)) .map(|assoc| assoc.container(db))
.and_then(Self::assoc_to_trait), .and_then(Self::assoc_to_trait),
ModuleDef::Const(located_const) => located_const Either::Left(ModuleDef::Const(located_const)) => located_const
.as_assoc_item(db) .as_assoc_item(db)
.map(|assoc| assoc.container(db)) .map(|assoc| assoc.container(db))
.and_then(Self::assoc_to_trait), .and_then(Self::assoc_to_trait),
@ -153,10 +155,11 @@ impl AutoImportAssets {
|_, assoc| Self::assoc_to_trait(assoc.container(db)), |_, assoc| Self::assoc_to_trait(assoc.container(db)),
) )
.map(ModuleDef::from) .map(ModuleDef::from)
.map(Either::Left)
} }
ImportCandidate::TraitMethod(function_callee, _) => { ImportCandidate::TraitMethod(function_callee, _) => {
let located_assoc_item = let located_assoc_item =
if let ModuleDef::Function(located_function) = module_def { if let Either::Left(ModuleDef::Function(located_function)) = candidate {
located_function located_function
.as_assoc_item(db) .as_assoc_item(db)
.map(|assoc| assoc.container(db)) .map(|assoc| assoc.container(db))
@ -179,10 +182,18 @@ impl AutoImportAssets {
}, },
) )
.map(ModuleDef::from) .map(ModuleDef::from)
.map(Either::Left)
}
_ => Some(candidate),
})
.filter_map(|candidate| match candidate {
Either::Left(module_def) => {
self.module_with_name_to_import.find_use_path(db, module_def)
}
Either::Right(macro_def) => {
self.module_with_name_to_import.find_use_path(db, macro_def)
} }
_ => Some(module_def),
}) })
.filter_map(|module_def| self.module_with_name_to_import.find_use_path(db, module_def))
.filter(|use_path| !use_path.segments.is_empty()) .filter(|use_path| !use_path.segments.is_empty())
.take(20) .take(20)
.collect::<BTreeSet<_>>() .collect::<BTreeSet<_>>()
@ -439,6 +450,30 @@ mod tests {
); );
} }
#[test]
fn macro_import() {
check_assist(
auto_import,
r"
//- /lib.rs crate:crate_with_macro
#[macro_export]
macro_rules! foo {
() => ()
}
//- /main.rs crate:main deps:crate_with_macro
fn main() {
foo<|>
}",
r"use crate_with_macro::foo;
fn main() {
foo<|>
}
",
);
}
#[test] #[test]
fn auto_import_target() { fn auto_import_target() {
check_assist_target( check_assist_target(

View file

@ -2,7 +2,7 @@
use std::iter; use std::iter;
use hir::{Adt, HasSource, Semantics}; use hir::{Adt, HasSource, ModuleDef, Semantics};
use itertools::Itertools; use itertools::Itertools;
use ra_ide_db::RootDatabase; use ra_ide_db::RootDatabase;
@ -154,7 +154,7 @@ fn resolve_tuple_of_enum_def(
} }
fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::EnumVariant) -> Option<ast::Pat> { fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::EnumVariant) -> Option<ast::Pat> {
let path = crate::ast_transform::path_to_ast(module.find_use_path(db, var.into())?); let path = crate::ast_transform::path_to_ast(module.find_use_path(db, ModuleDef::from(var))?);
// FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though
let pat: ast::Pat = match var.source(db).value.kind() { let pat: ast::Pat = match var.source(db).value.kind() {

View file

@ -165,7 +165,6 @@ mod helpers {
use ra_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt}; use ra_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt};
use ra_ide_db::{symbol_index::SymbolsDatabase, RootDatabase}; use ra_ide_db::{symbol_index::SymbolsDatabase, RootDatabase};
use ra_syntax::TextRange;
use test_utils::{add_cursor, assert_eq_text, extract_range_or_offset, RangeOrOffset}; use test_utils::{add_cursor, assert_eq_text, extract_range_or_offset, RangeOrOffset};
use crate::{AssistCtx, AssistHandler}; use crate::{AssistCtx, AssistHandler};
@ -175,8 +174,7 @@ mod helpers {
let (mut db, file_id) = RootDatabase::with_single_file(text); let (mut db, file_id) = RootDatabase::with_single_file(text);
// FIXME: ideally, this should be done by the above `RootDatabase::with_single_file`, // FIXME: ideally, this should be done by the above `RootDatabase::with_single_file`,
// but it looks like this might need specialization? :( // but it looks like this might need specialization? :(
let local_roots = vec![db.file_source_root(file_id)]; db.set_local_roots(Arc::new(vec![db.file_source_root(file_id)]));
db.set_local_roots(Arc::new(local_roots));
(db, file_id) (db, file_id)
} }
@ -206,11 +204,24 @@ mod helpers {
} }
fn check(assist: AssistHandler, before: &str, expected: ExpectedResult) { fn check(assist: AssistHandler, before: &str, expected: ExpectedResult) {
let (range_or_offset, before) = extract_range_or_offset(before); let (text_without_caret, file_with_caret_id, range_or_offset, db) =
let range: TextRange = range_or_offset.into(); if before.contains("//-") {
let (mut db, position) = RootDatabase::with_position(before);
db.set_local_roots(Arc::new(vec![db.file_source_root(position.file_id)]));
(
db.file_text(position.file_id).as_ref().to_owned(),
position.file_id,
RangeOrOffset::Offset(position.offset),
db,
)
} else {
let (range_or_offset, text_without_caret) = extract_range_or_offset(before);
let (db, file_id) = with_single_file(&text_without_caret);
(text_without_caret, file_id, range_or_offset, db)
};
let frange = FileRange { file_id: file_with_caret_id, range: range_or_offset.into() };
let (db, file_id) = with_single_file(&before);
let frange = FileRange { file_id, range };
let sema = Semantics::new(&db); let sema = Semantics::new(&db);
let assist_ctx = AssistCtx::new(&sema, frange, true); let assist_ctx = AssistCtx::new(&sema, frange, true);
@ -218,7 +229,7 @@ mod helpers {
(Some(assist), ExpectedResult::After(after)) => { (Some(assist), ExpectedResult::After(after)) => {
let action = assist.0[0].action.clone().unwrap(); let action = assist.0[0].action.clone().unwrap();
let mut actual = action.edit.apply(&before); let mut actual = action.edit.apply(&text_without_caret);
match action.cursor_position { match action.cursor_position {
None => { None => {
if let RangeOrOffset::Offset(before_cursor_pos) = range_or_offset { if let RangeOrOffset::Offset(before_cursor_pos) = range_or_offset {
@ -237,7 +248,7 @@ mod helpers {
(Some(assist), ExpectedResult::Target(target)) => { (Some(assist), ExpectedResult::Target(target)) => {
let action = assist.0[0].action.clone().unwrap(); let action = assist.0[0].action.clone().unwrap();
let range = action.target.expect("expected target on action"); let range = action.target.expect("expected target on action");
assert_eq_text!(&before[range], target); assert_eq_text!(&text_without_caret[range], target);
} }
(Some(_), ExpectedResult::NotApplicable) => panic!("assist should not be applicable!"), (Some(_), ExpectedResult::NotApplicable) => panic!("assist should not be applicable!"),
(None, ExpectedResult::After(_)) | (None, ExpectedResult::Target(_)) => { (None, ExpectedResult::After(_)) | (None, ExpectedResult::Target(_)) => {

View file

@ -33,7 +33,11 @@ use ra_syntax::{
}; };
use rustc_hash::FxHashSet; use rustc_hash::FxHashSet;
use crate::{db::HirDatabase, has_source::HasSource, CallableDef, HirDisplay, InFile, Name}; use crate::{
db::{DefDatabase, HirDatabase},
has_source::HasSource,
CallableDef, HirDisplay, InFile, Name,
};
/// hir::Crate describes a single crate. It's the main interface with which /// hir::Crate describes a single crate. It's the main interface with which
/// a crate's dependencies interact. Mostly, it should be just a proxy for the /// a crate's dependencies interact. Mostly, it should be just a proxy for the
@ -274,20 +278,10 @@ impl Module {
/// this module, if possible. /// this module, if possible.
pub fn find_use_path( pub fn find_use_path(
self, self,
db: &dyn HirDatabase, db: &dyn DefDatabase,
item: ModuleDef, item: impl Into<ItemInNs>,
) -> Option<hir_def::path::ModPath> { ) -> Option<hir_def::path::ModPath> {
// FIXME expose namespace choice hir_def::find_path::find_path(db, item.into(), self.into())
hir_def::find_path::find_path(db.upcast(), determine_item_namespace(item), self.into())
}
}
fn determine_item_namespace(module_def: ModuleDef) -> ItemInNs {
match module_def {
ModuleDef::Static(_) | ModuleDef::Const(_) | ModuleDef::Function(_) => {
ItemInNs::Values(module_def.into())
}
_ => ItemInNs::Types(module_def.into()),
} }
} }

View file

@ -9,8 +9,8 @@ use hir_def::{
}; };
use crate::{ use crate::{
Adt, AssocItem, AttrDef, DefWithBody, EnumVariant, GenericDef, Local, ModuleDef, StructField, code_model::ItemInNs, Adt, AssocItem, AttrDef, DefWithBody, EnumVariant, GenericDef, Local,
VariantDef, MacroDef, ModuleDef, StructField, VariantDef,
}; };
macro_rules! from_id { macro_rules! from_id {
@ -228,3 +228,20 @@ impl From<(DefWithBodyId, PatId)> for Local {
Local { parent, pat_id } Local { parent, pat_id }
} }
} }
impl From<MacroDef> for ItemInNs {
fn from(macro_def: MacroDef) -> Self {
ItemInNs::Macros(macro_def.into())
}
}
impl From<ModuleDef> for ItemInNs {
fn from(module_def: ModuleDef) -> Self {
match module_def {
ModuleDef::Static(_) | ModuleDef::Const(_) | ModuleDef::Function(_) => {
ItemInNs::Values(module_def.into())
}
_ => ItemInNs::Types(module_def.into()),
}
}
}

View file

@ -17,6 +17,7 @@ fst = { version = "0.4", default-features = false }
rustc-hash = "1.1.0" rustc-hash = "1.1.0"
superslice = "1.0.0" superslice = "1.0.0"
once_cell = "1.3.1" once_cell = "1.3.1"
either = "1.5.3"
ra_syntax = { path = "../ra_syntax" } ra_syntax = { path = "../ra_syntax" }
ra_text_edit = { path = "../ra_text_edit" } ra_text_edit = { path = "../ra_text_edit" }

View file

@ -1,7 +1,7 @@
//! This module contains an import search funcionality that is provided to the ra_assists module. //! This module contains an import search funcionality that is provided to the ra_assists module.
//! Later, this should be moved away to a separate crate that is accessible from the ra_assists module. //! Later, this should be moved away to a separate crate that is accessible from the ra_assists module.
use hir::{ModuleDef, Semantics}; use hir::{MacroDef, ModuleDef, Semantics};
use ra_prof::profile; use ra_prof::profile;
use ra_syntax::{ast, AstNode, SyntaxKind::NAME}; use ra_syntax::{ast, AstNode, SyntaxKind::NAME};
@ -10,6 +10,7 @@ use crate::{
symbol_index::{self, FileSymbol, Query}, symbol_index::{self, FileSymbol, Query},
RootDatabase, RootDatabase,
}; };
use either::Either;
pub struct ImportsLocator<'a> { pub struct ImportsLocator<'a> {
sema: Semantics<'a, RootDatabase>, sema: Semantics<'a, RootDatabase>,
@ -20,7 +21,7 @@ impl<'a> ImportsLocator<'a> {
Self { sema: Semantics::new(db) } Self { sema: Semantics::new(db) }
} }
pub fn find_imports(&mut self, name_to_import: &str) -> Vec<ModuleDef> { pub fn find_imports(&mut self, name_to_import: &str) -> Vec<Either<ModuleDef, MacroDef>> {
let _p = profile("search_for_imports"); let _p = profile("search_for_imports");
let db = self.sema.db; let db = self.sema.db;
@ -43,7 +44,8 @@ impl<'a> ImportsLocator<'a> {
.chain(lib_results.into_iter()) .chain(lib_results.into_iter())
.filter_map(|import_candidate| self.get_name_definition(&import_candidate)) .filter_map(|import_candidate| self.get_name_definition(&import_candidate))
.filter_map(|name_definition_to_import| match name_definition_to_import { .filter_map(|name_definition_to_import| match name_definition_to_import {
Definition::ModuleDef(module_def) => Some(module_def), Definition::ModuleDef(module_def) => Some(Either::Left(module_def)),
Definition::Macro(macro_def) => Some(Either::Right(macro_def)),
_ => None, _ => None,
}) })
.collect() .collect()