]> git.lizzy.rs Git - rust.git/blobdiff - crates/hir/src/semantics.rs
Auto merge of #12895 - Veykril:compl-anchor, r=Veykril
[rust.git] / crates / hir / src / semantics.rs
index 043f2b7c24dcb42adbf82307e9341543d5f91460..c84318b2fb8774123dbe91b69df2aae53da34547 100644 (file)
@@ -29,9 +29,9 @@
     db::HirDatabase,
     semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
     source_analyzer::{resolve_hir_path, SourceAnalyzer},
-    Access, BindingMode, BuiltinAttr, Callable, ConstParam, Crate, Field, Function, HasSource,
-    HirFileId, Impl, InFile, Label, LifetimeParam, Local, Macro, Module, ModuleDef, Name, Path,
-    ScopeDef, ToolModule, Trait, Type, TypeAlias, TypeParam, VariantDef,
+    Access, BindingMode, BuiltinAttr, Callable, ConstParam, Crate, DeriveHelper, Field, Function,
+    HasSource, HirFileId, Impl, InFile, Label, LifetimeParam, Local, Macro, Module, ModuleDef,
+    Name, Path, ScopeDef, ToolModule, Trait, Type, TypeAlias, TypeParam, VariantDef,
 };
 
 #[derive(Debug, Clone, PartialEq, Eq)]
