]> git.lizzy.rs Git - rust.git/blobdiff - crates/hir/src/semantics.rs
Fix syntax highlighting not highlighting derives anymore
[rust.git] / crates / hir / src / semantics.rs
index 9596e818185a05c71a420b2dc5737049990daf3f..2e519158162249be65d910ae2a2e4d56d54b3ea8 100644 (file)
@@ -5,31 +5,29 @@
 use std::{cell::RefCell, fmt, iter};
 
 use base_db::{FileId, FileRange};
-use either::Either;
 use hir_def::{
     body,
     resolver::{self, HasResolver, Resolver, TypeNs},
     AsMacroCall, FunctionId, TraitId, VariantId,
 };
 use hir_expand::{name::AsName, ExpansionInfo, MacroCallId};
-use hir_ty::{associated_type_shorthand_candidates, Interner};
+use hir_ty::Interner;
 use itertools::Itertools;
 use rustc_hash::{FxHashMap, FxHashSet};
 use smallvec::{smallvec, SmallVec};
 use syntax::{
     algo::skip_trivia_token,
     ast::{self, HasAttrs as _, HasGenericParams, HasLoopBody},
-    match_ast, AstNode, AstToken, Direction, SyntaxElement, SyntaxNode, SyntaxNodePtr, SyntaxToken,
-    TextSize, T,
+    match_ast, AstNode, Direction, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextSize,
 };
 
 use crate::{
     db::HirDatabase,
     semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
     source_analyzer::{resolve_hir_path, SourceAnalyzer},
-    Access, AssocItem, BuiltinAttr, Callable, ConstParam, Crate, Field, Function, HasAttrs as _,
-    HasSource, HirFileId, Impl, InFile, Label, LifetimeParam, Local, MacroDef, Module, ModuleDef,
-    Name, Path, ScopeDef, ToolModule, Trait, Type, TypeAlias, TypeParam, VariantDef,
+    Access, AssocItem, BuiltinAttr, Callable, ConstParam, Crate, Field, Function, HasSource,
+    HirFileId, Impl, InFile, Label, LifetimeParam, Local, MacroDef, Module, ModuleDef, Name, Path,
+    ScopeDef, ToolModule, Trait, Type, TypeAlias, TypeParam, VariantDef,
 };
 
 #[derive(Debug, Clone, PartialEq, Eq)]
