Unnamed repository; edit this file 'description' to name the repository.
Merge #10232
10232: internal: Add more tests for ide functionality in attributed items r=Veykril a=Veykril cc https://github.com/rust-analyzer/rust-analyzer/issues/9868 Co-authored-by: Lukas Wirth <[email protected]>
bors[bot] 2021-09-14
parent e896def · parent 0f4463e · commit f750eeb
-rw-r--r--crates/hir/src/semantics.rs27
-rw-r--r--crates/hir/src/semantics/source_to_def.rs17
-rw-r--r--crates/hir_expand/src/lib.rs33
-rw-r--r--crates/ide/src/expand_macro.rs4
-rw-r--r--crates/ide/src/highlight_related.rs16
-rw-r--r--crates/ide/src/hover.rs22
-rw-r--r--crates/ide/src/references.rs19
-rw-r--r--crates/ide/src/rename.rs22
-rw-r--r--crates/ide/src/runnables.rs82
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlighting.html7
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs2
-rw-r--r--crates/ide_db/src/search.rs50
12 files changed, 250 insertions, 51 deletions
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index ed27f6122b..1aa0d61fe6 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -16,7 +16,6 @@ use itertools::Itertools;
use rustc_hash::{FxHashMap, FxHashSet};
use smallvec::{smallvec, SmallVec};
use syntax::{
- algo::find_node_at_offset,
ast::{self, GenericParamsOwner, LoopBodyOwner},
match_ast, AstNode, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, TextSize,
};
@@ -241,10 +240,6 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
node: &SyntaxNode,
offset: TextSize,
) -> Option<N> {
- if let Some(it) = find_node_at_offset(node, offset) {
- return Some(it);
- }
-
self.imp.descend_node_at_offset(node, offset).flatten().find_map(N::cast)
}
@@ -567,16 +562,25 @@ impl<'db> SemanticsImpl<'db> {
// Note this return type is deliberate as [`find_nodes_at_offset_with_descend`] wants to stop
// traversing the inner iterator when it finds a node.
+ // The outer iterator is over the tokens descendants
+ // The inner iterator is the ancestors of a descendant
fn descend_node_at_offset(
&self,
node: &SyntaxNode,
offset: TextSize,
) -> impl Iterator<Item = impl Iterator<Item = SyntaxNode> + '_> + '_ {
- // Handle macro token cases
node.token_at_offset(offset)
.map(move |token| self.descend_into_macros(token))
- .map(|it| it.into_iter().map(move |it| self.token_ancestors_with_macros(it)))
- .flatten()
+ .map(|descendants| {
+ descendants.into_iter().map(move |it| self.token_ancestors_with_macros(it))
+ })
+ // re-order the tokens from token_at_offset by returning the ancestors with the smaller first nodes first
+ // See algo::ancestors_at_offset, which uses the same approach
+ .kmerge_by(|left, right| {
+ left.clone()
+ .map(|node| node.text_range().len())
+ .lt(right.clone().map(|node| node.text_range().len()))
+ })
}
fn original_range(&self, node: &SyntaxNode) -> FileRange {
@@ -594,11 +598,14 @@ impl<'db> SemanticsImpl<'db> {
fn token_ancestors_with_macros(
&self,
token: SyntaxToken,
- ) -> impl Iterator<Item = SyntaxNode> + '_ {
+ ) -> impl Iterator<Item = SyntaxNode> + Clone + '_ {
token.parent().into_iter().flat_map(move |parent| self.ancestors_with_macros(parent))
}
- fn ancestors_with_macros(&self, node: SyntaxNode) -> impl Iterator<Item = SyntaxNode> + '_ {
+ fn ancestors_with_macros(
+ &self,
+ node: SyntaxNode,
+ ) -> impl Iterator<Item = SyntaxNode> + Clone + '_ {
let node = self.find_file(node);
node.ancestors_with_macros(self.db.upcast()).map(|it| it.value)
}
diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs
index 1f80fd3404..723c7a1727 100644
--- a/crates/hir/src/semantics/source_to_def.rs
+++ b/crates/hir/src/semantics/source_to_def.rs
@@ -131,8 +131,12 @@ impl SourceToDefCtx<'_, '_> {
pub(super) fn module_to_def(&mut self, src: InFile<ast::Module>) -> Option<ModuleId> {
let _p = profile::span("module_to_def");
- let parent_declaration =
- src.syntax().cloned().ancestors_with_macros(self.db.upcast()).skip(1).find_map(|it| {
+ let parent_declaration = src
+ .syntax()
+ .cloned()
+ .ancestors_with_macros_skip_attr_item(self.db.upcast())
+ .skip(1)
+ .find_map(|it| {
let m = ast::Module::cast(it.value.clone())?;
Some(it.with_value(m))
});
@@ -306,7 +310,8 @@ impl SourceToDefCtx<'_, '_> {
}
pub(super) fn find_container(&mut self, src: InFile<&SyntaxNode>) -> Option<ChildContainer> {
- for container in src.cloned().ancestors_with_macros(self.db.upcast()).skip(1) {
+ for container in src.cloned().ancestors_with_macros_skip_attr_item(self.db.upcast()).skip(1)
+ {
if let Some(res) = self.container_to_def(container) {
return Some(res);
}
@@ -370,7 +375,8 @@ impl SourceToDefCtx<'_, '_> {
}
fn find_generic_param_container(&mut self, src: InFile<&SyntaxNode>) -> Option<GenericDefId> {
- for container in src.cloned().ancestors_with_macros(self.db.upcast()).skip(1) {
+ for container in src.cloned().ancestors_with_macros_skip_attr_item(self.db.upcast()).skip(1)
+ {
let res: GenericDefId = match_ast! {
match (container.value) {
ast::Fn(it) => self.fn_to_def(container.with_value(it))?.into(),
@@ -388,7 +394,8 @@ impl SourceToDefCtx<'_, '_> {
}
fn find_pat_or_label_container(&mut self, src: InFile<&SyntaxNode>) -> Option<DefWithBodyId> {
- for container in src.cloned().ancestors_with_macros(self.db.upcast()).skip(1) {
+ for container in src.cloned().ancestors_with_macros_skip_attr_item(self.db.upcast()).skip(1)
+ {
let res: DefWithBodyId = match_ast! {
match (container.value) {
ast::Const(it) => self.const_to_def(container.with_value(it))?.into(),
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs
index 871cccb830..8bb56e0700 100644
--- a/crates/hir_expand/src/lib.rs
+++ b/crates/hir_expand/src/lib.rs
@@ -186,6 +186,17 @@ impl HirFileId {
}
}
+ /// Return whether this file is an include macro
+ pub fn is_attr_macro(&self, db: &dyn db::AstDatabase) -> bool {
+ match self.0 {
+ HirFileIdRepr::MacroFile(macro_file) => {
+ let loc: MacroCallLoc = db.lookup_intern_macro(macro_file.macro_call_id);
+ matches!(loc.kind, MacroCallKind::Attr { .. })
+ }
+ _ => false,
+ }
+ }
+
pub fn is_macro(self) -> bool {
matches!(self.0, HirFileIdRepr::MacroFile(_))
}
@@ -525,7 +536,7 @@ impl InFile<SyntaxNode> {
pub fn ancestors_with_macros(
self,
db: &dyn db::AstDatabase,
- ) -> impl Iterator<Item = InFile<SyntaxNode>> + '_ {
+ ) -> impl Iterator<Item = InFile<SyntaxNode>> + Clone + '_ {
iter::successors(Some(self), move |node| match node.value.parent() {
Some(parent) => Some(node.with_value(parent)),
None => {
@@ -534,6 +545,26 @@ impl InFile<SyntaxNode> {
}
})
}
+
+ /// Skips the attributed item that caused the macro invocation we are climbing up
+ 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() {
+ Some(parent) => Some(node.with_value(parent)),
+ None => {
+ let parent_node = node.file_id.call_node(db)?;
+ if node.file_id.is_attr_macro(db) {
+ // macro call was an attributed item, skip it
+ // FIXME: does this fail if this is a direct expansion of another macro?
+ parent_node.map(|node| node.parent()).transpose()
+ } else {
+ Some(parent_node)
+ }
+ }
+ })
+ }
}
impl<'a> InFile<&'a SyntaxNode> {
diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs
index 8ec5e10c4d..079f847030 100644
--- a/crates/ide/src/expand_macro.rs
+++ b/crates/ide/src/expand_macro.rs
@@ -31,6 +31,7 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<
SyntaxKind::IDENT => 1,
_ => 0,
})?;
+
let descended = sema.descend_into_macros(tok.clone());
if let Some(attr) = descended.ancestors().find_map(ast::Attr::cast) {
if let Some((path, tt)) = attr.as_simple_call() {
@@ -45,6 +46,9 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<
}
}
}
+
+ // FIXME: Intermix attribute and bang! expansions
+ // currently we only recursively expand one of the two types
let mut expanded = None;
let mut name = None;
for node in tok.ancestors() {
diff --git a/crates/ide/src/highlight_related.rs b/crates/ide/src/highlight_related.rs
index 6994e41c8a..7cce99c3b0 100644
--- a/crates/ide/src/highlight_related.rs
+++ b/crates/ide/src/highlight_related.rs
@@ -414,6 +414,22 @@ fn foo() {
}
#[test]
+ fn test_hl_local_in_attr() {
+ check(
+ r#"
+//- proc_macros: identity
+#[proc_macros::identity]
+fn foo() {
+ let mut bar = 3;
+ // ^^^ write
+ bar$0;
+ // ^^^ read
+}
+"#,
+ );
+ }
+
+ #[test]
fn test_multi_macro_usage() {
check(
r#"
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 14c0abaaf0..7058f051b8 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -1731,6 +1731,28 @@ id! {
}
#[test]
+ fn test_hover_through_attr() {
+ check(
+ r#"
+//- proc_macros: identity
+#[proc_macros::identity]
+fn foo$0() {}
+"#,
+ expect![[r#"
+ *foo*
+
+ ```rust
+ test
+ ```
+
+ ```rust
+ fn foo()
+ ```
+ "#]],
+ );
+ }
+
+ #[test]
fn test_hover_through_expr_in_macro() {
check(
r#"
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index 8c3ad0fced..c39d00b6e7 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -1507,4 +1507,23 @@ fn f() {
"#]],
)
}
+
+ #[test]
+ fn attr_expanded() {
+ check(
+ r#"
+//- proc_macros: identity
+
+#[proc_macros::identity]
+fn func$0() {
+ func();
+}
+"#,
+ expect![[r#"
+ func Function FileId(0) 26..51 29..33
+
+ FileId(0) 42..46
+ "#]],
+ )
+ }
}
diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs
index a4297a2fec..a495e6c543 100644
--- a/crates/ide/src/rename.rs
+++ b/crates/ide/src/rename.rs
@@ -1880,4 +1880,26 @@ fn main() { f$0() }
"error: No identifier available to rename",
)
}
+
+ #[test]
+ fn attributed_item() {
+ check(
+ "function",
+ r#"
+//- proc_macros: identity
+
+#[proc_macros::identity]
+fn func$0() {
+ func();
+}
+"#,
+ r#"
+
+#[proc_macros::identity]
+fn function() {
+ function();
+}
+"#,
+ )
+ }
}
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs
index 6b74ec3430..376384670a 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -1738,6 +1738,88 @@ fn t1() {}
}
#[test]
+ fn attributed_module() {
+ check(
+ r#"
+//- proc_macros: identity
+//- /lib.rs
+$0
+#[proc_macros::identity]
+mod module {
+ #[test]
+ fn t0() {}
+ #[test]
+ fn t1() {}
+}
+"#,
+ &[TestMod, Test, Test],
+ expect![[r#"
+ [
+ Runnable {
+ use_name_in_title: true,
+ nav: NavigationTarget {
+ file_id: FileId(
+ 0,
+ ),
+ full_range: 26..94,
+ focus_range: 30..36,
+ name: "module",
+ kind: Module,
+ description: "mod module",
+ },
+ kind: TestMod {
+ path: "module",
+ },
+ cfg: None,
+ },
+ Runnable {
+ use_name_in_title: true,
+ nav: NavigationTarget {
+ file_id: FileId(
+ 0,
+ ),
+ full_range: 43..65,
+ focus_range: 58..60,
+ name: "t0",
+ kind: Function,
+ },
+ kind: Test {
+ test_id: Path(
+ "module::t0",
+ ),
+ attr: TestAttr {
+ ignore: false,
+ },
+ },
+ cfg: None,
+ },
+ Runnable {
+ use_name_in_title: true,
+ nav: NavigationTarget {
+ file_id: FileId(
+ 0,
+ ),
+ full_range: 70..92,
+ focus_range: 85..87,
+ name: "t1",
+ kind: Function,
+ },
+ kind: Test {
+ test_id: Path(
+ "module::t1",
+ ),
+ attr: TestAttr {
+ ignore: false,
+ },
+ },
+ cfg: None,
+ },
+ ]
+ "#]],
+ );
+ }
+
+ #[test]
fn find_no_tests() {
check_tests(
r#"
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
index e43119aa3b..448b30aa84 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
@@ -51,14 +51,15 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration public">Copy</span> <span class="brace">{</span><span class="brace">}</span>
<span class="brace">}</span>
+<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="module attribute">proc_macros</span><span class="operator attribute">::</span><span class="builtin_attr attribute">identity</span><span class="attribute attribute">]</span>
<span class="keyword">pub</span> <span class="keyword">mod</span> <span class="module declaration public">ops</span> <span class="brace">{</span>
- <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute">lang</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"fn_once"</span><span class="attribute attribute">]</span>
+ <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute">lang</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"fn_once"</span><span class="attribute attribute">]</span>
<span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration public">FnOnce</span><span class="angle">&lt;</span><span class="type_param declaration">Args</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="brace">}</span>
- <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute">lang</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"fn_mut"</span><span class="attribute attribute">]</span>
+ <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute">lang</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"fn_mut"</span><span class="attribute attribute">]</span>
<span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration public">FnMut</span><span class="angle">&lt;</span><span class="type_param declaration">Args</span><span class="angle">&gt;</span><span class="colon">:</span> <span class="trait public">FnOnce</span><span class="angle">&lt;</span><span class="type_param">Args</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="brace">}</span>
- <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute">lang</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"fn"</span><span class="attribute attribute">]</span>
+ <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute">lang</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"fn"</span><span class="attribute attribute">]</span>
<span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration public">Fn</span><span class="angle">&lt;</span><span class="type_param declaration">Args</span><span class="angle">&gt;</span><span class="colon">:</span> <span class="trait public">FnMut</span><span class="angle">&lt;</span><span class="type_param">Args</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="brace">}</span>
<span class="brace">}</span>
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index 740127202f..6b08e916a9 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -10,6 +10,7 @@ use crate::{fixture, FileRange, HlTag, TextRange};
fn test_highlighting() {
check_highlighting(
r#"
+//- proc_macros: identity
//- /main.rs crate:main deps:foo
use inner::{self as inner_mod};
mod inner {}
@@ -23,6 +24,7 @@ pub mod marker {
pub trait Copy {}
}
+#[proc_macros::identity]
pub mod ops {
#[lang = "fn_once"]
pub trait FnOnce<Args> {}
diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs
index 8b8c91b2fc..a84e6b3ba4 100644
--- a/crates/ide_db/src/search.rs
+++ b/crates/ide_db/src/search.rs
@@ -232,50 +232,36 @@ impl Definition {
let file_id = file_id.original_file(db);
if let Definition::Local(var) = self {
- let range = match var.parent(db) {
- DefWithBody::Function(f) => f.source(db).map(|src| src.value.syntax().text_range()),
- DefWithBody::Const(c) => c.source(db).map(|src| src.value.syntax().text_range()),
- DefWithBody::Static(s) => s.source(db).map(|src| src.value.syntax().text_range()),
+ let def = match var.parent(db) {
+ DefWithBody::Function(f) => f.source(db).map(|src| src.syntax().cloned()),
+ DefWithBody::Const(c) => c.source(db).map(|src| src.syntax().cloned()),
+ DefWithBody::Static(s) => s.source(db).map(|src| src.syntax().cloned()),
};
- return match range {
- Some(range) => SearchScope::file_range(FileRange { file_id, range }),
+ return match def {
+ Some(def) => SearchScope::file_range(def.as_ref().original_file_range(db)),
None => SearchScope::single_file(file_id),
};
}
if let Definition::SelfType(impl_) = self {
- return match impl_.source(db).map(|src| src.value.syntax().text_range()) {
- Some(range) => SearchScope::file_range(FileRange { file_id, range }),
+ return match impl_.source(db).map(|src| src.syntax().cloned()) {
+ Some(def) => SearchScope::file_range(def.as_ref().original_file_range(db)),
None => SearchScope::single_file(file_id),
};
}
if let Definition::GenericParam(hir::GenericParam::LifetimeParam(param)) = self {
- let range = match param.parent(db) {
- hir::GenericDef::Function(it) => {
- it.source(db).map(|src| src.value.syntax().text_range())
- }
- hir::GenericDef::Adt(it) => {
- it.source(db).map(|src| src.value.syntax().text_range())
- }
- hir::GenericDef::Trait(it) => {
- it.source(db).map(|src| src.value.syntax().text_range())
- }
- hir::GenericDef::TypeAlias(it) => {
- it.source(db).map(|src| src.value.syntax().text_range())
- }
- hir::GenericDef::Impl(it) => {
- it.source(db).map(|src| src.value.syntax().text_range())
- }
- hir::GenericDef::Variant(it) => {
- it.source(db).map(|src| src.value.syntax().text_range())
- }
- hir::GenericDef::Const(it) => {
- it.source(db).map(|src| src.value.syntax().text_range())
- }
+ let def = match param.parent(db) {
+ hir::GenericDef::Function(it) => it.source(db).map(|src| src.syntax().cloned()),
+ hir::GenericDef::Adt(it) => it.source(db).map(|src| src.syntax().cloned()),
+ hir::GenericDef::Trait(it) => it.source(db).map(|src| src.syntax().cloned()),
+ hir::GenericDef::TypeAlias(it) => it.source(db).map(|src| src.syntax().cloned()),
+ hir::GenericDef::Impl(it) => it.source(db).map(|src| src.syntax().cloned()),
+ hir::GenericDef::Variant(it) => it.source(db).map(|src| src.syntax().cloned()),
+ hir::GenericDef::Const(it) => it.source(db).map(|src| src.syntax().cloned()),
};
- return match range {
- Some(range) => SearchScope::file_range(FileRange { file_id, range }),
+ return match def {
+ Some(def) => SearchScope::file_range(def.as_ref().original_file_range(db)),
None => SearchScope::single_file(file_id),
};
}