about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLukas Wirth <lukastw97@gmail.com>2022-01-31 13:56:14 +0100
committerLukas Wirth <lukastw97@gmail.com>2022-01-31 15:53:44 +0100
commit45ff51ba2206c50be7b79ec2817bb8c5a66d9b5c (patch)
tree9a17effef314480f431ddd98323ce329acd9e96a
parent5fd36880181cc8c6ed4bbb8ddaa47a94d5b9cc21 (diff)
downloadrust-45ff51ba2206c50be7b79ec2817bb8c5a66d9b5c.tar.gz
rust-45ff51ba2206c50be7b79ec2817bb8c5a66d9b5c.zip
Make more precise range macro upmapping
-rw-r--r--crates/hir/src/semantics.rs27
-rw-r--r--crates/hir/src/symbols.rs14
-rw-r--r--crates/hir_expand/src/lib.rs50
-rw-r--r--crates/ide/src/call_hierarchy.rs4
-rw-r--r--crates/ide/src/hover.rs13
-rw-r--r--crates/ide/src/navigation_target.rs44
-rw-r--r--crates/ide/src/references.rs4
-rw-r--r--crates/syntax/src/lib.rs4
8 files changed, 80 insertions, 80 deletions
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index a210574a06e..e3ff05b49af 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -219,14 +219,20 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
         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)
     }
@@ -445,7 +451,7 @@ impl<'db> SemanticsImpl<'db> {
     }
 
     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)?;
@@ -519,8 +525,7 @@ impl<'db> SemanticsImpl<'db> {
         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(),
@@ -740,8 +745,7 @@ impl<'db> SemanticsImpl<'db> {
     }
 
     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 {
@@ -792,8 +796,7 @@ impl<'db> SemanticsImpl<'db> {
             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)
     }
 
@@ -815,8 +818,7 @@ impl<'db> SemanticsImpl<'db> {
                     .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)
     }
 
@@ -880,7 +882,7 @@ impl<'db> SemanticsImpl<'db> {
     }
 
     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 })
     }