@@ -50,7 +48,7 @@ pub enum PathResolution {
 }
 
 impl PathResolution {
-    fn in_type_ns(&self) -> Option<TypeNs> {
+    pub(crate) fn in_type_ns(&self) -> Option<TypeNs> {
         match self {
             PathResolution::Def(ModuleDef::Adt(adt)) => Some(TypeNs::AdtId((*adt).into())),
             PathResolution::Def(ModuleDef::BuiltinType(builtin)) => {
@@ -80,18 +78,6 @@ fn in_type_ns(&self) -> Option<TypeNs> {
             }
         }
     }
-
-    /// Returns an iterator over associated types that may be specified after this path (using
-    /// `Ty::Assoc` syntax).
-    pub fn assoc_type_shorthand_candidates<R>(
-        &self,
-        db: &dyn HirDatabase,
-        mut cb: impl FnMut(&Name, TypeAlias) -> Option<R>,
-    ) -> Option<R> {
-        associated_type_shorthand_candidates(db, self.in_type_ns()?, |name, _, id| {
-            cb(name, id.into())
-        })
-    }
 }
 
 #[derive(Debug)]
@@ -174,6 +160,10 @@ pub fn is_attr_macro_call(&self, item: &ast::Item) -> bool {
         self.imp.is_attr_macro_call(item)
     }
 
+    pub fn is_derive_annotated(&self, item: &ast::Adt) -> bool {
+        self.imp.is_derive_annotated(item)
+    }
+
     pub fn speculative_expand(
         &self,
         actual_macro_call: &ast::MacroCall,
@@ -219,14 +209,20 @@ pub fn hir_file_for(&self, syntax_node: &SyntaxNode) -> HirFileId {
         self.imp.find_file(syntax_node).file_id
     }
 
+    /// Attempts to map the node out of macro expanded files returning the original file range.
+    /// If upmapping is not possible, this will fall back to the range of the macro call of the
+    /// macro file the node resides in.
     pub fn original_range(&self, node: &SyntaxNode) -> FileRange {
         self.imp.original_range(node)
     }
 
+    /// Attempts to map the node out of macro expanded files returning the original file range.
     pub fn original_range_opt(&self, node: &SyntaxNode) -> Option<FileRange> {
         self.imp.original_range_opt(node)
     }
 
+    /// Attempts to map the node out of macro expanded files.
+    /// This only work for attribute expansions, as other ones do not have nodes as input.
     pub fn original_ast_node<N: AstNode>(&self, node: N) -> Option<N> {
         self.imp.original_ast_node(node)
     }
@@ -356,17 +352,6 @@ pub fn resolve_bind_pat_to_const(&self, pat: &ast::IdentPat) -> Option<ModuleDef
         self.imp.resolve_bind_pat_to_const(pat)
     }
 
-    pub fn resolve_derive_ident(
-        &self,
-        derive: &ast::Attr,
-        ident: &ast::Ident,
-    ) -> Option<PathResolution> {
-        self.imp.resolve_derive_ident(derive, ident)
-    }
-
-    // FIXME: use this instead?
-    // pub fn resolve_name_ref(&self, name_ref: &ast::NameRef) -> Option<???>;
-
     pub fn record_literal_missing_fields(&self, literal: &ast::RecordExpr) -> Vec<(Field, Type)> {
         self.imp.record_literal_missing_fields(literal)
     }
@@ -392,8 +377,8 @@ pub fn scope(&self, node: &SyntaxNode) -> SemanticsScope<'db> {
         self.imp.scope(node)
     }
 
-    pub fn scope_at_offset(&self, token: &SyntaxToken, offset: TextSize) -> SemanticsScope<'db> {
-        self.imp.scope_at_offset(&token.parent().unwrap(), offset)
+    pub fn scope_at_offset(&self, node: &SyntaxNode, offset: TextSize) -> SemanticsScope<'db> {
+        self.imp.scope_at_offset(&node, offset)
     }
 
     pub fn scope_for_def(&self, def: Trait) -> SemanticsScope<'db> {
@@ -448,7 +433,7 @@ fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> {
     }
 
     fn expand_attr_macro(&self, item: &ast::Item) -> Option<SyntaxNode> {
-        let src = self.find_file(item.syntax()).with_value(item.clone());
+        let src = self.wrap_node_infile(item.clone());
         let macro_call_id = self.with_ctx(|ctx| ctx.item_to_macro_call(src))?;
         let file_id = macro_call_id.as_file();
         let node = self.parse_or_expand(file_id)?;
@@ -484,11 +469,17 @@ fn derive_macro_calls(&self, attr: &ast::Attr) -> Option<Vec<Option<MacroCallId>
         let adt = InFile::new(file_id, &adt);
         let src = InFile::new(file_id, attr.clone());
         self.with_ctx(|ctx| {
-            let (_, res) = ctx.attr_to_derive_macro_call(adt, src)?;
+            let (.., res) = ctx.attr_to_derive_macro_call(adt, src)?;
             Some(res.to_vec())
         })
     }
 
