Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 17 additions & 10 deletions crates/hir/src/semantics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down Expand Up @@ -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)
}

Expand Down Expand Up @@ -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 {
Expand All @@ -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)
}
Expand Down
17 changes: 12 additions & 5 deletions crates/hir/src/semantics/source_to_def.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
});
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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(),
Expand All @@ -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(),
Expand Down
33 changes: 32 additions & 1 deletion crates/hir_expand/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(_))
}
Expand Down Expand Up @@ -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 => {
Expand All @@ -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> {
Expand Down
4 changes: 4 additions & 0 deletions crates/ide/src/expand_macro.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -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() {
Expand Down
16 changes: 16 additions & 0 deletions crates/ide/src/highlight_related.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
22 changes: 22 additions & 0 deletions crates/ide/src/hover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1661,6 +1661,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(
Expand Down
19 changes: 19 additions & 0 deletions crates/ide/src/references.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
"#]],
)
}
}
22 changes: 22 additions & 0 deletions crates/ide/src/rename.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
"#,
)
}
}
82 changes: 82 additions & 0 deletions crates/ide/src/runnables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
]
"#]],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a meta note, I feel that we over-use expect-debug tests. Here they are OK maybe, but, eg, in hover actions they are way to verbose.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep agree, for hover I'll tackle that problem(that is fix up tests in general) once I start refactoring it into multiple modules.

);
}

#[test]
fn find_no_tests() {
check_tests(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,15 @@
<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>

Expand Down
Loading