Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit f271b18

Browse files
committed
Consider walking up macro expansions when searching for surrounding entities in completion analysis
1 parent c1446a2 commit f271b18

File tree

1 file changed

+45
-40
lines changed

1 file changed

+45
-40
lines changed

crates/ide-completion/src/context/analysis.rs

Lines changed: 45 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -456,7 +456,7 @@ impl<'a> CompletionContext<'a> {
456456
ast::IdentPat(bind_pat) => {
457457
let mut pat_ctx = pattern_context_for(sema, original_file, bind_pat.into());
458458
if let Some(record_field) = ast::RecordPatField::for_field_name(&name) {
459-
pat_ctx.record_pat = find_node_in_file_compensated(original_file, &record_field.parent_record_pat());
459+
pat_ctx.record_pat = find_node_in_file_compensated(sema, original_file, &record_field.parent_record_pat());
460460
}
461461

462462
NameKind::IdentPat(pat_ctx)
@@ -493,9 +493,13 @@ impl<'a> CompletionContext<'a> {
493493
|kind| (NameRefContext { nameref: nameref.clone(), kind }, Default::default());
494494

495495
if let Some(record_field) = ast::RecordExprField::for_field_name(&name_ref) {
496-
return find_node_in_file_compensated(original_file, &record_field.parent_record_lit())
497-
.map(NameRefKind::RecordExpr)
498-
.map(make_res);
496+
return find_node_in_file_compensated(
497+
sema,
498+
original_file,
499+
&record_field.parent_record_lit(),
500+
)
501+
.map(NameRefKind::RecordExpr)
502+
.map(make_res);
499503
}
500504
if let Some(record_field) = ast::RecordPatField::for_field_name_ref(&name_ref) {
501505
let kind = NameRefKind::Pattern(PatternContext {
@@ -504,6 +508,7 @@ impl<'a> CompletionContext<'a> {
504508
ref_token: None,
505509
mut_token: None,
506510
record_pat: find_node_in_file_compensated(
511+
sema,
507512
original_file,
508513
&record_field.parent_record_pat(),
509514
),
@@ -568,7 +573,7 @@ impl<'a> CompletionContext<'a> {
568573
};
569574
let func_update_record = |syn: &SyntaxNode| {
570575
if let Some(record_expr) = syn.ancestors().nth(2).and_then(ast::RecordExpr::cast) {
571-
find_node_in_file_compensated(original_file, &record_expr)
576+
find_node_in_file_compensated(sema, original_file, &record_expr)
572577
} else {
573578
None
574579
}
@@ -670,9 +675,9 @@ impl<'a> CompletionContext<'a> {
670675
ast::TypeBound(_) => TypeLocation::TypeBound,
671676
// is this case needed?
672677
ast::TypeBoundList(_) => TypeLocation::TypeBound,
673-
ast::GenericArg(it) => TypeLocation::GenericArgList(find_opt_node_in_file_compensated(original_file, it.syntax().parent().and_then(ast::GenericArgList::cast))),
678+
ast::GenericArg(it) => TypeLocation::GenericArgList(find_opt_node_in_file_compensated(sema, original_file, it.syntax().parent().and_then(ast::GenericArgList::cast))),
674679
// is this case needed?
675-
ast::GenericArgList(it) => TypeLocation::GenericArgList(find_opt_node_in_file_compensated(original_file, Some(it))),
680+
ast::GenericArgList(it) => TypeLocation::GenericArgList(find_opt_node_in_file_compensated(sema, original_file, Some(it))),
676681
ast::TupleField(_) => TypeLocation::TupleField,
677682
_ => return None,
678683
}
@@ -734,7 +739,7 @@ impl<'a> CompletionContext<'a> {
734739
_ => Some(None),
735740
};
736741

737-
match dbg!(find_node_in_file_compensated(original_file, &expr)) {
742+
match find_node_in_file_compensated(sema, original_file, &expr) {
738743
Some(it) => {
739744
let innermost_ret_ty = sema
740745
.ancestors_with_macros(it.syntax().clone())
@@ -757,7 +762,7 @@ impl<'a> CompletionContext<'a> {
757762
.parent()
758763
.and_then(ast::LetStmt::cast)
759764
.map_or(false, |it| it.semicolon_token().is_none());
760-
let impl_ = fetch_immediate_impl(sema, original_file, &expr);
765+
let impl_ = fetch_immediate_impl(sema, original_file, expr.syntax());
761766

762767
PathKind::Expr {
763768
in_block_expr,
@@ -826,7 +831,7 @@ impl<'a> CompletionContext<'a> {
826831
match it {
827832
ast::Trait(_) => ItemListKind::Trait,
828833
ast::Impl(it) => if it.trait_().is_some() {
829-
ItemListKind::TraitImpl(find_node_in_file_compensated(original_file, &it))
834+
ItemListKind::TraitImpl(find_node_in_file_compensated(sema, original_file, &it))
830835
} else {
831836
ItemListKind::Impl
832837
},
@@ -983,7 +988,7 @@ fn pattern_context_for(
983988
let has_type_ascription = param.ty().is_some();
984989
is_param = (|| {
985990
let fake_param_list = param.syntax().parent().and_then(ast::ParamList::cast)?;
986-
let param_list = find_node_in_file_compensated(original_file, &fake_param_list)?;
991+
let param_list = find_node_in_file_compensated(sema, original_file, &fake_param_list)?;
987992
let param_list_owner = param_list.syntax().parent()?;
988993
let kind = match_ast! {
989994
match param_list_owner {
@@ -1017,42 +1022,25 @@ fn pattern_context_for(
10171022
mut_token,
10181023
ref_token,
10191024
record_pat: None,
1020-
impl_: fetch_immediate_impl(sema, original_file, &pat),
1025+
impl_: fetch_immediate_impl(sema, original_file, pat.syntax()),
10211026
}
10221027
}
10231028

10241029
fn fetch_immediate_impl(
10251030
sema: &Semantics<RootDatabase>,
10261031
original_file: &SyntaxNode,
1027-
node: &impl AstNode,
1032+
node: &SyntaxNode,
10281033
) -> Option<ast::Impl> {
1029-
// FIXME: The fallback here could be done better
1030-
let (f, s) = match find_node_in_file_compensated(original_file, node) {
1031-
Some(node) => {
1032-
let mut items = sema
1033-
.ancestors_with_macros(node.syntax().clone())
1034-
.filter_map(ast::Item::cast)
1035-
.filter(|it| !matches!(it, ast::Item::MacroCall(_)))
1036-
.take(2);
1037-
(items.next(), items.next())
1038-
}
1039-
None => {
1040-
let mut items = node
1041-
.syntax()
1042-
.ancestors()
1043-
.filter_map(ast::Item::cast)
1044-
.filter(|it| !matches!(it, ast::Item::MacroCall(_)))
1045-
.take(2);
1046-
(items.next(), items.next())
1047-
}
1048-
};
1034+
let mut ancestors = ancestors_in_file_compensated(sema, original_file, node)?
1035+
.filter_map(ast::Item::cast)
1036+
.filter(|it| !matches!(it, ast::Item::MacroCall(_)));
10491037

1050-
match f? {
1038+
match ancestors.next()? {
10511039
ast::Item::Const(_) | ast::Item::Fn(_) | ast::Item::TypeAlias(_) => (),
10521040
ast::Item::Impl(it) => return Some(it),
10531041
_ => return None,
10541042
}
1055-
match s? {
1043+
match ancestors.next()? {
10561044
ast::Item::Impl(it) => Some(it),
10571045
_ => None,
10581046
}
@@ -1076,27 +1064,44 @@ fn find_node_in_file<N: AstNode>(syntax: &SyntaxNode, node: &N) -> Option<N> {
10761064
/// Attempts to find `node` inside `syntax` via `node`'s text range while compensating
10771065
/// for the offset introduced by the fake ident.
10781066
/// This is wrong if `node` comes before the insertion point! Use `find_node_in_file` instead.
1079-
fn find_node_in_file_compensated<N: AstNode>(syntax: &SyntaxNode, node: &N) -> Option<N> {
1080-
let syntax_range = syntax.text_range();
1081-
let range = node.syntax().text_range();
1067+
fn find_node_in_file_compensated<N: AstNode>(
1068+
sema: &Semantics<RootDatabase>,
1069+
in_file: &SyntaxNode,
1070+
node: &N,
1071+
) -> Option<N> {
1072+
ancestors_in_file_compensated(sema, in_file, node.syntax())?.find_map(N::cast)
1073+
}
1074+
1075+
fn ancestors_in_file_compensated<'sema>(
1076+
sema: &'sema Semantics<RootDatabase>,
1077+
in_file: &SyntaxNode,
1078+
node: &SyntaxNode,
1079+
) -> Option<impl Iterator<Item = SyntaxNode> + 'sema> {
1080+
let syntax_range = in_file.text_range();
1081+
let range = node.text_range();
10821082
let end = range.end().checked_sub(TextSize::try_from(COMPLETION_MARKER.len()).ok()?)?;
10831083
if end < range.start() {
10841084
return None;
10851085
}
10861086
let range = TextRange::new(range.start(), end);
10871087
// our inserted ident could cause `range` to go outside of the original syntax, so cap it
10881088
let intersection = range.intersect(syntax_range)?;
1089-
syntax.covering_element(intersection).ancestors().find_map(N::cast)
1089+
let node = match in_file.covering_element(intersection) {
1090+
NodeOrToken::Node(node) => node,
1091+
NodeOrToken::Token(tok) => tok.parent()?,
1092+
};
1093+
Some(sema.ancestors_with_macros(node))
10901094
}
10911095

10921096
/// Attempts to find `node` inside `syntax` via `node`'s text range while compensating
10931097
/// for the offset introduced by the fake ident..
10941098
/// This is wrong if `node` comes before the insertion point! Use `find_node_in_file` instead.
10951099
fn find_opt_node_in_file_compensated<N: AstNode>(
1100+
sema: &Semantics<RootDatabase>,
10961101
syntax: &SyntaxNode,
10971102
node: Option<N>,
10981103
) -> Option<N> {
1099-
find_node_in_file_compensated(syntax, &node?)
1104+
find_node_in_file_compensated(sema, syntax, &node?)
11001105
}
11011106

11021107
fn path_or_use_tree_qualifier(path: &ast::Path) -> Option<(ast::Path, bool)> {

0 commit comments

Comments
 (0)