+    fn is_derive_annotated(&self, adt: &ast::Adt) -> bool {
+        let file_id = self.find_file(adt.syntax()).file_id;
+        let adt = InFile::new(file_id, adt);
+        self.with_ctx(|ctx| ctx.has_derives(adt))
+    }
+
     fn is_attr_macro_call(&self, item: &ast::Item) -> bool {
         let file_id = self.find_file(item.syntax()).file_id;
         let src = InFile::new(file_id, item.clone());
@@ -522,8 +513,7 @@ fn speculative_expand_attr(
         speculative_args: &ast::Item,
         token_to_map: SyntaxToken,
     ) -> Option<(SyntaxNode, SyntaxToken)> {
-        let file_id = self.find_file(actual_macro_call.syntax()).file_id;
-        let macro_call = InFile::new(file_id, actual_macro_call.clone());
+        let macro_call = self.wrap_node_infile(actual_macro_call.clone());
         let macro_call_id = self.with_ctx(|ctx| ctx.item_to_macro_call(macro_call))?;
         hir_expand::db::expand_speculative(
             self.db.upcast(),
@@ -678,7 +668,27 @@ fn descend_into_macros_impl(
                     // FIXME replace map.while_some with take_while once stable
                     token.value.ancestors().map(ast::TokenTree::cast).while_some().last()
                 {
-                    let macro_call = tt.syntax().parent().and_then(ast::MacroCall::cast)?;
+                    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
+                            // so try downmapping the token into the pseudo derive expansion
+                            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)
+                                })?;
+                                let file_id = call_id.as_file();
+                                return process_expansion_for_token(&mut stack,file_id,Some(adt.into()),token.as_ref(),);
+                            },
+                            _ => return None,
+                        }
+                    };
+
                     if tt.left_delimiter_token().map_or(false, |it| it == token.value) {
                         return None;
                     }
@@ -743,8 +753,7 @@ fn original_range_opt(&self, node: &SyntaxNode) -> Option<FileRange> {
     }
 
     fn original_ast_node<N: AstNode>(&self, node: N) -> Option<N> {
-        let InFile { file_id, .. } = self.find_file(node.syntax());
-        InFile::new(file_id, node).original_ast_node(self.db.upcast()).map(|it| it.value)
+        self.wrap_node_infile(node).original_ast_node(self.db.upcast()).map(|it| it.value)
     }
 
     fn diagnostics_display_range(&self, src: InFile<SyntaxNodePtr>) -> FileRange {
@@ -795,8 +804,7 @@ fn resolve_lifetime_param(&self, lifetime: &ast::Lifetime) -> Option<LifetimePar
             gpl.lifetime_params()
                 .find(|tp| tp.lifetime().as_ref().map(|lt| lt.text()).as_ref() == Some(&text))
         })?;
-        let file_id = self.find_file(lifetime_param.syntax()).file_id;
-        let src = InFile::new(file_id, lifetime_param);
+        let src = self.wrap_node_infile(lifetime_param);
         ToDef::to_def(self, src)
     }
 
@@ -818,8 +826,7 @@ fn resolve_label(&self, lifetime: &ast::Lifetime) -> Option<Label> {
                     .map_or(false, |lt| lt.text() == text)
             })
         })?;
-        let file_id = self.find_file(label.syntax()).file_id;
-        let src = InFile::new(file_id, label);
+        let src = self.wrap_node_infile(label);
         ToDef::to_def(self, src)
     }
 
@@ -883,7 +890,7 @@ fn resolve_macro_call(&self, macro_call: &ast::MacroCall) -> Option<MacroDef> {
     }
 
     fn resolve_attr_macro_call(&self, item: &ast::Item) -> Option<MacroDef> {
-        let item_in_file = self.find_file(item.syntax()).with_value(item.clone());
+        let item_in_file = self.wrap_node_infile(item.clone());
         let macro_call_id = self.with_ctx(|ctx| ctx.item_to_macro_call(item_in_file))?;
         Some(MacroDef { id: self.db.lookup_intern_macro_call(macro_call_id).def })
     }
@@ -911,69 +918,6 @@ fn resolve_bind_pat_to_const(&self, pat: &ast::IdentPat) -> Option<ModuleDef> {
         self.analyze(pat.syntax()).resolve_bind_pat_to_const(self.db, pat)
     }
 
