Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide-completion/src/context/analysis.rs')
| -rw-r--r-- | crates/ide-completion/src/context/analysis.rs | 177 |
1 files changed, 129 insertions, 48 deletions
diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index fc6ee70a0b..9ceb60f90f 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/crates/ide-completion/src/context/analysis.rs @@ -330,11 +330,6 @@ impl<'a> CompletionContext<'a> { self.previous_token = syntax_element.clone().into_token().and_then(previous_non_trivia_token); - self.incomplete_let = - syntax_element.ancestors().take(6).find_map(ast::LetStmt::cast).map_or(false, |it| { - it.syntax().text_range().end() == syntax_element.text_range().end() - }); - (self.expected_type, self.expected_name) = self.expected_type_and_name(); // Overwrite the path kind for derives @@ -394,28 +389,6 @@ impl<'a> CompletionContext<'a> { return Some(()); } }; - self.impl_def = self - .sema - .token_ancestors_with_macros(self.token.clone()) - .take_while(|it| it.kind() != SyntaxKind::SOURCE_FILE) - .filter_map(ast::Item::cast) - .take(2) - .find_map(|it| match it { - ast::Item::Impl(impl_) => Some(impl_), - _ => None, - }); - self.function_def = self - .sema - .token_ancestors_with_macros(self.token.clone()) - .take_while(|it| { - it.kind() != SyntaxKind::SOURCE_FILE && it.kind() != SyntaxKind::MODULE - }) - .filter_map(ast::Item::cast) - .take(2) - .find_map(|it| match it { - ast::Item::Fn(fn_) => Some(fn_), - _ => None, - }); match name_like { ast::NameLike::Lifetime(lifetime) => { @@ -469,7 +442,7 @@ impl<'a> CompletionContext<'a> { } fn classify_name( - _sema: &Semantics<RootDatabase>, + sema: &Semantics<RootDatabase>, original_file: &SyntaxNode, name: ast::Name, ) -> Option<NameContext> { @@ -481,9 +454,9 @@ impl<'a> CompletionContext<'a> { ast::Enum(_) => NameKind::Enum, ast::Fn(_) => NameKind::Function, ast::IdentPat(bind_pat) => { - let mut pat_ctx = pattern_context_for(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) { - 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) @@ -520,9 +493,13 @@ impl<'a> CompletionContext<'a> { |kind| (NameRefContext { nameref: nameref.clone(), kind }, Default::default()); 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()) - .map(NameRefKind::RecordExpr) - .map(make_res); + return find_node_in_file_compensated( + sema, + 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) { let kind = NameRefKind::Pattern(PatternContext { @@ -531,10 +508,12 @@ impl<'a> CompletionContext<'a> { ref_token: None, mut_token: None, record_pat: find_node_in_file_compensated( + sema, original_file, &record_field.parent_record_pat(), ), ..pattern_context_for( + sema, original_file, record_field.parent_record_pat().clone().into(), ) @@ -594,7 +573,7 @@ impl<'a> CompletionContext<'a> { }; let func_update_record = |syn: &SyntaxNode| { 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 { None } @@ -696,9 +675,9 @@ impl<'a> CompletionContext<'a> { ast::TypeBound(_) => TypeLocation::TypeBound, // is this case needed? 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? - 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, _ => return None, } @@ -727,8 +706,63 @@ impl<'a> CompletionContext<'a> { let after_if_expr = after_if_expr(it.clone()); let ref_expr_parent = path.as_single_name_ref().and_then(|_| it.parent()).and_then(ast::RefExpr::cast); + let (innermost_ret_ty, self_param) = { + let find_ret_ty = |it: SyntaxNode| { + if let Some(item) = ast::Item::cast(it.clone()) { + match item { + ast::Item::Fn(f) => { + Some(sema.to_def(&f).map(|it| it.ret_type(sema.db))) + } + ast::Item::MacroCall(_) => None, + _ => Some(None), + } + } else { + let expr = ast::Expr::cast(it)?; + let callable = match expr { + // FIXME + // ast::Expr::BlockExpr(b) if b.async_token().is_some() || b.try_token().is_some() => sema.type_of_expr(b), + ast::Expr::ClosureExpr(_) => sema.type_of_expr(&expr), + _ => return None, + }; + Some( + callable + .and_then(|c| c.adjusted().as_callable(sema.db)) + .map(|it| it.return_type()), + ) + } + }; + let find_fn_self_param = |it| match it { + ast::Item::Fn(fn_) => { + Some(sema.to_def(&fn_).and_then(|it| it.self_param(sema.db))) + } + ast::Item::MacroCall(_) => None, + _ => Some(None), + }; + + match find_node_in_file_compensated(sema, original_file, &expr) { + Some(it) => { + let innermost_ret_ty = sema + .ancestors_with_macros(it.syntax().clone()) + .find_map(find_ret_ty) + .flatten(); + + let self_param = sema + .ancestors_with_macros(it.syntax().clone()) + .filter_map(ast::Item::cast) + .find_map(find_fn_self_param) + .flatten(); + (innermost_ret_ty, self_param) + } + None => (None, None), + } + }; let is_func_update = func_update_record(it); let in_condition = is_in_condition(&expr); + let incomplete_let = it + .parent() + .and_then(ast::LetStmt::cast) + .map_or(false, |it| it.semicolon_token().is_none()); + let impl_ = fetch_immediate_impl(sema, original_file, expr.syntax()); PathKind::Expr { in_block_expr, @@ -737,6 +771,10 @@ impl<'a> CompletionContext<'a> { in_condition, ref_expr_parent, is_func_update, + innermost_ret_ty, + self_param, + incomplete_let, + impl_, } }; let make_path_kind_type = |ty: ast::Type| { @@ -764,14 +802,14 @@ impl<'a> CompletionContext<'a> { }, ast::TupleStructPat(it) => { path_ctx.has_call_parens = true; - PathKind::Pat { pat_ctx: pattern_context_for(original_file, it.into())} + PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())} }, ast::RecordPat(it) => { path_ctx.has_call_parens = true; - PathKind::Pat { pat_ctx: pattern_context_for(original_file, it.into())} + PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())} }, ast::PathPat(it) => { - PathKind::Pat { pat_ctx: pattern_context_for(original_file, it.into())} + PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())} }, ast::MacroCall(it) => { // A macro call in this position is usually a result of parsing recovery, so check that @@ -785,7 +823,7 @@ impl<'a> CompletionContext<'a> { match_ast! { match parent { ast::MacroExpr(expr) => make_path_kind_expr(expr.into()), - ast::MacroPat(it) => PathKind::Pat { pat_ctx: pattern_context_for(original_file, it.into())}, + ast::MacroPat(it) => PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())}, ast::MacroType(ty) => make_path_kind_type(ty.into()), ast::ItemList(_) => PathKind::Item { kind: ItemListKind::Module }, ast::AssocItemList(_) => PathKind::Item { kind: match parent.parent() { @@ -793,7 +831,7 @@ impl<'a> CompletionContext<'a> { match it { ast::Trait(_) => ItemListKind::Trait, ast::Impl(it) => if it.trait_().is_some() { - ItemListKind::TraitImpl + ItemListKind::TraitImpl(find_node_in_file_compensated(sema, original_file, &it)) } else { ItemListKind::Impl }, @@ -930,7 +968,11 @@ impl<'a> CompletionContext<'a> { } } -fn pattern_context_for(original_file: &SyntaxNode, pat: ast::Pat) -> PatternContext { +fn pattern_context_for( + sema: &Semantics<RootDatabase>, + original_file: &SyntaxNode, + pat: ast::Pat, +) -> PatternContext { let mut is_param = None; let (refutability, has_type_ascription) = pat @@ -946,7 +988,7 @@ fn pattern_context_for(original_file: &SyntaxNode, pat: ast::Pat) -> PatternCont let has_type_ascription = param.ty().is_some(); is_param = (|| { 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 kind = match_ast! { match param_list_owner { @@ -971,6 +1013,7 @@ fn pattern_context_for(original_file: &SyntaxNode, pat: ast::Pat) -> PatternCont ast::Pat::IdentPat(it) => (it.ref_token(), it.mut_token()), _ => (None, None), }; + PatternContext { refutability, param_ctx: is_param, @@ -979,6 +1022,27 @@ fn pattern_context_for(original_file: &SyntaxNode, pat: ast::Pat) -> PatternCont mut_token, ref_token, record_pat: None, + impl_: fetch_immediate_impl(sema, original_file, pat.syntax()), + } +} + +fn fetch_immediate_impl( + sema: &Semantics<RootDatabase>, + original_file: &SyntaxNode, + node: &SyntaxNode, +) -> Option<ast::Impl> { + let mut ancestors = ancestors_in_file_compensated(sema, original_file, node)? + .filter_map(ast::Item::cast) + .filter(|it| !matches!(it, ast::Item::MacroCall(_))); + + match ancestors.next()? { + ast::Item::Const(_) | ast::Item::Fn(_) | ast::Item::TypeAlias(_) => (), + ast::Item::Impl(it) => return Some(it), + _ => return None, + } + match ancestors.next()? { + ast::Item::Impl(it) => Some(it), + _ => None, } } @@ -1000,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 /// for the offset introduced by the fake ident. /// 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> { - let syntax_range = syntax.text_range(); - let range = node.syntax().text_range(); +fn find_node_in_file_compensated<N: AstNode>( + sema: &Semantics<RootDatabase>, + 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()?)?; if end < range.start() { return None; @@ -1010,17 +1086,22 @@ fn find_node_in_file_compensated<N: AstNode>(syntax: &SyntaxNode, node: &N) -> O let range = TextRange::new(range.start(), end); // our inserted ident could cause `range` to go outside of the original syntax, so cap it 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 /// for the offset introduced by the fake ident.. /// 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>( + sema: &Semantics<RootDatabase>, syntax: &SyntaxNode, node: 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)> { |