Skip to content

Commit f88293f

Browse files
committed
Auto merge of rust-lang#13324 - Veykril:trait-impl-completion, r=Veykril
Fix trait impl item completions using macro file text ranges Fixes rust-lang/rust-analyzer#13323
2 parents 5b7e400 + bfd5f00 commit f88293f

File tree

3 files changed

+68
-23
lines changed

3 files changed

+68
-23
lines changed

crates/hir-expand/src/lib.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -811,6 +811,31 @@ impl<'a> InFile<&'a SyntaxNode> {
811811
_ => None,
812812
}
813813
}
814+
815+
pub fn original_syntax_node(self, db: &dyn db::AstDatabase) -> Option<InFile<SyntaxNode>> {
816+
// This kind of upmapping can only be achieved in attribute expanded files,
817+
// as we don't have node inputs otherwise and therefor can't find an `N` node in the input
818+
if !self.file_id.is_macro() {
819+
return Some(self.map(Clone::clone));
820+
} else if !self.file_id.is_attr_macro(db) {
821+
return None;
822+
}
823+
824+
if let Some(InFile { file_id, value: (first, last) }) = ascend_node_border_tokens(db, self)
825+
{
826+
if file_id.is_macro() {
827+
let range = first.text_range().cover(last.text_range());
828+
tracing::error!("Failed mapping out of macro file for {:?}", range);
829+
return None;
830+
}
831+
// FIXME: This heuristic is brittle and with the right macro may select completely unrelated nodes
832+
let anc = algo::least_common_ancestor(&first.parent()?, &last.parent()?)?;
833+
let kind = self.value.kind();
834+
let value = anc.ancestors().find(|it| it.kind() == kind)?;
835+
return Some(InFile::new(file_id, value));
836+
}
837+
None
838+
}
814839
}
815840

816841
impl InFile<SyntaxToken> {

crates/hir/src/semantics.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,11 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
257257
pub fn original_ast_node<N: AstNode>(&self, node: N) -> Option<N> {
258258
self.imp.original_ast_node(node)
259259
}
260+
/// Attempts to map the node out of macro expanded files.
261+
/// This only work for attribute expansions, as other ones do not have nodes as input.
262+
pub fn original_syntax_node(&self, node: &SyntaxNode) -> Option<SyntaxNode> {
263+
self.imp.original_syntax_node(node)
264+
}
260265

261266
pub fn diagnostics_display_range(&self, diagnostics: InFile<SyntaxNodePtr>) -> FileRange {
262267
self.imp.diagnostics_display_range(diagnostics)
@@ -956,6 +961,16 @@ impl<'db> SemanticsImpl<'db> {
956961
)
957962
}
958963

964+
fn original_syntax_node(&self, node: &SyntaxNode) -> Option<SyntaxNode> {
965+
let InFile { file_id, .. } = self.find_file(node);
966+
InFile::new(file_id, node).original_syntax_node(self.db.upcast()).map(
967+
|InFile { file_id, value }| {
968+
self.cache(find_root(&value), file_id);
969+
value
970+
},
971+
)
972+
}
973+
959974
fn diagnostics_display_range(&self, src: InFile<SyntaxNodePtr>) -> FileRange {
960975
let root = self.parse_or_expand(src.file_id).unwrap();
961976
let node = src.map(|it| it.to_node(&root));

crates/ide-completion/src/completions/item_list/trait_impl.rs

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ use ide_db::{
3838
};
3939
use syntax::{
4040
ast::{self, edit_in_place::AttrsOwnerEdit},
41-
AstNode, SyntaxElement, SyntaxKind, SyntaxNode, TextRange, T,
41+
AstNode, SyntaxElement, SyntaxKind, TextRange, T,
4242
};
4343
use text_edit::TextEdit;
4444

@@ -85,20 +85,36 @@ fn complete_trait_impl_name(
8585
name: &Option<ast::Name>,
8686
kind: ImplCompletionKind,
8787
) -> Option<()> {
88-
let token = ctx.token.clone();
8988
let item = match name {
9089
Some(name) => name.syntax().parent(),
91-
None => if token.kind() == SyntaxKind::WHITESPACE { token.prev_token()? } else { token }
92-
.parent(),
90+
None => {
91+
let token = &ctx.token;
92+
match token.kind() {
93+
SyntaxKind::WHITESPACE => token.prev_token()?,
94+
_ => token.clone(),
95+
}
96+
.parent()
97+
}
9398
}?;
94-
complete_trait_impl(
95-
acc,
96-
ctx,
97-
kind,
98-
replacement_range(ctx, &item),
99-
// item -> ASSOC_ITEM_LIST -> IMPL
100-
&ast::Impl::cast(item.parent()?.parent()?)?,
101-
);
99+
let item = ctx.sema.original_syntax_node(&item)?;
100+
// item -> ASSOC_ITEM_LIST -> IMPL
101+
let impl_def = ast::Impl::cast(item.parent()?.parent()?)?;
102+
let replacement_range = {
103+
// ctx.sema.original_ast_node(item)?;
104+
let first_child = item
105+
.children_with_tokens()
106+
.find(|child| {
107+
!matches!(
108+
child.kind(),
109+
SyntaxKind::COMMENT | SyntaxKind::WHITESPACE | SyntaxKind::ATTR
110+
)
111+
})
112+
.unwrap_or_else(|| SyntaxElement::Node(item.clone()));
113+
114+
TextRange::new(first_child.text_range().start(), ctx.source_range().end())
115+
};
116+
117+
complete_trait_impl(acc, ctx, kind, replacement_range, &impl_def);
102118
Some(())
103119
}
104120

@@ -341,17 +357,6 @@ fn function_declaration(node: &ast::Fn, needs_whitespace: bool) -> String {
341357
syntax.trim_end().to_owned()
342358
}
343359

344-
fn replacement_range(ctx: &CompletionContext<'_>, item: &SyntaxNode) -> TextRange {
345-
let first_child = item
346-
.children_with_tokens()
347-
.find(|child| {
348-
!matches!(child.kind(), SyntaxKind::COMMENT | SyntaxKind::WHITESPACE | SyntaxKind::ATTR)
349-
})
350-
.unwrap_or_else(|| SyntaxElement::Node(item.clone()));
351-
352-
TextRange::new(first_child.text_range().start(), ctx.source_range().end())
353-
}
354-
355360
#[cfg(test)]
356361
mod tests {
357362
use expect_test::{expect, Expect};

0 commit comments

Comments
 (0)