mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-04 19:12:50 +00:00
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 <lukastw97@gmail.com>
This commit is contained in:
commit
f750eebd0d
@ -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)
|
||||
}
|
||||
|
@ -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(),
|
||||
|
@ -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> {
|
||||
|
@ -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() {
|
||||
|
@ -413,6 +413,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(
|
||||
|
@ -1730,6 +1730,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(
|
||||
|
@ -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
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
"#,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1737,6 +1737,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(
|
||||
|
@ -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"><</span><span class="type_param declaration">Args</span><span class="angle">></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"><</span><span class="type_param declaration">Args</span><span class="angle">></span><span class="colon">:</span> <span class="trait public">FnOnce</span><span class="angle"><</span><span class="type_param">Args</span><span class="angle">></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"><</span><span class="type_param declaration">Args</span><span class="angle">></span><span class="colon">:</span> <span class="trait public">FnMut</span><span class="angle"><</span><span class="type_param">Args</span><span class="angle">></span> <span class="brace">{</span><span class="brace">}</span>
|
||||
<span class="brace">}</span>
|
||||
|
||||
|
@ -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> {}
|
||||
|
@ -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),
|
||||
};
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user