@@ -1080,6 +1082,11 @@ impl<'db> SemanticsImpl<'db> {
         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(|| {
diff --git a/crates/hir/src/symbols.rs b/crates/hir/src/symbols.rs
index f1669d52884..857c9e0ed9f 100644
--- a/crates/hir/src/symbols.rs
+++ b/crates/hir/src/symbols.rs
@@ -39,24 +39,24 @@ impl DeclarationLocation {
     }
 
     pub fn original_range(&self, db: &dyn HirDatabase) -> Option<FileRange> {
-        find_original_file_range(db, self.hir_file_id, &self.ptr)
+        let node = resolve_node(db, self.hir_file_id, &self.ptr)?;
+        Some(node.as_ref().original_file_range(db.upcast()))
     }
 
     pub fn original_name_range(&self, db: &dyn HirDatabase) -> Option<FileRange> {
-        find_original_file_range(db, self.hir_file_id, &self.name_ptr)
+        let node = resolve_node(db, self.hir_file_id, &self.name_ptr)?;
+        node.as_ref().original_file_range_opt(db.upcast())
     }
 }
 
-fn find_original_file_range(
+fn resolve_node(
     db: &dyn HirDatabase,
     file_id: HirFileId,
     ptr: &SyntaxNodePtr,
-) -> Option<FileRange> {
+) -> Option<InFile<SyntaxNode>> {
     let root = db.parse_or_expand(file_id)?;
     let node = ptr.to_node(&root);
-    let node = InFile::new(file_id, &node);
-
-    Some(node.original_file_range(db.upcast()))
+    Some(InFile::new(file_id, node))
 }
 
 #[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)]
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs
index 2750d498573..580a1225cb2 100644
--- a/crates/hir_expand/src/lib.rs
+++ b/crates/hir_expand/src/lib.rs
@@ -710,21 +710,9 @@ fn ascend_node_border_tokens(
 
     let first = first_token(node)?;
     let last = last_token(node)?;
-    let is_single_token = first == last;
-
-    node.descendants().find_map(|it| {
-        let first = first_token(&it)?;
-        let first = ascend_call_token(db, &expansion, InFile::new(file_id, first))?;
-
-        let last = last_token(&it)?;
-        let last = ascend_call_token(db, &expansion, InFile::new(file_id, last))?;
-
-        if (is_single_token && first != last) || (first.file_id != last.file_id) {
-            return None;
-        }
-
-        Some(InFile::new(first.file_id, (first.value, last.value)))
-    })
+    let first = ascend_call_token(db, &expansion, InFile::new(file_id, first))?;
+    let last = ascend_call_token(db, &expansion, InFile::new(file_id, last))?;
+    (first.file_id == last.file_id).then(|| InFile::new(first.file_id, (first.value, last.value)))
 }
 
 fn ascend_call_token(
@@ -760,20 +748,28 @@ impl<N: AstNode> InFile<N> {
     }
 
     pub fn original_ast_node(self, db: &dyn db::AstDatabase) -> Option<InFile<N>> {
-        match ascend_node_border_tokens(db, self.syntax()) {
-            Some(InFile { file_id, value: (first, last) }) => {
-                let original_file = file_id.original_file(db);
-                if file_id != original_file.into() {
-                    let range = first.text_range().cover(last.text_range());
-                    tracing::error!("Failed mapping up more for {:?}", range);
-                    return None;
-                }
-                let anc = algo::least_common_ancestor(&first.parent()?, &last.parent()?)?;
-                Some(InFile::new(file_id, anc.ancestors().find_map(N::cast)?))
+        // This kind of upmapping can only be achieved in attribute expanded files,
+        // as we don't have node inputs otherwise and  therefor can't find an `N` node in the input
+        if !self.file_id.is_macro() {
+            return Some(self);
+        } else if !self.file_id.is_attr_macro(db) {
+            return None;
+        }
+
+        if let Some(InFile { file_id, value: (first, last) }) =
+            ascend_node_border_tokens(db, self.syntax())
+        {
+            if file_id.is_macro() {
+                let range = first.text_range().cover(last.text_range());
+                tracing::error!("Failed mapping out of macro file for {:?}", range);
+                return None;
             }
-            _ if !self.file_id.is_macro() => Some(self),
-            _ => None,
+            // FIXME: This heuristic is brittle and with the right macro may select completely unrelated nodes
+            let anc = algo::least_common_ancestor(&first.parent()?, &last.parent()?)?;
+            let value = anc.ancestors().find_map(N::cast)?;
+            return Some(InFile::new(file_id, value));
         }
+        None
     }
 
     pub fn syntax(&self) -> InFile<&SyntaxNode> {
diff --git a/crates/ide/src/call_hierarchy.rs b/crates/ide/src/call_hierarchy.rs
index 31c83dbc9f9..b43de6b9e2d 100644
--- a/crates/ide/src/call_hierarchy.rs
+++ b/crates/ide/src/call_hierarchy.rs
@@ -405,7 +405,7 @@ fn caller() {
     call!(call$0ee);
 }
 "#,
-            expect![[r#"callee Function FileId(0) 152..158 152..158"#]],
+            expect![[r#"callee Function FileId(0) 144..159 152..158"#]],
             expect![[r#"caller Function FileId(0) 160..194 163..169 : [184..190]"#]],
             expect![[]],
         );
@@ -426,7 +426,7 @@ fn caller() {
     call!(callee);
 }
 "#,
-            expect![[r#"callee Function FileId(0) 152..158 152..158"#]],
+            expect![[r#"callee Function FileId(0) 144..159 152..158"#]],
             expect![[r#"caller Function FileId(0) 160..194 163..169 : [184..190]"#]],
             expect![[]],
         );
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 21bea25a5f7..21ff480df41 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -146,9 +146,10 @@ pub(crate) fn hover(
         if let Some(res) = render::keyword(sema, config, &original_token) {
             return Some(RangeInfo::new(original_token.text_range(), res));
         }
-        if let res @ Some(_) =
-            descended.iter().find_map(|token| hover_type_fallback(sema, config, token))
-        {
+        let res = descended
+            .iter()
+            .find_map(|token| hover_type_fallback(sema, config, token, &original_token));
+        if let res @ Some(_) = res {
             return res;
         }
     }
@@ -230,6 +231,7 @@ fn hover_type_fallback(
     sema: &Semantics<RootDatabase>,
     config: &HoverConfig,
     token: &SyntaxToken,
+    original_token: &SyntaxToken,
 ) -> Option<RangeInfo<HoverResult>> {
     let node = token
         .ancestors()
@@ -248,7 +250,10 @@ fn hover_type_fallback(
     };
 
     let res = render::type_info(sema, config, &expr_or_pat)?;
-    let range = sema.original_range(&node).range;
+    let range = sema
+        .original_range_opt(&node)
+        .map(|frange| frange.range)
+        .unwrap_or_else(|| original_token.text_range());
     Some(RangeInfo::new(range, res))
 }
 
diff --git a/crates/ide/src/navigation_target.rs b/crates/ide/src/navigation_target.rs
index 1c3ff062471..dd4c17df29f 100644
--- a/crates/ide/src/navigation_target.rs
+++ b/crates/ide/src/navigation_target.rs
@@ -14,7 +14,7 @@ use ide_db::{
 use ide_db::{defs::Definition, RootDatabase};
 use syntax::{
     ast::{self, HasName},
-    match_ast, AstNode, SmolStr, TextRange,
+    match_ast, AstNode, SmolStr, SyntaxNode, TextRange,
 };
 
 /// `NavigationTarget` represents an element in the editor's UI which you can
@@ -90,10 +90,8 @@ impl NavigationTarget {
         let name = module.name(db).map(|it| it.to_smol_str()).unwrap_or_default();
         if let Some(src @ InFile { value, .. }) = &module.declaration_source(db) {
             let FileRange { file_id, range: full_range } = src.syntax().original_file_range(db);
-            let focus_range = value
-                .name()
-                .and_then(|name| src.with_value(name.syntax()).original_file_range_opt(db))
-                .map(|it| it.range);
+            let focus_range =
+                value.name().and_then(|it| orig_focus_range(db, src.file_id, it.syntax()));
             let mut res = NavigationTarget::from_syntax(
                 file_id,
                 name,
@@ -129,15 +127,11 @@ impl NavigationTarget {
     /// Allows `NavigationTarget` to be created from a `NameOwner`
     pub(crate) fn from_named(
         db: &RootDatabase,
-        node: InFile<&dyn ast::HasName>,
+        node @ InFile { file_id, value }: InFile<&dyn ast::HasName>,
         kind: SymbolKind,
     ) -> NavigationTarget {
-        let name = node.value.name().map(|it| it.text().into()).unwrap_or_else(|| "_".into());
-        let focus_range = node
-            .value
-            .name()
-            .and_then(|it| node.with_value(it.syntax()).original_file_range_opt(db))
-            .map(|it| it.range);
+        let name = value.name().map(|it| it.text().into()).unwrap_or_else(|| "_".into());
+        let focus_range = value.name().and_then(|it| orig_focus_range(db, file_id, it.syntax()));
         let FileRange { file_id, range } = node.map(|it| it.syntax()).original_file_range(db);
 
         NavigationTarget::from_syntax(file_id, name, focus_range, range, kind)
@@ -279,9 +273,7 @@ impl ToNav for hir::Module {
             ModuleSource::SourceFile(node) => (node.syntax(), None),
             ModuleSource::Module(node) => (
                 node.syntax(),
-                node.name()
-                    .and_then(|it| InFile::new(file_id, it.syntax()).original_file_range_opt(db))
-                    .map(|it| it.range),
+                node.name().and_then(|it| orig_focus_range(db, file_id, it.syntax())),
             ),
             ModuleSource::BlockExpr(node) => (node.syntax(), None),
         };
@@ -299,10 +291,7 @@ impl TryToNav for hir::Impl {
         let focus_range = if derive_attr.is_some() {
             None
         } else {
-            value
-                .self_ty()
-                .and_then(|ty| InFile::new(file_id, ty.syntax()).original_file_range_opt(db))
-                .map(|it| it.range)
+            value.self_ty().and_then(|ty| orig_focus_range(db, file_id, ty.syntax()))
         };
 
         let FileRange { file_id, range: full_range } = match &derive_attr {
@@ -397,9 +386,7 @@ impl ToNav for hir::Local {
             Either::Left(bind_pat) => (bind_pat.syntax(), bind_pat.name()),
             Either::Right(it) => (it.syntax(), it.name()),
         };
-        let focus_range = name
-            .and_then(|it| InFile::new(file_id, it.syntax()).original_file_range_opt(db))
-            .map(|it| it.range);
+        let focus_range = name.and_then(|it| orig_focus_range(db, file_id, it.syntax()));
         let FileRange { file_id, range: full_range } =
             InFile::new(file_id, node).original_file_range(db);
 
@@ -505,10 +492,7 @@ impl TryToNav for hir::ConstParam {
         let InFile { file_id, value } = self.source(db)?;
         let name = self.name(db).to_smol_str();
 
-        let focus_range = value
-            .name()
-            .and_then(|it| InFile::new(file_id, it.syntax()).original_file_range_opt(db))
-            .map(|it| it.range);
+        let focus_range = value.name().and_then(|it| orig_focus_range(db, file_id, it.syntax()));
         let FileRange { file_id, range: full_range } =
             InFile::new(file_id, value.syntax()).original_file_range(db);
         Some(NavigationTarget {
@@ -549,6 +533,14 @@ pub(crate) fn description_from_symbol(db: &RootDatabase, symbol: &FileSymbol) ->
     }
 }
 
+fn orig_focus_range(
+    db: &RootDatabase,
+    file_id: hir::HirFileId,
+    syntax: &SyntaxNode,
+) -> Option<TextRange> {
+    InFile::new(file_id, syntax).original_file_range_opt(db).map(|it| it.range)
+}
+
 #[cfg(test)]
 mod tests {
     use expect_test::expect;
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index bfa3f552af4..5e6f0ef6a57 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -1484,12 +1484,12 @@ fn f() {
 }
             "#,
             expect![[r#"
-                func Function FileId(0) 140..144 140..144
+                func Function FileId(0) 137..146 140..144
 
                 FileId(0) 161..165
 
 
-                func Function FileId(0) 140..144 140..144
+                func Function FileId(0) 137..146 140..144
 
                 FileId(0) 181..185
             "#]],
diff --git a/crates/syntax/src/lib.rs b/crates/syntax/src/lib.rs
index d6b1cce45ff..0f7855a053e 100644
--- a/crates/syntax/src/lib.rs
+++ b/crates/syntax/src/lib.rs
@@ -187,10 +187,10 @@ macro_rules! match_ast {
     (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) };
 
     (match ($node:expr) {
-        $( ast::$ast:ident($it:pat) => $res:expr, )*
+        $( $( $path:ident )::+ ($it:pat) => $res:expr, )*
         _ => $catch_all:expr $(,)?
     }) => {{
-        $( if let Some($it) = ast::$ast::cast($node.clone()) { $res } else )*
+        $( if let Some($it) = $($path::)+cast($node.clone()) { $res } else )*
         { $catch_all }
     }};
 }