]> git.lizzy.rs Git - rust.git/blobdiff - crates/hir_expand/src/lib.rs
Merge #11182
[rust.git] / crates / hir_expand / src / lib.rs
index bca380a4d6f8e6c660d2f07393ed958068eb8b6b..51899ca2f65b0ab612b3c32bdfec694dfe9afef4 100644 (file)
 pub mod proc_macro;
 pub mod quote;
 pub mod eager;
+pub mod mod_path;
 
 use base_db::ProcMacroKind;
 use either::Either;
 
 pub use mbe::{ExpandError, ExpandResult, Origin};
+use mod_path::ModPath;
 
 use std::{hash::Hash, iter, sync::Arc};
 
 use base_db::{impl_intern_key, salsa, CrateId, FileId, FileRange};
 use syntax::{
     algo::{self, skip_trivia_token},
-    ast::{self, AstNode, HasAttrs},
+    ast::{self, AstNode, HasDocComments},
     Direction, SyntaxNode, SyntaxToken,
 };
 
@@ -121,7 +123,7 @@ pub enum MacroCallKind {
         expand_to: ExpandTo,
     },
     Derive {
-        ast_id: AstId<ast::Item>,
+        ast_id: AstId<ast::Adt>,
         derive_name: Box<str>,
         /// Syntactical index of the invoking `#[derive]` attribute.
         ///
@@ -201,8 +203,9 @@ pub fn expansion_info(self, db: &dyn db::AstDatabase) -> Option<ExpansionInfo> {
                     MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => {
                         let tt = ast_id
                             .to_node(db)
-                            .attrs()
-                            .nth(invoc_attr_index as usize)?
+                            .doc_comments_and_attrs()
+                            .nth(invoc_attr_index as usize)
+                            .and_then(Either::left)?
                             .token_tree()?;
                         Some(InFile::new(ast_id.file_id, tt))
                     }
@@ -328,11 +331,10 @@ pub fn is_attribute(&self) -> bool {
 impl MacroCallKind {
     /// Returns the file containing the macro invocation.
     fn file_id(&self) -> HirFileId {
-        match self {
-            MacroCallKind::FnLike { ast_id, .. } => ast_id.file_id,
-            MacroCallKind::Derive { ast_id, .. } | MacroCallKind::Attr { ast_id, .. } => {
-                ast_id.file_id
-            }
+        match *self {
+            MacroCallKind::FnLike { ast_id: InFile { file_id, .. }, .. }
+            | MacroCallKind::Derive { ast_id: InFile { file_id, .. }, .. }
+            | MacroCallKind::Attr { ast_id: InFile { file_id, .. }, .. } => file_id,
         }
     }
 
@@ -341,10 +343,60 @@ pub fn to_node(&self, db: &dyn db::AstDatabase) -> InFile<SyntaxNode> {
             MacroCallKind::FnLike { ast_id, .. } => {
                 ast_id.with_value(ast_id.to_node(db).syntax().clone())
             }
-            MacroCallKind::Derive { ast_id, .. } | MacroCallKind::Attr { ast_id, .. } => {
+            MacroCallKind::Derive { ast_id, .. } => {
                 ast_id.with_value(ast_id.to_node(db).syntax().clone())
             }
+            MacroCallKind::Attr { ast_id, .. } => {
+                ast_id.with_value(ast_id.to_node(db).syntax().clone())
+            }
+        }
+    }
+
+    /// Returns the original file range that best describes the location of this macro call.
+    ///
+    /// Here we try to roughly match what rustc does to improve diagnostics: fn-like macros
+    /// get the whole `ast::MacroCall`, attribute macros get the attribute's range, and derives
+    /// get only the specific derive that is being referred to.
+    pub fn original_call_range(self, db: &dyn db::AstDatabase) -> FileRange {
+        let mut kind = self;
+        loop {
+            match kind.file_id().0 {
+                HirFileIdRepr::MacroFile(file) => {
+                    kind = db.lookup_intern_macro_call(file.macro_call_id).kind;
+                }
+                _ => break,
+            }
         }
+
+        // `call_id` is now the outermost macro call, so its location is in a real file.
+        let file_id = match kind.file_id().0 {
+            HirFileIdRepr::FileId(it) => it,
+            HirFileIdRepr::MacroFile(_) => unreachable!("encountered unexpected macro file"),
+        };
+        let range = match kind {
+            MacroCallKind::FnLike { ast_id, .. } => ast_id.to_node(db).syntax().text_range(),
+            MacroCallKind::Derive { ast_id, derive_attr_index, .. } => {
+                // FIXME: should be the range of the macro name, not the whole derive
+                ast_id
+                    .to_node(db)
+                    .doc_comments_and_attrs()
+                    .nth(derive_attr_index as usize)
+                    .expect("missing derive")
+                    .expect_left("derive is a doc comment?")
+                    .syntax()
+                    .text_range()
+            }
+            MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => ast_id
+                .to_node(db)
+                .doc_comments_and_attrs()
+                .nth(invoc_attr_index as usize)
+                .expect("missing attribute")
+                .expect_left("attribute macro is a doc comment?")
+                .syntax()
+                .text_range(),
+        };
+
+        FileRange { range, file_id }
     }
 
     fn arg(&self, db: &dyn db::AstDatabase) -> Option<SyntaxNode> {
@@ -352,9 +404,8 @@ fn arg(&self, db: &dyn db::AstDatabase) -> Option<SyntaxNode> {
             MacroCallKind::FnLike { ast_id, .. } => {
                 Some(ast_id.to_node(db).token_tree()?.syntax().clone())
             }
-            MacroCallKind::Derive { ast_id, .. } | MacroCallKind::Attr { ast_id, .. } => {
-                Some(ast_id.to_node(db).syntax().clone())
-            }
+            MacroCallKind::Derive { ast_id, .. } => Some(ast_id.to_node(db).syntax().clone()),
+            MacroCallKind::Attr { ast_id, .. } => Some(ast_id.to_node(db).syntax().clone()),
         }
     }
 
@@ -428,8 +479,11 @@ pub fn map_token_down(
 
             let token_range = token.value.text_range();
             match &loc.kind {
-                MacroCallKind::Attr { attr_args, invoc_attr_index, .. } => {
-                    let attr = item.attrs().nth(*invoc_attr_index as usize)?;
+                MacroCallKind::Attr { attr_args: (_, map), invoc_attr_index, .. } => {
+                    let attr = item
+                        .doc_comments_and_attrs()
+                        .nth(*invoc_attr_index as usize)
+                        .and_then(Either::left)?;
                     match attr.token_tree() {
                         Some(token_tree)
                             if token_tree.syntax().text_range().contains_range(token_range) =>
@@ -439,9 +493,8 @@ pub fn map_token_down(
                             let relative_range =
                                 token.value.text_range().checked_sub(attr_input_start)?;
                             // shift by the item's tree's max id
-                            let token_id = self
-                                .macro_arg_shift
-                                .shift(attr_args.1.token_by_range(relative_range)?);
+                            let token_id =
+                                self.macro_arg_shift.shift(map.token_by_range(relative_range)?);
                             Some(token_id)
                         }
                         _ => None,
@@ -578,17 +631,14 @@ pub fn transpose(self) -> Option<InFile<T>> {
     }
 }
 
-impl InFile<SyntaxNode> {
+impl<'a> InFile<&'a SyntaxNode> {
     pub fn ancestors_with_macros(
         self,
         db: &dyn db::AstDatabase,
     ) -> impl Iterator<Item = InFile<SyntaxNode>> + Clone + '_ {
-        iter::successors(Some(self), move |node| match node.value.parent() {
+        iter::successors(Some(self.cloned()), move |node| match node.value.parent() {
             Some(parent) => Some(node.with_value(parent)),
-            None => {
-                let parent_node = node.file_id.call_node(db)?;
-                Some(parent_node)
-            }
+            None => node.file_id.call_node(db),
         })
     }
 
@@ -597,7 +647,7 @@ pub fn ancestors_with_macros_skip_attr_item(
         self,
         db: &dyn db::AstDatabase,
     ) -> impl Iterator<Item = InFile<SyntaxNode>> + '_ {
-        iter::successors(Some(self), move |node| match node.value.parent() {
+        iter::successors(Some(self.cloned()), move |node| match node.value.parent() {
             Some(parent) => Some(node.with_value(parent)),
             None => {
                 let parent_node = node.file_id.call_node(db)?;
@@ -611,9 +661,7 @@ pub fn ancestors_with_macros_skip_attr_item(
             }
         })
     }
-}
 
-impl<'a> InFile<&'a SyntaxNode> {
     /// Falls back to the macro call range if the node cannot be mapped up fully.
     pub fn original_file_range(self, db: &dyn db::AstDatabase) -> FileRange {
         if let Some(res) = self.original_file_range_opt(db) {
@@ -621,15 +669,13 @@ pub fn original_file_range(self, db: &dyn db::AstDatabase) -> FileRange {
         }
 
         // Fall back to whole macro call.
-        let mut node = self.cloned();
-        while let Some(call_node) = node.file_id.call_node(db) {
-            node = call_node;
+        match self.file_id.0 {
+            HirFileIdRepr::FileId(file_id) => FileRange { file_id, range: self.value.text_range() },
+            HirFileIdRepr::MacroFile(mac_file) => {
+                let loc = db.lookup_intern_macro_call(mac_file.macro_call_id);
+                loc.kind.original_call_range(db)
+            }
         }
-
-        let orig_file = node.file_id.original_file(db);
-        assert_eq!(node.file_id, orig_file.into());
-
-        FileRange { file_id: orig_file, range: node.value.text_range() }
     }
 
     /// Attempts to map the syntax node back up its macro calls.
@@ -701,7 +747,7 @@ pub fn ancestors_with_macros(
     ) -> impl Iterator<Item = InFile<SyntaxNode>> + '_ {
         self.value.parent().into_iter().flat_map({
             let file_id = self.file_id;
-            move |parent| InFile::new(file_id, parent).ancestors_with_macros(db)
+            move |parent| InFile::new(file_id, &parent).ancestors_with_macros(db)
         })
     }
 }
@@ -791,3 +837,8 @@ pub fn from_call_site(call: &ast::MacroCall) -> ExpandTo {
         }
     }
 }
+
+#[derive(Debug)]
+pub struct UnresolvedMacro {
+    pub path: ModPath,
+}