]> git.lizzy.rs Git - rust.git/blobdiff - crates/ide_assists/src/handlers/qualify_path.rs
Merge #11481
[rust.git] / crates / ide_assists / src / handlers / qualify_path.rs
index 2510eae97143460e51440937ccab22449d49da3f..ae29068bd702a1d1ece31460afa035d630cba641 100644 (file)
@@ -8,8 +8,8 @@
 use ide_db::RootDatabase;
 use syntax::{
     ast,
-    ast::{make, ArgListOwner},
-    AstNode,
+    ast::{make, HasArgList},
+    AstNode, NodeOrToken,
 };
 
 use crate::{
@@ -42,32 +42,39 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
         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
@@ -91,16 +98,16 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
     }
     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,
@@ -114,7 +121,7 @@ fn qualify(
             }
             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));
@@ -122,24 +129,26 @@ fn qualify(
             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),
@@ -148,7 +157,7 @@ fn qualify_trait_method(
             replacer(format!(
                 "{}::{}{}{}",
                 import,
-                trait_method_name,
+                method_name,
                 generics,
                 match arg_list {
                     Some(args) => make::arg_list(iter::once(receiver).chain(args)),
@@ -158,6 +167,19 @@ fn qualify_trait_method(
         }
         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(
@@ -181,10 +203,9 @@ 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),
     }
 }
 
@@ -1222,6 +1243,30 @@ fn main() {
     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;
 "#,
         );
     }