@@ -47,6 +47,7 @@ pub enum PathResolution {
     SelfType(Impl),
     BuiltinAttr(BuiltinAttr),
     ToolModule(ToolModule),
+    DeriveHelper(DeriveHelper),
 }
 
 impl PathResolution {
@@ -71,6 +72,7 @@ pub(crate) fn in_type_ns(&self) -> Option<TypeNs> {
             PathResolution::BuiltinAttr(_)
             | PathResolution::ToolModule(_)
             | PathResolution::Local(_)
+            | PathResolution::DeriveHelper(_)
             | PathResolution::ConstParam(_) => None,
             PathResolution::TypeParam(param) => Some(TypeNs::GenericParam((*param).into())),
             PathResolution::SelfType(impl_def) => Some(TypeNs::SelfType((*impl_def).into())),
@@ -322,6 +324,10 @@ pub fn resolve_type(&self, ty: &ast::Type) -> Option<Type> {
         self.imp.resolve_type(ty)
     }
 
+    pub fn resolve_trait(&self, trait_: &ast::Path) -> Option<Trait> {
+        self.imp.resolve_trait(trait_)
+    }
+
     // FIXME: Figure out a nice interface to inspect adjustments
     pub fn is_implicit_reborrow(&self, expr: &ast::Expr) -> Option<Mutability> {
         self.imp.is_implicit_reborrow(expr)
@@ -733,6 +739,8 @@ fn descend_into_macros_impl(
             Some(it) => it,
             None => return,
         };
+        let def_map = sa.resolver.def_map();
+
         let mut stack: SmallVec<[_; 4]> = smallvec![InFile::new(sa.file_id, token)];
         let mut cache = self.expansion_info_cache.borrow_mut();
         let mut mcache = self.macro_call_cache.borrow_mut();
@@ -764,7 +772,7 @@ fn descend_into_macros_impl(
         while let Some(token) = stack.pop() {
             self.db.unwind_if_cancelled();
             let was_not_remapped = (|| {
-                // are we inside an attribute macro call
+                // First expand into attribute invocations
                 let containing_attribute_macro_call = self.with_ctx(|ctx| {
                     token.value.parent_ancestors().filter_map(ast::Item::cast).find_map(|item| {
                         if item.attrs().next().is_none() {
@@ -784,34 +792,49 @@ fn descend_into_macros_impl(
                     );
                 }
 
-                // or are we inside a function-like macro call
-                if let Some(tt) =
-                    // FIXME replace map.while_some with take_while once stable
-                    token
-                        .value
-                        .parent_ancestors()
-                        .map(ast::TokenTree::cast)
-                        .while_some()
-                        .last()
-                {
-                    let parent = tt.syntax().parent()?;
-                    // check for derive attribute here
-                    let macro_call = match_ast! {
-                        match parent {
-                            ast::MacroCall(mcall) => mcall,
-                            // attribute we failed expansion for earlier, this might be a derive invocation
+                // Then check for token trees, that means we are either in a function-like macro or
+                // secondary attribute inputs
+                let tt = token.value.parent_ancestors().map_while(ast::TokenTree::cast).last()?;
+                let parent = tt.syntax().parent()?;
+
+                if tt.left_delimiter_token().map_or(false, |it| it == token.value) {
+                    return None;
+                }
+                if tt.right_delimiter_token().map_or(false, |it| it == token.value) {
+                    return None;
+                }
+
+                if let Some(macro_call) = ast::MacroCall::cast(parent.clone()) {
+                    let mcall = token.with_value(macro_call);
+                    let file_id = match mcache.get(&mcall) {
+                        Some(&it) => it,
+                        None => {
+                            let it = sa.expand(self.db, mcall.as_ref())?;
+                            mcache.insert(mcall, it);
+                            it
+                        }
+                    };
+                    process_expansion_for_token(&mut stack, file_id, None, token.as_ref())
+                } else if let Some(meta) = ast::Meta::cast(parent.clone()) {
+                    // attribute we failed expansion for earlier, this might be a derive invocation
+                    // or derive helper attribute
+                    let attr = meta.parent_attr()?;
+
+                    let adt = if let Some(adt) = attr.syntax().parent().and_then(ast::Adt::cast) {
+                        // this might be a derive, or a derive helper on an ADT
+                        let derive_call = self.with_ctx(|ctx| {
                             // so try downmapping the token into the pseudo derive expansion
                             // see [hir_expand::builtin_attr_macro] for how the pseudo derive expansion works
-                            ast::Meta(meta) => {
-                                let attr = meta.parent_attr()?;
-                                let adt = attr.syntax().parent().and_then(ast::Adt::cast)?;
-                                let call_id = self.with_ctx(|ctx| {
-                                    let (_, call_id, _) = ctx.attr_to_derive_macro_call(
-                                        token.with_value(&adt),
-                                        token.with_value(attr),
-                                    )?;
-                                    Some(call_id)
-                                })?;
+                            ctx.attr_to_derive_macro_call(
+                                token.with_value(&adt),
+                                token.with_value(attr.clone()),
+                            )
+                            .map(|(_, call_id, _)| call_id)
+                        });
+
+                        match derive_call {
+                            Some(call_id) => {
+                                // resolved to a derive
                                 let file_id = call_id.as_file();
                                 return process_expansion_for_token(
                                     &mut stack,
@@ -819,32 +842,49 @@ fn descend_into_macros_impl(
                                     Some(adt.into()),
                                     token.as_ref(),
                                 );
-                            },
-                            _ => return None,
+                            }
+                            None => Some(adt),
                         }
-                    };
-
-                    if tt.left_delimiter_token().map_or(false, |it| it == token.value) {
+                    } else {
+                        // Otherwise this could be a derive helper on a variant or field
+                        if let Some(field) = attr.syntax().parent().and_then(ast::RecordField::cast)
+                        {
+                            field.syntax().ancestors().take(4).find_map(ast::Adt::cast)
+                        } else if let Some(field) =
+                            attr.syntax().parent().and_then(ast::TupleField::cast)
+                        {
+                            field.syntax().ancestors().take(4).find_map(ast::Adt::cast)
+                        } else if let Some(variant) =
+                            attr.syntax().parent().and_then(ast::Variant::cast)
+                        {
+                            variant.syntax().ancestors().nth(2).and_then(ast::Adt::cast)
+                        } else {
+                            None
+                        }
+                    }?;
+                    if !self.with_ctx(|ctx| ctx.has_derives(InFile::new(token.file_id, &adt))) {
                         return None;
                     }
-                    if tt.right_delimiter_token().map_or(false, |it| it == token.value) {
-                        return None;
+                    // Not an attribute, nor a derive, so it's either a builtin or a derive helper
+                    // Try to resolve to a derive helper and downmap
+                    let attr_name = attr.path().and_then(|it| it.as_single_name_ref())?.as_name();
+                    let id = self.db.ast_id_map(token.file_id).ast_id(&adt);
+                    let helpers =
+                        def_map.derive_helpers_in_scope(InFile::new(token.file_id, id))?;
+                    let item = Some(adt.into());
+                    let mut res = None;
+                    for (.., derive) in helpers.iter().filter(|(helper, ..)| *helper == attr_name) {
+                        res = res.or(process_expansion_for_token(
+                            &mut stack,
+                            derive.as_file(),
+                            item.clone(),
+                            token.as_ref(),
+                        ));
                     }
-
-                    let mcall = token.with_value(macro_call);
-                    let file_id = match mcache.get(&mcall) {
-                        Some(&it) => it,
-                        None => {
-                            let it = sa.expand(self.db, mcall.as_ref())?;
-                            mcache.insert(mcall, it);
-                            it
-                        }
-                    };
-                    return process_expansion_for_token(&mut stack, file_id, None, token.as_ref());
+                    res
+                } else {
+                    None
                 }
-
-                // outside of a macro invocation so this is a "final" token
-                None
             })()
             .is_none();
 
@@ -888,7 +928,12 @@ fn original_range_opt(&self, node: &SyntaxNode) -> Option<FileRange> {
     }
 
     fn original_ast_node<N: AstNode>(&self, node: N) -> Option<N> {
-        self.wrap_node_infile(node).original_ast_node(self.db.upcast()).map(|it| it.value)
+        self.wrap_node_infile(node).original_ast_node(self.db.upcast()).map(
+            |InFile { file_id, value }| {
+                self.cache(find_root(value.syntax()), file_id);
+                value
+            },
+        )
     }
 
     fn diagnostics_display_range(&self, src: InFile<SyntaxNodePtr>) -> FileRange {
@@ -973,6 +1018,20 @@ fn resolve_type(&self, ty: &ast::Type) -> Option<Type> {
         Some(Type::new_with_resolver(self.db, &analyze.resolver, ty))
     }
 
+    fn resolve_trait(&self, path: &ast::Path) -> Option<Trait> {
+        let analyze = self.analyze(path.syntax())?;
+        let hygiene = hir_expand::hygiene::Hygiene::new(self.db.upcast(), analyze.file_id);
+        let ctx = body::LowerCtx::with_hygiene(self.db.upcast(), &hygiene);
+        let hir_path = Path::from_src(path.clone(), &ctx)?;
+        match analyze
+            .resolver
+            .resolve_path_in_type_ns_fully(self.db.upcast(), hir_path.mod_path())?
+        {
+            TypeNs::TraitId(id) => Some(Trait { id }),
+            _ => None,
+        }
+    }
+
     fn is_implicit_reborrow(&self, expr: &ast::Expr) -> Option<Mutability> {
         self.analyze(expr.syntax())?.is_implicit_reborrow(self.db, expr)
     }