-    fn resolve_derive_ident(
-        &self,
-        derive: &ast::Attr,
-        ident: &ast::Ident,
-    ) -> Option<PathResolution> {
-        debug_assert!(ident.syntax().parent().and_then(ast::TokenTree::cast).is_some());
-        debug_assert!(ident.syntax().ancestors().any(|anc| anc == *derive.syntax()));
-        // derive macros are always at depth 2, tokentree -> meta -> attribute
-        let syntax = ident.syntax();
-
-        let tt = derive.token_tree()?;
-        let file = self.find_file(derive.syntax());
-        let adt = derive.syntax().parent().and_then(ast::Adt::cast)?;
-        let adt_def = ToDef::to_def(self, file.with_value(adt.clone()))?;
-        let res = self.with_ctx(|ctx| {
-            let (attr_id, derives) = ctx.attr_to_derive_macro_call(
-                file.with_value(&adt),
-                file.with_value(derive.clone()),
-            )?;
-            let attrs = adt_def.attrs(self.db);
-            let mut derive_paths = attrs[attr_id].parse_path_comma_token_tree()?;
-
-            let derive_idx = tt
-                .syntax()
-                .children_with_tokens()
-                .filter_map(SyntaxElement::into_token)
-                .take_while(|tok| tok != syntax)
-                .filter(|t| t.kind() == T![,])
-                .count();
-            let path_segment_idx = syntax
-                .siblings_with_tokens(Direction::Prev)
-                .filter_map(SyntaxElement::into_token)
-                .take_while(|tok| matches!(tok.kind(), T![:] | T![ident]))
-                .filter(|tok| tok.kind() == T![ident])
-                .count();
-
-            let mut mod_path = derive_paths.nth(derive_idx)?;
-
-            if path_segment_idx < mod_path.len() {
-                // the path for the given ident is a qualifier, resolve to module if possible
-                while path_segment_idx < mod_path.len() {
-                    mod_path.pop_segment();
-                }
-                Some(Either::Left(mod_path))
-            } else {
-                // otherwise fetch the derive
-                Some(Either::Right(derives[derive_idx]))
-            }
-        })?;
-
-        match res {
-            Either::Left(path) => resolve_hir_path(
-                self.db,
-                &self.scope(derive.syntax()).resolver,
-                &Path::from_known_path(path, []),
-            )
-            .filter(|res| matches!(res, PathResolution::Def(ModuleDef::Module(_)))),
-            Either::Right(derive) => derive
-                .map(|call| MacroDef { id: self.db.lookup_intern_macro_call(call).def })
-                .map(PathResolution::Macro),
-        }
-    }
-
     fn record_literal_missing_fields(&self, literal: &ast::RecordExpr) -> Vec<(Field, Type)> {
         self.analyze(literal.syntax())
             .record_literal_missing_fields(self.db, literal)
@@ -1083,6 +1027,11 @@ fn lookup(&self, root_node: &SyntaxNode) -> Option<HirFileId> {
         cache.get(root_node).copied()
     }
 
+    fn wrap_node_infile<N: AstNode>(&self, node: N) -> InFile<N> {
+        let InFile { file_id, .. } = self.find_file(node.syntax());
+        InFile::new(file_id, node)
+    }
+
     fn find_file<'node>(&self, node: &'node SyntaxNode) -> InFile<&'node SyntaxNode> {
         let root_node = find_root(node);
         let file_id = self.lookup(&root_node).unwrap_or_else(|| {
@@ -1310,4 +1259,20 @@ pub fn speculative_resolve(&self, path: &ast::Path) -> Option<PathResolution> {
         let path = Path::from_src(path.clone(), &ctx)?;
         resolve_hir_path(self.db, &self.resolver, &path)
     }
+
+    /// Iterates over associated types that may be specified after the given path (using
+    /// `Ty::Assoc` syntax).
+    pub fn assoc_type_shorthand_candidates<R>(
+        &self,
+        resolution: &PathResolution,
+        mut cb: impl FnMut(&Name, TypeAlias) -> Option<R>,
+    ) -> Option<R> {
+        let def = self.resolver.generic_def()?;
+        hir_ty::associated_type_shorthand_candidates(
+            self.db,
+            def,
+            resolution.in_type_ns()?,
+            |name, _, id| cb(name, id.into()),
+        )
+    }
 }