insert_use::{insert_use, ImportScope},
mod_path_to_ast,
};
-use syntax::{ast, AstNode, SyntaxNode};
+use syntax::{ast, AstNode, AstToken, NodeOrToken, SyntaxElement};
use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel};
// ```
pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
let (import_assets, syntax_under_caret) = find_importable_node(ctx)?;
- let proposed_imports =
+ let mut proposed_imports =
import_assets.search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind);
if proposed_imports.is_empty() {
return None;
}
- let range = ctx.sema.original_range(&syntax_under_caret).range;
+ let range = match &syntax_under_caret {
+ NodeOrToken::Node(node) => ctx.sema.original_range(node).range,
+ NodeOrToken::Token(token) => token.text_range(),
+ };
let group_label = group_label(import_assets.import_candidate());
- let scope = ImportScope::find_insert_use_container_with_macros(&syntax_under_caret, &ctx.sema)?;
+ let scope = ImportScope::find_insert_use_container(
+ &match syntax_under_caret {
+ NodeOrToken::Node(it) => it,
+ NodeOrToken::Token(it) => it.parent()?,
+ },
+ &ctx.sema,
+ )?;
+
+ // we aren't interested in different namespaces
+ proposed_imports.dedup_by(|a, b| a.import_path == b.import_path);
for import in proposed_imports {
acc.add_group(
&group_label,
let scope = match scope.clone() {
ImportScope::File(it) => ImportScope::File(builder.make_mut(it)),
ImportScope::Module(it) => ImportScope::Module(builder.make_mut(it)),
+ ImportScope::Block(it) => ImportScope::Block(builder.make_mut(it)),
};
insert_use(&scope, mod_path_to_ast(&import.import_path), &ctx.config.insert_use);
},
Some(())
}
-pub(super) fn find_importable_node(ctx: &AssistContext) -> Option<(ImportAssets, SyntaxNode)> {
+pub(super) fn find_importable_node(ctx: &AssistContext) -> Option<(ImportAssets, SyntaxElement)> {
if let Some(path_under_caret) = ctx.find_node_at_offset_with_descend::<ast::Path>() {
ImportAssets::for_exact_path(&path_under_caret, &ctx.sema)
- .zip(Some(path_under_caret.syntax().clone()))
+ .zip(Some(path_under_caret.syntax().clone().into()))
} else if let Some(method_under_caret) =
ctx.find_node_at_offset_with_descend::<ast::MethodCallExpr>()
{
ImportAssets::for_method_call(&method_under_caret, &ctx.sema)
- .zip(Some(method_under_caret.syntax().clone()))
+ .zip(Some(method_under_caret.syntax().clone().into()))
+ } else if let Some(pat) = ctx
+ .find_node_at_offset_with_descend::<ast::IdentPat>()
+ .filter(ast::IdentPat::is_simple_ident)
+ {
+ ImportAssets::for_ident_pat(&ctx.sema, &pat).zip(Some(pat.syntax().clone().into()))
} else {
- None
+ // FIXME: Descend?
+ let ident = ctx.find_token_at_offset()?;
+ ImportAssets::for_derive_ident(&ctx.sema, &ident).zip(Some(ident.syntax().clone().into()))
}
}
#[cfg(test)]
mod tests {
use super::*;
+
use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
+ #[test]
+ fn not_applicable_if_scope_inside_macro() {
+ check_assist_not_applicable(
+ auto_import,
+ r"
+mod bar {
+ pub struct Baz;
+}
+macro_rules! foo {
+ ($it:ident) => {
+ mod __ {
+ fn __(x: $it) {}
+ }
+ };
+}
+foo! {
+ Baz$0
+}
+",
+ );
+ }
+
+ #[test]
+ fn applicable_in_attributes() {
+ check_assist(
+ auto_import,
+ r"
+//- proc_macros: identity
+#[proc_macros::identity]
+mod foo {
+ mod bar {
+ const _: Baz$0 = ();
+ }
+}
+mod baz {
+ pub struct Baz;
+}
+",
+ r"
+#[proc_macros::identity]
+mod foo {
+ mod bar {
+ use crate::baz::Baz;
+
+ const _: Baz = ();
+ }
+}
+mod baz {
+ pub struct Baz;
+}
+",
+ );
+ }
+
#[test]
fn applicable_when_found_an_import_partial() {
check_assist(
#[test]
fn uses_abs_path_with_extern_crate_clash() {
+ cov_mark::check!(ambiguous_crate_start);
check_assist(
auto_import,
r#"
const _: () = {
Foo
};
+"#,
+ );
+ }
+
+ #[test]
+ fn works_on_ident_patterns() {
+ check_assist(
+ auto_import,
+ r#"
+mod foo {
+ pub struct Foo {}
+}
+fn foo() {
+ let Foo$0;
+}
+"#,
+ r#"
+use foo::Foo;
+
+mod foo {
+ pub struct Foo {}
+}
+fn foo() {
+ let Foo;
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn works_in_derives() {
+ check_assist(
+ auto_import,
+ r#"
+//- minicore:derive
+mod foo {
+ #[rustc_builtin_macro]
+ pub macro Copy {}
+}
+#[derive(Copy$0)]
+struct Foo;
+"#,
+ r#"
+use foo::Copy;
+
+mod foo {
+ #[rustc_builtin_macro]
+ pub macro Copy {}
+}
+#[derive(Copy)]
+struct Foo;
"#,
);
}