use ide_db::RootDatabase;
use syntax::{
ast,
- ast::{make, ArgListOwner},
- AstNode,
+ ast::{make, HasArgList},
+ AstNode, NodeOrToken,
};
use crate::{
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 candidate = import_assets.import_candidate();
- let qualify_candidate = match candidate {
- ImportCandidate::Path(candidate) if candidate.qualifier.is_some() => {
- cov_mark::hit!(qualify_path_qualifier_start);
- let path = ast::Path::cast(syntax_under_caret)?;
- let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?);
- QualifyCandidate::QualifierStart(segment, prev_segment.generic_arg_list())
- }
- ImportCandidate::Path(_) => {
- cov_mark::hit!(qualify_path_unqualified_name);
- let path = ast::Path::cast(syntax_under_caret)?;
- let generics = path.segment()?.generic_arg_list();
- QualifyCandidate::UnqualifiedName(generics)
- }
- ImportCandidate::TraitAssocItem(_) => {
- cov_mark::hit!(qualify_path_trait_assoc_item);
- let path = ast::Path::cast(syntax_under_caret)?;
- let (qualifier, segment) = (path.qualifier()?, path.segment()?);
- QualifyCandidate::TraitAssocItem(qualifier, segment)
- }
- ImportCandidate::TraitMethod(_) => {
- cov_mark::hit!(qualify_path_trait_method);
- let mcall_expr = ast::MethodCallExpr::cast(syntax_under_caret)?;
- QualifyCandidate::TraitMethod(ctx.sema.db, mcall_expr)
- }
+ let qualify_candidate = match syntax_under_caret {
+ NodeOrToken::Node(syntax_under_caret) => match candidate {
+ ImportCandidate::Path(candidate) if candidate.qualifier.is_some() => {
+ cov_mark::hit!(qualify_path_qualifier_start);
+ let path = ast::Path::cast(syntax_under_caret)?;
+ let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?);
+ QualifyCandidate::QualifierStart(segment, prev_segment.generic_arg_list())
+ }
+ ImportCandidate::Path(_) => {
+ cov_mark::hit!(qualify_path_unqualified_name);
+ let path = ast::Path::cast(syntax_under_caret)?;
+ let generics = path.segment()?.generic_arg_list();
+ QualifyCandidate::UnqualifiedName(generics)
+ }
+ ImportCandidate::TraitAssocItem(_) => {
+ cov_mark::hit!(qualify_path_trait_assoc_item);
+ let path = ast::Path::cast(syntax_under_caret)?;
+ let (qualifier, segment) = (path.qualifier()?, path.segment()?);
+ QualifyCandidate::TraitAssocItem(qualifier, segment)
+ }
+ ImportCandidate::TraitMethod(_) => {
+ cov_mark::hit!(qualify_path_trait_method);
+ let mcall_expr = ast::MethodCallExpr::cast(syntax_under_caret)?;
+ QualifyCandidate::TraitMethod(ctx.sema.db, mcall_expr)
+ }
+ },
+ // derive attribute path
+ NodeOrToken::Token(_) => QualifyCandidate::UnqualifiedName(None),
};
// we aren't interested in different namespaces
}
Some(())
}
-
-enum QualifyCandidate<'db> {
+pub(crate) enum QualifyCandidate<'db> {
QualifierStart(ast::PathSegment, Option<ast::GenericArgList>),
UnqualifiedName(Option<ast::GenericArgList>),
TraitAssocItem(ast::Path, ast::PathSegment),
TraitMethod(&'db RootDatabase, ast::MethodCallExpr),
+ ImplMethod(&'db RootDatabase, ast::MethodCallExpr, hir::Function),
}
impl QualifyCandidate<'_> {
- fn qualify(
+ pub(crate) fn qualify(
&self,
mut replacer: impl FnMut(String),
import: &hir::ModPath,
}
QualifyCandidate::UnqualifiedName(generics) => {
let generics = generics.as_ref().map_or_else(String::new, ToString::to_string);
- replacer(format!("{}{}", import.to_string(), generics));
+ replacer(format!("{}{}", import, generics));
}
QualifyCandidate::TraitAssocItem(qualifier, segment) => {
replacer(format!("<{} as {}>::{}", qualifier, import, segment));
QualifyCandidate::TraitMethod(db, mcall_expr) => {
Self::qualify_trait_method(db, mcall_expr, replacer, import, item);
}
+ QualifyCandidate::ImplMethod(db, mcall_expr, hir_fn) => {
+ Self::qualify_fn_call(db, mcall_expr, replacer, import, hir_fn);
+ }
}
}
- fn qualify_trait_method(
+ fn qualify_fn_call(
db: &RootDatabase,
mcall_expr: &ast::MethodCallExpr,
mut replacer: impl FnMut(String),
import: ast::Path,
- item: hir::ItemInNs,
+ hir_fn: &hir::Function,
) -> Option<()> {
let receiver = mcall_expr.receiver()?;
- let trait_method_name = mcall_expr.name_ref()?;
+ let method_name = mcall_expr.name_ref()?;
let generics =
mcall_expr.generic_arg_list().as_ref().map_or_else(String::new, ToString::to_string);
let arg_list = mcall_expr.arg_list().map(|arg_list| arg_list.args());
- let trait_ = item_as_trait(db, item)?;
- let method = find_trait_method(db, trait_, &trait_method_name)?;
- if let Some(self_access) = method.self_param(db).map(|sp| sp.access(db)) {
+
+ if let Some(self_access) = hir_fn.self_param(db).map(|sp| sp.access(db)) {
let receiver = match self_access {
hir::Access::Shared => make::expr_ref(receiver, false),
hir::Access::Exclusive => make::expr_ref(receiver, true),
replacer(format!(
"{}::{}{}{}",
import,
- trait_method_name,
+ method_name,
generics,
match arg_list {
Some(args) => make::arg_list(iter::once(receiver).chain(args)),
}
Some(())
}
+
+ fn qualify_trait_method(
+ db: &RootDatabase,
+ mcall_expr: &ast::MethodCallExpr,
+ replacer: impl FnMut(String),
+ import: ast::Path,
+ item: hir::ItemInNs,
+ ) -> Option<()> {
+ let trait_method_name = mcall_expr.name_ref()?;
+ let trait_ = item_as_trait(db, item)?;
+ let method = find_trait_method(db, trait_, &trait_method_name)?;
+ Self::qualify_fn_call(db, mcall_expr, replacer, import, &method)
+ }
}
fn find_trait_method(
fn item_as_trait(db: &RootDatabase, item: hir::ItemInNs) -> Option<hir::Trait> {
let item_module_def = item.as_module_def()?;
- if let hir::ModuleDef::Trait(trait_) = item_module_def {
- Some(trait_)
- } else {
- item_module_def.as_assoc_item(db)?.containing_trait(db)
+ match item_module_def {
+ hir::ModuleDef::Trait(trait_) => Some(trait_),
+ _ => item_module_def.as_assoc_item(db)?.containing_trait(db),
}
}
let test_struct = test_mod::TestStruct {};
test_mod::TestTrait::test_method::<()>(&test_struct)
}
+"#,
+ );
+ }
+
+ #[test]
+ fn works_in_derives() {
+ check_assist(
+ qualify_path,
+ r#"
+//- minicore:derive
+mod foo {
+ #[rustc_builtin_macro]
+ pub macro Copy {}
+}
+#[derive(Copy$0)]
+struct Foo;
+"#,
+ r#"
+mod foo {
+ #[rustc_builtin_macro]
+ pub macro Copy {}
+}
+#[derive(foo::Copy)]
+struct Foo;
"#,
);
}