mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-11-03 13:23:25 +00:00
Consider walking up macro expansions when searching for surrounding entities in completion analysis
This commit is contained in:
parent
c1446a2743
commit
f271b18129
1 changed files with 45 additions and 40 deletions
|
|
@ -456,7 +456,7 @@ impl<'a> CompletionContext<'a> {
|
||||||
ast::IdentPat(bind_pat) => {
|
ast::IdentPat(bind_pat) => {
|
||||||
let mut pat_ctx = pattern_context_for(sema, original_file, bind_pat.into());
|
let mut pat_ctx = pattern_context_for(sema, original_file, bind_pat.into());
|
||||||
if let Some(record_field) = ast::RecordPatField::for_field_name(&name) {
|
if let Some(record_field) = ast::RecordPatField::for_field_name(&name) {
|
||||||
pat_ctx.record_pat = find_node_in_file_compensated(original_file, &record_field.parent_record_pat());
|
pat_ctx.record_pat = find_node_in_file_compensated(sema, original_file, &record_field.parent_record_pat());
|
||||||
}
|
}
|
||||||
|
|
||||||
NameKind::IdentPat(pat_ctx)
|
NameKind::IdentPat(pat_ctx)
|
||||||
|
|
@ -493,9 +493,13 @@ impl<'a> CompletionContext<'a> {
|
||||||
|kind| (NameRefContext { nameref: nameref.clone(), kind }, Default::default());
|
|kind| (NameRefContext { nameref: nameref.clone(), kind }, Default::default());
|
||||||
|
|
||||||
if let Some(record_field) = ast::RecordExprField::for_field_name(&name_ref) {
|
if let Some(record_field) = ast::RecordExprField::for_field_name(&name_ref) {
|
||||||
return find_node_in_file_compensated(original_file, &record_field.parent_record_lit())
|
return find_node_in_file_compensated(
|
||||||
.map(NameRefKind::RecordExpr)
|
sema,
|
||||||
.map(make_res);
|
original_file,
|
||||||
|
&record_field.parent_record_lit(),
|
||||||
|
)
|
||||||
|
.map(NameRefKind::RecordExpr)
|
||||||
|
.map(make_res);
|
||||||
}
|
}
|
||||||
if let Some(record_field) = ast::RecordPatField::for_field_name_ref(&name_ref) {
|
if let Some(record_field) = ast::RecordPatField::for_field_name_ref(&name_ref) {
|
||||||
let kind = NameRefKind::Pattern(PatternContext {
|
let kind = NameRefKind::Pattern(PatternContext {
|
||||||
|
|
@ -504,6 +508,7 @@ impl<'a> CompletionContext<'a> {
|
||||||
ref_token: None,
|
ref_token: None,
|
||||||
mut_token: None,
|
mut_token: None,
|
||||||
record_pat: find_node_in_file_compensated(
|
record_pat: find_node_in_file_compensated(
|
||||||
|
sema,
|
||||||
original_file,
|
original_file,
|
||||||
&record_field.parent_record_pat(),
|
&record_field.parent_record_pat(),
|
||||||
),
|
),
|
||||||
|
|
@ -568,7 +573,7 @@ impl<'a> CompletionContext<'a> {
|
||||||
};
|
};
|
||||||
let func_update_record = |syn: &SyntaxNode| {
|
let func_update_record = |syn: &SyntaxNode| {
|
||||||
if let Some(record_expr) = syn.ancestors().nth(2).and_then(ast::RecordExpr::cast) {
|
if let Some(record_expr) = syn.ancestors().nth(2).and_then(ast::RecordExpr::cast) {
|
||||||
find_node_in_file_compensated(original_file, &record_expr)
|
find_node_in_file_compensated(sema, original_file, &record_expr)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
@ -670,9 +675,9 @@ impl<'a> CompletionContext<'a> {
|
||||||
ast::TypeBound(_) => TypeLocation::TypeBound,
|
ast::TypeBound(_) => TypeLocation::TypeBound,
|
||||||
// is this case needed?
|
// is this case needed?
|
||||||
ast::TypeBoundList(_) => TypeLocation::TypeBound,
|
ast::TypeBoundList(_) => TypeLocation::TypeBound,
|
||||||
ast::GenericArg(it) => TypeLocation::GenericArgList(find_opt_node_in_file_compensated(original_file, it.syntax().parent().and_then(ast::GenericArgList::cast))),
|
ast::GenericArg(it) => TypeLocation::GenericArgList(find_opt_node_in_file_compensated(sema, original_file, it.syntax().parent().and_then(ast::GenericArgList::cast))),
|
||||||
// is this case needed?
|
// is this case needed?
|
||||||
ast::GenericArgList(it) => TypeLocation::GenericArgList(find_opt_node_in_file_compensated(original_file, Some(it))),
|
ast::GenericArgList(it) => TypeLocation::GenericArgList(find_opt_node_in_file_compensated(sema, original_file, Some(it))),
|
||||||
ast::TupleField(_) => TypeLocation::TupleField,
|
ast::TupleField(_) => TypeLocation::TupleField,
|
||||||
_ => return None,
|
_ => return None,
|
||||||
}
|
}
|
||||||
|
|
@ -734,7 +739,7 @@ impl<'a> CompletionContext<'a> {
|
||||||
_ => Some(None),
|
_ => Some(None),
|
||||||
};
|
};
|
||||||
|
|
||||||
match dbg!(find_node_in_file_compensated(original_file, &expr)) {
|
match find_node_in_file_compensated(sema, original_file, &expr) {
|
||||||
Some(it) => {
|
Some(it) => {
|
||||||
let innermost_ret_ty = sema
|
let innermost_ret_ty = sema
|
||||||
.ancestors_with_macros(it.syntax().clone())
|
.ancestors_with_macros(it.syntax().clone())
|
||||||
|
|
@ -757,7 +762,7 @@ impl<'a> CompletionContext<'a> {
|
||||||
.parent()
|
.parent()
|
||||||
.and_then(ast::LetStmt::cast)
|
.and_then(ast::LetStmt::cast)
|
||||||
.map_or(false, |it| it.semicolon_token().is_none());
|
.map_or(false, |it| it.semicolon_token().is_none());
|
||||||
let impl_ = fetch_immediate_impl(sema, original_file, &expr);
|
let impl_ = fetch_immediate_impl(sema, original_file, expr.syntax());
|
||||||
|
|
||||||
PathKind::Expr {
|
PathKind::Expr {
|
||||||
in_block_expr,
|
in_block_expr,
|
||||||
|
|
@ -826,7 +831,7 @@ impl<'a> CompletionContext<'a> {
|
||||||
match it {
|
match it {
|
||||||
ast::Trait(_) => ItemListKind::Trait,
|
ast::Trait(_) => ItemListKind::Trait,
|
||||||
ast::Impl(it) => if it.trait_().is_some() {
|
ast::Impl(it) => if it.trait_().is_some() {
|
||||||
ItemListKind::TraitImpl(find_node_in_file_compensated(original_file, &it))
|
ItemListKind::TraitImpl(find_node_in_file_compensated(sema, original_file, &it))
|
||||||
} else {
|
} else {
|
||||||
ItemListKind::Impl
|
ItemListKind::Impl
|
||||||
},
|
},
|
||||||
|
|
@ -983,7 +988,7 @@ fn pattern_context_for(
|
||||||
let has_type_ascription = param.ty().is_some();
|
let has_type_ascription = param.ty().is_some();
|
||||||
is_param = (|| {
|
is_param = (|| {
|
||||||
let fake_param_list = param.syntax().parent().and_then(ast::ParamList::cast)?;
|
let fake_param_list = param.syntax().parent().and_then(ast::ParamList::cast)?;
|
||||||
let param_list = find_node_in_file_compensated(original_file, &fake_param_list)?;
|
let param_list = find_node_in_file_compensated(sema, original_file, &fake_param_list)?;
|
||||||
let param_list_owner = param_list.syntax().parent()?;
|
let param_list_owner = param_list.syntax().parent()?;
|
||||||
let kind = match_ast! {
|
let kind = match_ast! {
|
||||||
match param_list_owner {
|
match param_list_owner {
|
||||||
|
|
@ -1017,42 +1022,25 @@ fn pattern_context_for(
|
||||||
mut_token,
|
mut_token,
|
||||||
ref_token,
|
ref_token,
|
||||||
record_pat: None,
|
record_pat: None,
|
||||||
impl_: fetch_immediate_impl(sema, original_file, &pat),
|
impl_: fetch_immediate_impl(sema, original_file, pat.syntax()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch_immediate_impl(
|
fn fetch_immediate_impl(
|
||||||
sema: &Semantics<RootDatabase>,
|
sema: &Semantics<RootDatabase>,
|
||||||
original_file: &SyntaxNode,
|
original_file: &SyntaxNode,
|
||||||
node: &impl AstNode,
|
node: &SyntaxNode,
|
||||||
) -> Option<ast::Impl> {
|
) -> Option<ast::Impl> {
|
||||||
// FIXME: The fallback here could be done better
|
let mut ancestors = ancestors_in_file_compensated(sema, original_file, node)?
|
||||||
let (f, s) = match find_node_in_file_compensated(original_file, node) {
|
.filter_map(ast::Item::cast)
|
||||||
Some(node) => {
|
.filter(|it| !matches!(it, ast::Item::MacroCall(_)));
|
||||||
let mut items = sema
|
|
||||||
.ancestors_with_macros(node.syntax().clone())
|
|
||||||
.filter_map(ast::Item::cast)
|
|
||||||
.filter(|it| !matches!(it, ast::Item::MacroCall(_)))
|
|
||||||
.take(2);
|
|
||||||
(items.next(), items.next())
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
let mut items = node
|
|
||||||
.syntax()
|
|
||||||
.ancestors()
|
|
||||||
.filter_map(ast::Item::cast)
|
|
||||||
.filter(|it| !matches!(it, ast::Item::MacroCall(_)))
|
|
||||||
.take(2);
|
|
||||||
(items.next(), items.next())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
match f? {
|
match ancestors.next()? {
|
||||||
ast::Item::Const(_) | ast::Item::Fn(_) | ast::Item::TypeAlias(_) => (),
|
ast::Item::Const(_) | ast::Item::Fn(_) | ast::Item::TypeAlias(_) => (),
|
||||||
ast::Item::Impl(it) => return Some(it),
|
ast::Item::Impl(it) => return Some(it),
|
||||||
_ => return None,
|
_ => return None,
|
||||||
}
|
}
|
||||||
match s? {
|
match ancestors.next()? {
|
||||||
ast::Item::Impl(it) => Some(it),
|
ast::Item::Impl(it) => Some(it),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
|
|
@ -1076,9 +1064,21 @@ fn find_node_in_file<N: AstNode>(syntax: &SyntaxNode, node: &N) -> Option<N> {
|
||||||
/// Attempts to find `node` inside `syntax` via `node`'s text range while compensating
|
/// Attempts to find `node` inside `syntax` via `node`'s text range while compensating
|
||||||
/// for the offset introduced by the fake ident.
|
/// for the offset introduced by the fake ident.
|
||||||
/// This is wrong if `node` comes before the insertion point! Use `find_node_in_file` instead.
|
/// This is wrong if `node` comes before the insertion point! Use `find_node_in_file` instead.
|
||||||
fn find_node_in_file_compensated<N: AstNode>(syntax: &SyntaxNode, node: &N) -> Option<N> {
|
fn find_node_in_file_compensated<N: AstNode>(
|
||||||
let syntax_range = syntax.text_range();
|
sema: &Semantics<RootDatabase>,
|
||||||
let range = node.syntax().text_range();
|
in_file: &SyntaxNode,
|
||||||
|
node: &N,
|
||||||
|
) -> Option<N> {
|
||||||
|
ancestors_in_file_compensated(sema, in_file, node.syntax())?.find_map(N::cast)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ancestors_in_file_compensated<'sema>(
|
||||||
|
sema: &'sema Semantics<RootDatabase>,
|
||||||
|
in_file: &SyntaxNode,
|
||||||
|
node: &SyntaxNode,
|
||||||
|
) -> Option<impl Iterator<Item = SyntaxNode> + 'sema> {
|
||||||
|
let syntax_range = in_file.text_range();
|
||||||
|
let range = node.text_range();
|
||||||
let end = range.end().checked_sub(TextSize::try_from(COMPLETION_MARKER.len()).ok()?)?;
|
let end = range.end().checked_sub(TextSize::try_from(COMPLETION_MARKER.len()).ok()?)?;
|
||||||
if end < range.start() {
|
if end < range.start() {
|
||||||
return None;
|
return None;
|
||||||
|
|
@ -1086,17 +1086,22 @@ fn find_node_in_file_compensated<N: AstNode>(syntax: &SyntaxNode, node: &N) -> O
|
||||||
let range = TextRange::new(range.start(), end);
|
let range = TextRange::new(range.start(), end);
|
||||||
// our inserted ident could cause `range` to go outside of the original syntax, so cap it
|
// our inserted ident could cause `range` to go outside of the original syntax, so cap it
|
||||||
let intersection = range.intersect(syntax_range)?;
|
let intersection = range.intersect(syntax_range)?;
|
||||||
syntax.covering_element(intersection).ancestors().find_map(N::cast)
|
let node = match in_file.covering_element(intersection) {
|
||||||
|
NodeOrToken::Node(node) => node,
|
||||||
|
NodeOrToken::Token(tok) => tok.parent()?,
|
||||||
|
};
|
||||||
|
Some(sema.ancestors_with_macros(node))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempts to find `node` inside `syntax` via `node`'s text range while compensating
|
/// Attempts to find `node` inside `syntax` via `node`'s text range while compensating
|
||||||
/// for the offset introduced by the fake ident..
|
/// for the offset introduced by the fake ident..
|
||||||
/// This is wrong if `node` comes before the insertion point! Use `find_node_in_file` instead.
|
/// This is wrong if `node` comes before the insertion point! Use `find_node_in_file` instead.
|
||||||
fn find_opt_node_in_file_compensated<N: AstNode>(
|
fn find_opt_node_in_file_compensated<N: AstNode>(
|
||||||
|
sema: &Semantics<RootDatabase>,
|
||||||
syntax: &SyntaxNode,
|
syntax: &SyntaxNode,
|
||||||
node: Option<N>,
|
node: Option<N>,
|
||||||
) -> Option<N> {
|
) -> Option<N> {
|
||||||
find_node_in_file_compensated(syntax, &node?)
|
find_node_in_file_compensated(sema, syntax, &node?)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path_or_use_tree_qualifier(path: &ast::Path) -> Option<(ast::Path, bool)> {
|
fn path_or_use_tree_qualifier(path: &ast::Path) -> Option<(ast::Path, bool)> {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue