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.rs110
1 files changed, 92 insertions, 18 deletions
diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs
index 2eabf99fc6..b33a547dee 100644
--- a/crates/ide-completion/src/context/analysis.rs
+++ b/crates/ide-completion/src/context/analysis.rs
@@ -559,6 +559,7 @@ fn expected_type_and_name<'db>(
token: &SyntaxToken,
name_like: &ast::NameLike,
) -> (Option<Type<'db>>, Option<NameOrNameRef>) {
+ let token = prev_special_biased_token_at_trivia(token.clone());
let mut node = match token.parent() {
Some(it) => it,
None => return (None, None),
@@ -629,6 +630,17 @@ fn expected_type_and_name<'db>(
.map(TypeInfo::original);
(ty, None)
},
+ ast::BinExpr(it) => {
+ if let Some(ast::BinaryOp::Assignment { op: None }) = it.op_kind() {
+ let ty = it.lhs()
+ .and_then(|lhs| sema.type_of_expr(&lhs))
+ .or_else(|| it.rhs().and_then(|rhs| sema.type_of_expr(&rhs)))
+ .map(TypeInfo::original);
+ (ty, None)
+ } else {
+ (None, None)
+ }
+ },
ast::ArgList(_) => {
cov_mark::hit!(expected_type_fn_param);
ActiveParameter::at_token(
@@ -712,6 +724,18 @@ fn expected_type_and_name<'db>(
let def = sema.to_def(&it);
(def.map(|def| def.ret_type(sema.db)), None)
},
+ ast::ReturnExpr(it) => {
+ let fn_ = sema.ancestors_with_macros(it.syntax().clone())
+ .find_map(Either::<ast::Fn, ast::ClosureExpr>::cast);
+ let ty = fn_.and_then(|f| match f {
+ Either::Left(f) => Some(sema.to_def(&f)?.ret_type(sema.db)),
+ Either::Right(f) => {
+ let ty = sema.type_of_expr(&f.into())?.original.as_callable(sema.db)?;
+ Some(ty.return_type())
+ },
+ });
+ (ty, None)
+ },
ast::ClosureExpr(it) => {
let ty = sema.type_of_expr(&it.into());
ty.and_then(|ty| ty.original.as_callable(sema.db))
@@ -923,20 +947,39 @@ fn classify_name_ref<'db>(
None
}
};
- let after_if_expr = |node: SyntaxNode| {
- let prev_expr = (|| {
- let node = match node.parent().and_then(ast::ExprStmt::cast) {
- Some(stmt) => stmt.syntax().clone(),
- None => node,
- };
- let prev_sibling = non_trivia_sibling(node.into(), Direction::Prev)?.into_node()?;
+ let prev_expr = |node: SyntaxNode| {
+ let node = match node.parent().and_then(ast::ExprStmt::cast) {
+ Some(stmt) => stmt.syntax().clone(),
+ None => node,
+ };
+ let prev_sibling = non_trivia_sibling(node.into(), Direction::Prev)?.into_node()?;
- ast::ExprStmt::cast(prev_sibling.clone())
- .and_then(|it| it.expr())
- .or_else(|| ast::Expr::cast(prev_sibling))
- })();
+ match_ast! {
+ match prev_sibling {
+ ast::ExprStmt(stmt) => stmt.expr().filter(|_| stmt.semicolon_token().is_none()),
+ ast::LetStmt(stmt) => stmt.initializer().filter(|_| stmt.semicolon_token().is_none()),
+ ast::Expr(expr) => Some(expr),
+ _ => None,
+ }
+ }
+ };
+ let after_if_expr = |node: SyntaxNode| {
+ let prev_expr = prev_expr(node);
matches!(prev_expr, Some(ast::Expr::IfExpr(_)))
};
+ let after_incomplete_let = |node: SyntaxNode| {
+ prev_expr(node).and_then(|it| it.syntax().parent()).and_then(ast::LetStmt::cast)
+ };
+ let before_else_kw = |node: &SyntaxNode| {
+ node.parent()
+ .and_then(ast::ExprStmt::cast)
+ .filter(|stmt| stmt.semicolon_token().is_none())
+ .and_then(|stmt| non_trivia_sibling(stmt.syntax().clone().into(), Direction::Next))
+ .and_then(NodeOrToken::into_node)
+ .filter(|next| next.kind() == SyntaxKind::ERROR)
+ .and_then(|next| next.first_token())
+ .is_some_and(|token| token.kind() == SyntaxKind::ELSE_KW)
+ };
// We do not want to generate path completions when we are sandwiched between an item decl signature and its body.
// ex. trait Foo $0 {}
@@ -1025,9 +1068,6 @@ fn classify_name_ref<'db>(
sema.source(trait_)?.value.generic_param_list()
}
}
- hir::ModuleDef::TraitAlias(trait_) => {
- sema.source(trait_)?.value.generic_param_list()
- }
hir::ModuleDef::TypeAlias(ty_) => {
sema.source(ty_)?.value.generic_param_list()
}
@@ -1162,19 +1202,23 @@ fn classify_name_ref<'db>(
Some(res)
};
- let is_in_condition = |it: &ast::Expr| {
+ fn is_in_condition(it: &ast::Expr) -> bool {
(|| {
let parent = it.syntax().parent()?;
if let Some(expr) = ast::WhileExpr::cast(parent.clone()) {
Some(expr.condition()? == *it)
- } else if let Some(expr) = ast::IfExpr::cast(parent) {
+ } else if let Some(expr) = ast::IfExpr::cast(parent.clone()) {
Some(expr.condition()? == *it)
+ } else if let Some(expr) = ast::BinExpr::cast(parent)
+ && expr.op_token()?.kind() == T![&&]
+ {
+ Some(is_in_condition(&expr.into()))
} else {
None
}
})()
.unwrap_or(false)
- };
+ }
let make_path_kind_expr = |expr: ast::Expr| {
let it = expr.syntax();
@@ -1235,10 +1279,15 @@ fn classify_name_ref<'db>(
};
let is_func_update = func_update_record(it);
let in_condition = is_in_condition(&expr);
+ let after_incomplete_let = after_incomplete_let(it.clone()).is_some();
+ let incomplete_expr_stmt =
+ it.parent().and_then(ast::ExprStmt::cast).map(|it| it.semicolon_token().is_none());
let incomplete_let = it
.parent()
.and_then(ast::LetStmt::cast)
- .is_some_and(|it| it.semicolon_token().is_none());
+ .is_some_and(|it| it.semicolon_token().is_none())
+ || after_incomplete_let && incomplete_expr_stmt.unwrap_or(true) && !before_else_kw(it);
+ let in_value = it.parent().and_then(Either::<ast::LetStmt, ast::ArgList>::cast).is_some();
let impl_ = fetch_immediate_impl(sema, original_file, expr.syntax());
let in_match_guard = match it.parent().and_then(ast::MatchArm::cast) {
@@ -1259,7 +1308,9 @@ fn classify_name_ref<'db>(
is_func_update,
innermost_ret_ty,
self_param,
+ in_value,
incomplete_let,
+ after_incomplete_let,
impl_,
in_match_guard,
},
@@ -1856,3 +1907,26 @@ fn next_non_trivia_sibling(ele: SyntaxElement) -> Option<SyntaxElement> {
}
None
}
+
+fn prev_special_biased_token_at_trivia(mut token: SyntaxToken) -> SyntaxToken {
+ while token.kind().is_trivia()
+ && let Some(prev) = token.prev_token()
+ && let T![=]
+ | T![+=]
+ | T![/=]
+ | T![*=]
+ | T![%=]
+ | T![>>=]
+ | T![<<=]
+ | T![-=]
+ | T![|=]
+ | T![&=]
+ | T![^=]
+ | T![return]
+ | T![break]
+ | T![continue] = prev.kind()
+ {
+ token = prev
+ }
+ token
+}